source: ogAgent-Git/src/OGAgentUser.py @ 1528428

decorare-oglive-methodsfix-urlfixes-winlgromero-filebeatmainmodulesnew-browserno-ptt-paramogadmcliogadmclient-statusogagent-jobsogagent-macosogcore1oglogoglog2override-moduleping1ping2ping3ping4py3-winpython3report-progresssched-tasktlsunification2unification3versionswindows-fixes
Last change on this file since 1528428 was 1528428, checked in by Ramón M. Gómez <ramongomez@…>, 5 years ago

#940: OGAgent "about" box supporting Qt5

  • Property mode set to 100644
File size: 11.6 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright (c) 2014 Virtual Cable S.L.
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without modification,
8# are permitted provided that the following conditions are met:
9#
10#    * Redistributions of source code must retain the above copyright notice,
11#      this list of conditions and the following disclaimer.
12#    * Redistributions in binary form must reproduce the above copyright notice,
13#      this list of conditions and the following disclaimer in the documentation
14#      and/or other materials provided with the distribution.
15#    * Neither the name of Virtual Cable S.L. nor the names of its contributors
16#      may be used to endorse or promote products derived from this software
17#      without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29"""
30@author: Adolfo Gómez, dkmaster at dkmon dot com
31"""
32from __future__ import unicode_literals
33
34import json
35import sys
36import time
37from PyQt5 import QtCore, QtGui, QtWidgets
38import six
39import atexit
40
41from opengnsys import VERSION, ipc, operations, utils
42from opengnsys.log import logger
43from opengnsys.service import IPC_PORT
44from about_dialog_ui import Ui_OGAAboutDialog
45from message_dialog_ui import Ui_OGAMessageDialog
46from opengnsys.scriptThread import ScriptExecutorThread
47from opengnsys.config import readConfig
48from opengnsys.loader import loadModules
49
50# Set default characters encoding to UTF-8
51reload(sys)
52if hasattr(sys, 'setdefaultencoding'):
53    sys.setdefaultencoding('utf-8')
54
55trayIcon = None
56
57
58def sigAtExit():
59    if trayIcon:
60        trayIcon.quit()
61
62
63# About dialog
64class OGAAboutDialog(QtGui.QDialog):
65    def __init__(self, parent=None):
66        QtWidgets.QDialog.__init__(self, parent)
67        self.ui = Ui_OGAAboutDialog()
68        self.ui.setupUi(self)
69        self.ui.VersionLabel.setText("Version " + VERSION)
70
71    def closeDialog(self):
72        self.hide()
73
74
75class OGAMessageDialog(QtWidgets.QDialog):
76    def __init__(self, parent=None):
77        QtWidgets.QDialog.__init__(self, parent)
78        self.ui = Ui_OGAMessageDialog()
79        self.ui.setupUi(self)
80
81    def message(self, message):
82        self.ui.message.setText(message)
83        self.show()
84
85    def closeDialog(self):
86        self.hide()
87
88
89class MessagesProcessor(QtCore.QThread):
90    logoff = QtCore.pyqtSignal(name='logoff')
91    message = QtCore.pyqtSignal(tuple, name='message')
92    script = QtCore.pyqtSignal(str, name='script')
93    exit = QtCore.pyqtSignal(name='exit')
94
95    def __init__(self, port):
96        super(self.__class__, self).__init__()
97        # Retries connection for a while
98        for _ in range(10):
99            try:
100                self.ipc = ipc.ClientIPC(port)
101                self.ipc.start()
102                break
103            except Exception:
104                logger.debug('IPC Server is not reachable')
105                self.ipc = None
106                time.sleep(2)
107
108        self.running = False
109
110    def stop(self):
111        self.running = False
112        if self.ipc:
113            self.ipc.stop()
114
115    def isAlive(self):
116        return self.ipc is not None
117
118    def sendLogin(self, userName, language):
119        if self.ipc:
120            self.ipc.sendLogin(userName, language)
121
122    def sendLogout(self, userName):
123        if self.ipc:
124            self.ipc.sendLogout(userName)
125
126    def run(self):
127        if self.ipc is None:
128            return
129        self.running = True
130
131        # Wait a bit so we ensure IPC thread is running...
132        time.sleep(2)
133
134        while self.running and self.ipc.running:
135            try:
136                msg = self.ipc.getMessage()
137                if msg is None:
138                    break
139                msgId, data = msg
140                logger.debug('Got Message on User Space: {}:{}'.format(msgId, data))
141                if msgId == ipc.MSG_MESSAGE:
142                    module, message, data = data.split('\0')
143                    self.message.emit((module, message, data))
144                elif msgId == ipc.MSG_LOGOFF:
145                    self.logoff.emit()
146                elif msgId == ipc.MSG_SCRIPT:
147                    self.script.emit(QtCore.QString.fromUtf8(data))
148            except Exception as e:
149                try:
150                    logger.error('Got error on IPC thread {}'.format(utils.exceptionToMessage(e)))
151                except:
152                    logger.error('Got error on IPC thread (an unicode error??)')
153
154        if self.ipc.running is False and self.running is True:
155            logger.warn('Lost connection with Service, closing program')
156
157        self.exit.emit()
158
159
160class OGASystemTray(QtWidgets.QSystemTrayIcon):
161    def __init__(self, app_, parent=None):
162        self.app = app_
163        self.config = readConfig(client=True)
164
165        # Get opengnsys section as dict
166        cfg = dict(self.config.items('opengnsys'))
167
168        # Set up log level
169        logger.setLevel(cfg.get('log', 'INFO'))
170
171        self.ipcport = int(cfg.get('ipc_port', IPC_PORT))
172
173        # style = app.style()
174        # icon = QtGui.QIcon(style.standardPixmap(QtGui.QStyle.SP_ComputerIcon))
175        icon = QtGui.QIcon(':/images/img/oga.png')
176
177        QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
178        self.menu = QtWidgets.QMenu(parent)
179        exit_action = self.menu.addAction("About")
180        exit_action.triggered.connect(self.about)
181        self.setContextMenu(self.menu)
182        self.ipc = MessagesProcessor(self.ipcport)
183
184        if self.ipc.isAlive() is False:
185            raise Exception('No connection to service, exiting.')
186
187        self.timer = QtCore.QTimer()
188        self.timer.timeout.connect(self.timerFnc)
189
190        self.stopped = False
191
192        self.ipc.message.connect(self.message)
193        self.ipc.exit.connect(self.quit)
194        self.ipc.script.connect(self.executeScript)
195        self.ipc.logoff.connect(self.logoff)
196
197        self.aboutDlg = OGAAboutDialog()
198        self.msgDlg = OGAMessageDialog()
199
200        self.timer.start(1000)  # Launch idle checking every 1 seconds
201
202        self.ipc.start()
203
204    def initialize(self):
205        # Load modules and activate them
206        # Also, sends "login" event to service
207        self.modules = loadModules(self, client=True)
208        logger.debug('Modules: {}'.format(list(v.name for v in self.modules)))
209
210        # Send init to all modules
211        validMods = []
212        for mod in self.modules:
213            try:
214                logger.debug('Activating module {}'.format(mod.name))
215                mod.activate()
216                validMods.append(mod)
217            except Exception as e:
218                logger.exception()
219                logger.error("Activation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
220
221        self.modules[:] = validMods  # copy instead of assignment
222
223        # If this is running, it's because he have logged in, inform service of this fact
224        self.ipc.sendLogin(operations.getCurrentUser(), operations.getSessionLanguage())
225
226    def deinitialize(self):
227        for mod in reversed(self.modules):  # Deinitialize reversed of initialization
228            try:
229                logger.debug('Deactivating module {}'.format(mod.name))
230                mod.deactivate()
231            except Exception as e:
232                logger.exception()
233                logger.error("Deactivation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
234
235    def timerFnc(self):
236        pass
237
238    def message(self, msg):
239        """
240        Processes the message sent asynchronously, msg is an QString
241        """
242        try:
243            logger.debug('msg: {}, {}'.format(type(msg), msg))
244            module, message, data = msg
245        except Exception as e:
246            logger.error('Got exception {} processing message {}'.format(e, msg))
247            return
248
249        for v in self.modules:
250            if v.name == module:  # Case Sensitive!!!!
251                try:
252                    logger.debug('Notifying message {} to module {} with json data {}'.format(message, v.name, data))
253                    v.processMessage(message, json.loads(data))
254                    return
255                except Exception as e:
256                    logger.error('Got exception {} processing generic message on {}'.format(e, v.name))
257
258        logger.error('Module {} not found, messsage {} not sent'.format(module, message))
259
260    def executeScript(self, script):
261        logger.debug('Executing script')
262        script = six.text_type(script.toUtf8()).decode('base64')
263        th = ScriptExecutorThread(script)
264        th.start()
265
266    def logoff(self):
267        logger.debug('Logoff invoked')
268        operations.logoff()  # Invoke log off
269
270    def about(self):
271        self.aboutDlg.exec_()
272
273    def cleanup(self):
274        logger.debug('Quit invoked')
275        if self.stopped is False:
276            self.stopped = True
277            try:
278                self.deinitialize()
279            except Exception:
280                logger.exception()
281                logger.error('Got exception deinitializing modules')
282
283            try:
284                # If we close Client, send Logoff to Broker
285                self.ipc.sendLogout(operations.getCurrentUser())
286                time.sleep(1)
287                self.timer.stop()
288                self.ipc.stop()
289            except Exception:
290                # May we have lost connection with server, simply log and exit in that case
291                logger.exception()
292                logger.exception("Got an exception, processing quit")
293
294            try:
295                # operations.logoff()  # Uncomment this after testing to logoff user
296                pass
297            except Exception:
298                pass
299
300    def quit(self):
301        # logger.debug("Exec quit {}".format(self.stopped))
302        if self.stopped is False:
303            self.cleanup()
304            self.app.quit()
305
306    def closeEvent(self, event):
307        logger.debug("Exec closeEvent")
308        event.accept()
309        self.quit()
310
311
312if __name__ == '__main__':
313    app = QtWidgets.QApplication(sys.argv)
314
315    if not QtWidgets.QSystemTrayIcon.isSystemTrayAvailable():
316        # QtGui.QMessageBox.critical(None, "Systray", "I couldn't detect any system tray on this system.")
317        sys.exit(1)
318
319    # This is important so our app won't close on messages windows (alerts, etc...)
320    QtWidgets.QApplication.setQuitOnLastWindowClosed(False)
321
322    try:
323        trayIcon = OGASystemTray(app)
324    except Exception as e:
325        logger.exception()
326        logger.error('OGA Service is not running, or it can\'t contact with OGA Server. User Tools stopped: {}'.format(
327            utils.exceptionToMessage(e)))
328        sys.exit(1)
329
330    try:
331        trayIcon.initialize()  # Initialize modules, etc..
332    except Exception as e:
333        logger.exception()
334        logger.error('Exception initializing OpenGnsys User Agent {}'.format(utils.exceptionToMessage(e)))
335        trayIcon.quit()
336        sys.exit(1)
337
338    app.aboutToQuit.connect(trayIcon.cleanup)
339    trayIcon.show()
340
341    # Catch kill and logout user :)
342    atexit.register(sigAtExit)
343
344    res = app.exec_()
345
346    logger.debug('Exiting')
347    trayIcon.quit()
348
349    sys.exit(res)
Note: See TracBrowser for help on using the repository browser.