# -*- coding: utf-8 -*- # Copyright 2012-2016 Mir Calculate. http://www.calculate-linux.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import absolute_import from .. import qt import time import urllib.request as urllib2 # from calculate.core.client.function import create_obj from .utils import create_obj try: from calculate.update.update_info import UpdateInfo except ImportError: UpdateInfo = None import dbus from .ReturnMessage import ReturnedMessage from .pid_information import client_pid_info, client_list_pid from .more import show_msg, get_sid, _print, get_icon # class MySignal(qt.QObject): # sig = qt.Signal(str, str) class TrayUpdateSystem(qt.QThread): mes_sig = qt.Signal(str, str) started_sig = qt.Signal(str) def __init__(self, target, parent=None): super().__init__(parent) self._parent = target def run(self): self.close_flag = False if not hasattr(self._parent, 'localhost_ClientObj'): self.mes_sig.emit(_('Connection Error'), \ _( 'You are not connected to the localhost server')) return 1 if not self._parent.localhost_ClientObj.client: self.mes_sig.emit(_('Connection Error'), \ _( 'You are not connected to the localhost server')) return 1 sid = get_sid(self._parent.localhost_ClientObj.client) try: param_object = create_obj(self._parent.localhost_ClientObj.client, "update") meth_result_temp = \ self._parent.localhost_ClientObj.client.service[0]['update'](sid, param_object) # self._parent.localhost_ClientObj.client.service.update(sid, param_object) print(meth_result_temp) except Exception as e: msg = e if type(e) == tuple: msg = ' '.join((str(x) for x in list(e))) self.mes_sig.emit(msg, 'cl-client-console Exception') return 1 meth_result = [] method_name = 'System update tray' error = False for res in meth_result_temp[0]: meth_result.append(ReturnedMessage(res.type, res.field, \ res.message, res.expert)) if res.type == 'error': error = True if error: return 1 if meth_result[0].type == "pid": # create process dict self._parent.localhost_ClientObj.process_dict \ [meth_result[0].message] = {} self._parent.localhost_ClientObj.process_dict \ [meth_result[0].message]['result'] = meth_result[0] self._parent.localhost_ClientObj.process_dict \ [meth_result[0].message]['name'] = method_name self._parent.localhost_ClientObj.process_dict \ [meth_result[0].message]['layout'] = qt.QVBoxLayout() self._parent.localhost_ClientObj.process_dict \ [meth_result[0].message]['layout'].setAlignment \ (qt.Qt.AlignTop) else: return 1 # self.mes_sig.emit('', _('Update running')) mess = meth_result[0] pid = mess.message self._parent.sys_update_pid = int(pid) self.started_sig.emit(pid) # self.get_update_status(sid, pid) def get_update_status(self, sid, pid): end_frame = True try: current_frame = self._parent.localhost_ClientObj.client. \ service.get_entire_frame(sid, pid) except urllib2.URLError: _print('get_entire_frame in TrayIcon Exception') current_frame = None while end_frame: while current_frame in [None, [], ""]: for i in range(5): time.sleep(0.1) if self.close_flag: # self.mes_sig.emit('', _('Update aborted')) return 0 try: client = self._parent.localhost_ClientObj.client current_frame = client.service.get_frame(sid, pid, "gui") except urllib2.URLError: _print('client.service.get_entire_frame in' ' TrayIcon Exception') current_frame = None for item in range(len(current_frame[0])): if current_frame[0][item].type == 'error': # self.mes_sig.emit(_("Failed to update!"), \ # current_frame[0][item].message) end_frame = False return 1 elif current_frame[0][item].type == 'endFrame': if current_frame[0][item].message: message = current_frame[0][item].message else: message = '' # self.mes_sig.emit(_('Update successfully completed'), \ # message) end_frame = False current_frame = None def close(self): self.close_flag = True class TrayIcon(qt.QSystemTrayIcon): default_theme_name = 'Tango' def __init__(self, parent): super().__init__(parent) self._parent = parent self.actions = [] icon = qt.QIcon() icon.addPixmap(qt.QPixmap('/usr/share/pixmaps/calculate-console-offline.svg')) self.setIcon(icon) self.is_online_icon = False self.setVisible(True) self.generation_actions() self.right_menu = self.create_menu('help', 'bug_report', None, 'tools', 'update_system', None, 'exit') self.activated.connect(self.activ) self.setToolTip(_('Calculate Console')) self.setContextMenu(self.right_menu) self.update_thread = TrayUpdateSystem(parent) self.update_thread.mes_sig.connect(self.showMessage) self.update_thread.started_sig.connect(self.showUpdate) def generation_actions(self): _("About") _("Report a Bug") _("Program settings") _("Update System") _("Exit Program") self.actions = { 'help': { 'icons': ['help-about', 'help-browser'], 'label': "About", 'trigger': self.help}, 'bug_report': { 'icons': ['tools-report-bug', 'system-help', 'help-browser'], 'label': "Report a Bug", 'trigger': self.bug_report}, 'tools': { 'icons': ['preferences-other', 'preferences-system'], 'label': "Program settings", 'trigger': self.tools}, 'update_system': { 'icons': ['system-software-update'], 'label': "Update System", 'trigger': self.start_update_system}, 'exit': { 'icons': ["application-exit", "system-log-out"], 'label': "Exit Program", 'trigger': self.exit}, 'show_hide': { 'icons': ['preferences-system-windows'], 'label': "Show/Hide Window", 'trigger': self.windowVisible} } for action_name, action_data in self.actions.items(): setattr(self, '%s_action' % action_name, qt.QAction(get_icon(*action_data['icons']), _(action_data['label']), self, triggered=action_data['trigger'])) def create_menu(self, *actions): menu = qt.QMenu() for action in actions: if action: menu.addAction(getattr(self, "%s_action" % action)) else: menu.addSeparator() return menu def check_for_update(self): if hasattr(self._parent, "localhost_ClientObj"): if self._parent.localhost_ClientObj: if UpdateInfo: uinfo = UpdateInfo(self._parent.localhost_ClientObj.VarsApi) return (uinfo.need_update() and not uinfo.update_already_run() and not self.get_update_pid() and not self.update_thread.isRunning()) return False def set_icon(self, online, has_update=None): if online: if not self.is_online_icon: icon = qt.QIcon() icon.addPixmap(qt.QPixmap('/usr/share/pixmaps/calculate-console-online.svg')) self.setIcon(icon) self.setVisible(True) self.is_online_icon = True else: if self.is_online_icon: icon = qt.QIcon() icon.addPixmap(qt.QPixmap('/usr/share/pixmaps/calculate-console-offline.svg')) self.setIcon(icon) # для решения странного размера иконки в mate #self.hide() self.setVisible(True) self.is_online_icon = False def translate(self): for action_name, action_data in self.actions.items(): getattr(self, '%s_action' % action_name).setText( _(action_data['label'])) def help(self): self._parent.currentWidget().help() def bug_report(self): self._parent.currentWidget().bug_report() def tools(self): self._parent.currentWidget().tools() def get_update_pid(self): """ Получить pid процесса выполняющего update для этой сессии """ pid = str(self._parent.sys_update_pid) client_obj = self._parent.localhost_ClientObj pids = [str(x) for x in client_list_pid(client_obj.client)] client_pid_info(client_obj, client_obj.client, 0) if self._parent.localhost_ClientObj: if pid and pid in client_obj.process_dict and pid in pids: return pid else: for pid in pids: d = client_obj.process_dict[pid] if (d['method_name'] == 'update' and d.get('status', '0') == '1'): return pid def show_update(self): pid = self.get_update_pid() if pid: client_obj = self._parent.localhost_ClientObj client_pid_info(client_obj, client_obj.client, 0) client_obj._parent.setWindowTitle \ ("Calculate console" + ' - ' + _('System Update')) result = client_obj.process_dict[pid]['result'] client_obj.MainWidget.main_view_process('update', result, pid) client_obj.app.processEvents() vsb = client_obj.MainWidget.main_frame.verticalScrollBar() vsb.setValue(vsb.maximum()) self._parent.hide() self._parent.show() self._parent.showNormal() else: self._parent.show() self._parent.move(self._parent.cur_pos) self._parent.show() return True def exit(self): # if self.update_thread.isRunning(): # text = _('The system is updated') # informative_text = _('Stop updating and exit?') # reply = show_question(self._parent, text, informative_text) # if reply == qt.QMessageBox.No: # return 0 # elif reply == qt.QMessageBox.Yes: # self.update_thread.close() # self.update_thread.wait() self._parent._closeEvent() def activ(self, reason): if reason == qt.QSystemTrayIcon.Trigger: self.windowVisible() if reason == qt.QSystemTrayIcon.DoubleClick: self.windowVisible() def showMessage(self, title, message, icon='dialog-warning'): # This uses the session bus because it's session-specific. bus = dbus.SessionBus() proxy = bus.get_object('org.freedesktop.Notifications', \ '/org/freedesktop/Notifications') interface = dbus.Interface(proxy, dbus_interface= \ 'org.freedesktop.Notifications') interface.Notify('cl-console-gui', 1, icon, title, message, [], {}, -1) # KDE KNotify # bus = dbus.SessionBus() # knotify = bus.get_object('org.kde.knotify', '/Notify') # knotify.event("warning", "kde", [], 'title', 'text', [], [], 0, 0, # dbus_interface="org.kde.KNotify") def windowVisible(self): if self._parent.isVisible(): self._parent.cur_pos = self._parent.pos() self._parent.hide() else: self._parent.show() self._parent.move(self._parent.cur_pos) def start_update_system(self): self.set_icon(True, False) if self.get_update_pid(): self.show_update() else: self.update_thread.start() # if not self.update_thread.isRunning(): def showUpdate(self, message): self.show_update()