#-*- coding: utf-8 -*- # Copyright 2012-2013 Calculate Ltd. 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 PySide import QtGui, QtCore import time, urllib2 from calculate.core.client.function import create_obj import dbus from ReturnMessage import ReturnedMessage from calculate.consolegui.application.pid_information import client_pid_info, \ client_list_pid from more import show_msg, get_sid, _print #class MySignal(QtCore.QObject): # sig = QtCore.Signal(str, str) class TrayUpdateSystem(QtCore.QThread): mes_sig = QtCore.Signal(str, str) started_sig = QtCore.Signal(str) def __init__(self, target, parent = None): QtCore.QThread.__init__(self, 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.update( sid, param_object) except Exception, e: msg = e.message if type (e.message) == tuple: msg = ' '.join( map(lambda x:str(x), list(e.message))) 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'] = QtGui.QVBoxLayout() self._parent.localhost_ClientObj.process_dict \ [meth_result[0].message]['layout'].setAlignment \ (QtCore.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 (QtGui.QSystemTrayIcon): default_theme_name = 'Tango' def __init__(self, parent): super(TrayIcon, self).__init__(parent) self._parent = parent self.actions = [] self.setIcon(QtGui.QIcon( '/usr/share/pixmaps/calculate-console-offline.png')) self.generation_actions() self.right_menu = self.create_menu('help', 'bug_report', None, 'tools', 'update_system', None, 'exit') self.left_menu = self.create_menu('show_hide', 'update_system', None, 'exit') self.activated.connect(self.activ) self.setToolTip('cl-console-gui - '+ _('System control')) 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 select_icon(self, icons): old_theme_name = QtGui.QIcon.themeName() try: for theme_name in (old_theme_name, self.default_theme_name): QtGui.QIcon.setThemeName(theme_name) for image in icons: if QtGui.QIcon.hasThemeIcon(image): return QtGui.QIcon.fromTheme(image) return QtGui.QIcon() finally: QtGui.QIcon.setThemeName(old_theme_name) def generation_actions(self): 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, QtGui.QAction(self.select_icon(action_data['icons']), _(action_data['label']), self, triggered=action_data['trigger'])) def create_menu(self, *actions): menu = QtGui.QMenu() for action in actions: if action: menu.addAction(getattr(self, "%s_action" % action)) else: menu.addSeparator() return menu def set_icon(self, online): if online: self.setIcon(QtGui.QIcon \ ('/usr/share/pixmaps/calculate-console-online.png')) else: self.setIcon(QtGui.QIcon \ ('/usr/share/pixmaps/calculate-console-offline.png')) 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 = map(str, 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() 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 == QtGui.QMessageBox.No: # return 0 # elif reply == QtGui.QMessageBox.Yes: # self.update_thread.close() # self.update_thread.wait() self._parent._closeEvent() def activ(self, reason): if reason == QtGui.QSystemTrayIcon.MiddleClick: self.windowVisible() elif reason == QtGui.QSystemTrayIcon.Trigger: if QtGui.QCursor.pos().y() < self.left_menu.sizeHint().height(): self.left_menu.move(QtCore.QPoint \ (self.geometry().x(), self.geometry().y()) + \ QtCore.QPoint(0, self.geometry().height() + 8)) else: point = QtCore.QPoint(self.left_menu.sizeHint().width(), \ self.left_menu.sizeHint().height() + 16) self.left_menu.move(QtGui.QCursor.pos() - point) self.left_menu.show() 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): 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()