From d75ef7982aaf96572ea26b1986b3578d9b1eca06 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 4 Sep 2012 04:45:48 +0900 Subject: first attempt at class splitting war on spaguetti! :D --- src/leap/baseapp/mainwindow.py | 506 ++++++++++++++++++++++------------------- 1 file changed, 270 insertions(+), 236 deletions(-) diff --git a/src/leap/baseapp/mainwindow.py b/src/leap/baseapp/mainwindow.py index 2f7a14dd..ca9b79b3 100644 --- a/src/leap/baseapp/mainwindow.py +++ b/src/leap/baseapp/mainwindow.py @@ -2,7 +2,9 @@ #!/usr/bin/env python import logging import time +logging.basicConfig() logger = logging.getLogger(name=__name__) +logger.setLevel(logging.DEBUG) from PyQt4.QtGui import (QMainWindow, QWidget, QVBoxLayout, QMessageBox, QSystemTrayIcon, QGroupBox, QLabel, QPixmap, @@ -14,63 +16,22 @@ from PyQt4.QtCore import (pyqtSlot, pyqtSignal, QTimer) from leap.baseapp.dialogs import ErrorDialog from leap.eip import exceptions as eip_exceptions from leap.eip.eipconnection import EIPConnection -from leap.gui import mainwindow_rc - - -class LeapWindow(QMainWindow): - #XXX tbd: refactor into model / view / controller - #and put in its own modules... - - newLogLine = pyqtSignal([str]) - statusChange = pyqtSignal([object]) - - def __init__(self, opts): - super(LeapWindow, self).__init__() - self.debugmode = getattr(opts, 'debug', False) - - self.eip_service_started = False - - self.createWindowHeader() - self.createIconGroupBox() - self.createActions() - self.createTrayIcon() - if self.debugmode: - self.createLogBrowser() - - # create timer - self.timer = QTimer() - - # bind signals - self.trayIcon.activated.connect(self.iconActivated) - self.newLogLine.connect(self.onLoggerNewLine) - self.statusChange.connect(self.onStatusChange) - self.timer.timeout.connect(self.onTimerTick) - - widget = QWidget() - self.setCentralWidget(widget) +from leap.gui import mainwindow_rc - # add widgets to layout - mainLayout = QVBoxLayout() - mainLayout.addWidget(self.headerBox) - mainLayout.addWidget(self.statusIconBox) - if self.debugmode: - mainLayout.addWidget(self.statusBox) - mainLayout.addWidget(self.loggerBox) - widget.setLayout(mainLayout) - self.trayIcon.show() - self.setWindowTitle("LEAP Client") - self.resize(400, 300) - self.set_statusbarMessage('ready') +class EIPConductorApp(object): + def __init__(self, *args, **kwargs): # # conductor is in charge of all # vpn-related configuration / monitoring. # we pass a tuple of signals that will be # triggered when status changes. # + opts = kwargs.pop('opts') config_file = getattr(opts, 'config_file', None) + self.conductor = EIPConnection( watcher_cb=self.newLogLine.emit, config_file=config_file, @@ -79,7 +40,11 @@ class LeapWindow(QMainWindow): # XXX remove skip download when sample service is ready self.conductor.run_checks(skip_download=True) + self.error_check() + if self.conductor.autostart: + self.start_or_stopVPN() + def error_check(self): ####### error checking ################ # # bunch of self checks. @@ -142,78 +107,89 @@ class LeapWindow(QMainWindow): 'error') ############ end error checking ################### - - if self.conductor.autostart: - self.start_or_stopVPN() - - def closeEvent(self, event): + @pyqtSlot() + def statusUpdate(self): """ - redefines close event (persistent window behaviour) + called on timer tick + polls status and updates ui with real time + info about transferred bytes / connection state. """ - if self.trayIcon.isVisible() and not self.debugmode: - QMessageBox.information(self, "Systray", - "The program will keep running " - "in the system tray. To " - "terminate the program, choose " - "Quit in the " - "context menu of the system tray entry.") - self.hide() - event.ignore() + # XXX it's too expensive to poll + # continously. move to signal events instead. + + if not self.eip_service_started: + return + + # XXX remove all access to manager layer + # from here. + if self.conductor.with_errors: + #XXX how to wait on pkexec??? + #something better that this workaround, plz!! + time.sleep(5) + print('errors. disconnect.') + self.start_or_stopVPN() # is stop + + state = self.conductor.poll_connection_state() + if not state: + return + + ts, con_status, ok, ip, remote = state + self.set_statusbarMessage(con_status) + self.setIconToolTip() + + ts = time.strftime("%a %b %d %X", ts) if self.debugmode: - self.cleanupAndQuit() + self.updateTS.setText(ts) + self.status_label.setText(con_status) + self.ip_label.setText(ip) + self.remote_label.setText(remote) - def setIcon(self, name): - icon = self.Icons.get(name) - self.trayIcon.setIcon(icon) - self.setWindowIcon(icon) + # status i/o - def setToolTip(self): - """ - get readable status and place it on systray tooltip - """ - status = self.conductor.status.get_readable_status() - self.trayIcon.setToolTip(status) + status = self.conductor.get_status_io() + if status and self.debugmode: + #XXX move this to systray menu indicators + ts, (tun_read, tun_write, tcp_read, tcp_write, auth_read) = status + ts = time.strftime("%a %b %d %X", ts) + self.updateTS.setText(ts) + self.tun_read_bytes.setText(tun_read) + self.tun_write_bytes.setText(tun_write) - def iconActivated(self, reason): + @pyqtSlot() + def start_or_stopVPN(self): """ - handles left click, left double click - showing the trayicon menu + stub for running child process with vpn """ - #XXX there's a bug here! - #menu shows on (0,0) corner first time, - #until double clicked at least once. - if reason in (QSystemTrayIcon.Trigger, - QSystemTrayIcon.DoubleClick): - self.trayIconMenu.show() + if self.eip_service_started is False: + try: + self.conductor.connect() + # XXX move this to error queue + except eip_exceptions.EIPNoCommandError: + dialog = ErrorDialog() + dialog.warningMessage( + 'No suitable openvpn command found. ' + '
(Might be a permissions problem)', + 'error') + if self.debugmode: + self.startStopButton.setText('&Disconnect') + self.eip_service_started = True - def createWindowHeader(self): - """ - description lines for main window - """ - #XXX good candidate to refactor out! :) - self.headerBox = QGroupBox() - self.headerLabel = QLabel("Encryption \ -Internet Proxy") - self.headerLabelSub = QLabel("trust your \ -technolust") + # XXX what is optimum polling interval? + # too little is overkill, too much + # will miss transition states.. - pixmap = QPixmap(':/images/leapfrog.jpg') - frog_lbl = QLabel() - frog_lbl.setPixmap(pixmap) + self.timer.start(250.0) + return + if self.eip_service_started is True: + self.conductor.disconnect() + if self.debugmode: + self.startStopButton.setText('&Connect') + self.eip_service_started = False + self.timer.stop() + return - headerLayout = QHBoxLayout() - headerLayout.addWidget(frog_lbl) - headerLayout.addWidget(self.headerLabel) - headerLayout.addWidget(self.headerLabelSub) - headerLayout.addStretch() - self.headerBox.setLayout(headerLayout) - def getIcon(self, icon_name): - # XXX get from connection dict - icons = {'disconnected': 0, - 'connecting': 1, - 'connected': 2} - return icons.get(icon_name, None) +class StatusAwareTrayIcon(object): def createIconGroupBox(self): """ @@ -254,6 +230,25 @@ technolust") statusIconLayout.itemAt(2).widget().hide() self.statusIconBox.setLayout(statusIconLayout) + def createTrayIcon(self): + """ + creates the tray icon + """ + self.trayIconMenu = QMenu(self) + + self.trayIconMenu.addAction(self.connectVPNAction) + self.trayIconMenu.addAction(self.dis_connectAction) + self.trayIconMenu.addSeparator() + self.trayIconMenu.addAction(self.minimizeAction) + self.trayIconMenu.addAction(self.maximizeAction) + self.trayIconMenu.addAction(self.restoreAction) + self.trayIconMenu.addSeparator() + self.trayIconMenu.addAction(self.quitAction) + + self.trayIcon = QSystemTrayIcon(self) + self.setIcon('disconnected') + self.trayIcon.setContextMenu(self.trayIconMenu) + def createActions(self): """ creates actions to be binded to tray icon @@ -261,8 +256,9 @@ technolust") self.connectVPNAction = QAction("Connect to &VPN", self, triggered=self.hide) # XXX change action name on (dis)connect - self.dis_connectAction = QAction("&(Dis)connect", self, - triggered=self.start_or_stopVPN) + self.dis_connectAction = QAction( + "&(Dis)connect", self, + triggered=lambda: self.start_or_stopVPN()) self.minimizeAction = QAction("Mi&nimize", self, triggered=self.hide) self.maximizeAction = QAction("Ma&ximize", self, @@ -272,24 +268,128 @@ technolust") self.quitAction = QAction("&Quit", self, triggered=self.cleanupAndQuit) - def createTrayIcon(self): + def setConnWidget(self, icon_name): + #print 'changing icon to %s' % icon_name + oldlayout = self.statusIconBox.layout() + + # XXX reuse with icons + # XXX move states to StateWidget + states = {"disconnected": 0, + "connecting": 1, + "connected": 2} + + for i in range(3): + oldlayout.itemAt(i).widget().hide() + new = states[icon_name] + oldlayout.itemAt(new).widget().show() + + def setIcon(self, name): + icon = self.Icons.get(name) + self.trayIcon.setIcon(icon) + self.setWindowIcon(icon) + + def getIcon(self, icon_name): + # XXX get from connection dict + icons = {'disconnected': 0, + 'connecting': 1, + 'connected': 2} + return icons.get(icon_name, None) + + def setIconToolTip(self): """ - creates the tray icon + get readable status and place it on systray tooltip """ - self.trayIconMenu = QMenu(self) + status = self.conductor.status.get_readable_status() + self.trayIcon.setToolTip(status) - self.trayIconMenu.addAction(self.connectVPNAction) - self.trayIconMenu.addAction(self.dis_connectAction) - self.trayIconMenu.addSeparator() - self.trayIconMenu.addAction(self.minimizeAction) - self.trayIconMenu.addAction(self.maximizeAction) - self.trayIconMenu.addAction(self.restoreAction) - self.trayIconMenu.addSeparator() - self.trayIconMenu.addAction(self.quitAction) + def iconActivated(self, reason): + """ + handles left click, left double click + showing the trayicon menu + """ + #XXX there's a bug here! + #menu shows on (0,0) corner first time, + #until double clicked at least once. + if reason in (QSystemTrayIcon.Trigger, + QSystemTrayIcon.DoubleClick): + self.trayIconMenu.show() - self.trayIcon = QSystemTrayIcon(self) - self.setIcon('disconnected') - self.trayIcon.setContextMenu(self.trayIconMenu) + @pyqtSlot() + def onTimerTick(self): + self.statusUpdate() + + @pyqtSlot(object) + def onStatusChange(self, status): + """ + slot for status changes. triggers new signals for + updating icon, status bar, etc. + """ + + #print('STATUS CHANGED! (on Qt-land)') + #print('%s -> %s' % (status.previous, status.current)) + icon_name = self.conductor.get_icon_name() + self.setIcon(icon_name) + #print 'icon = ', icon_name + + # change connection pixmap widget + self.setConnWidget(icon_name) + + +class LeapMainWindow(object): + + def createWindowHeader(self): + """ + description lines for main window + """ + #XXX good candidate to refactor out! :) + self.headerBox = QGroupBox() + self.headerLabel = QLabel("Encryption \ +Internet Proxy") + self.headerLabelSub = QLabel("trust your \ +technolust") + + pixmap = QPixmap(':/images/leapfrog.jpg') + frog_lbl = QLabel() + frog_lbl.setPixmap(pixmap) + + headerLayout = QHBoxLayout() + headerLayout.addWidget(frog_lbl) + headerLayout.addWidget(self.headerLabel) + headerLayout.addWidget(self.headerLabelSub) + headerLayout.addStretch() + self.headerBox.setLayout(headerLayout) + + def set_statusbarMessage(self, msg): + self.statusBar().showMessage(msg) + + def closeEvent(self, event): + """ + redefines close event (persistent window behaviour) + """ + if self.trayIcon.isVisible() and not self.debugmode: + QMessageBox.information(self, "Systray", + "The program will keep running " + "in the system tray. To " + "terminate the program, choose " + "Quit in the " + "context menu of the system tray entry.") + self.hide() + event.ignore() + if self.debugmode: + self.cleanupAndQuit() + + def cleanupAndQuit(self): + """ + cleans state before shutting down app. + """ + # TODO:make sure to shutdown all child process / threads + # in conductor + # XXX send signal instead? + self.conductor.cleanup() + qApp.quit() + + +class LogPane(object): def createLogBrowser(self): """ @@ -301,7 +401,7 @@ technolust") self.logbrowser = QTextBrowser() startStopButton = QPushButton("&Connect") - startStopButton.clicked.connect(self.start_or_stopVPN) + #startStopButton.clicked.connect(self.start_or_stopVPN) self.startStopButton = startStopButton logging_layout.addWidget(self.logbrowser) @@ -342,130 +442,64 @@ technolust") if self.debugmode: self.logbrowser.append(line[:-1]) - def set_statusbarMessage(self, msg): - self.statusBar().showMessage(msg) - - @pyqtSlot(object) - def onStatusChange(self, status): - """ - slot for status changes. triggers new signals for - updating icon, status bar, etc. - """ - - #print('STATUS CHANGED! (on Qt-land)') - #print('%s -> %s' % (status.previous, status.current)) - icon_name = self.conductor.get_icon_name() - self.setIcon(icon_name) - #print 'icon = ', icon_name - - # change connection pixmap widget - self.setConnWidget(icon_name) - - def setConnWidget(self, icon_name): - #print 'changing icon to %s' % icon_name - oldlayout = self.statusIconBox.layout() - - # XXX reuse with icons - # XXX move states to StateWidget - states = {"disconnected": 0, - "connecting": 1, - "connected": 2} - - for i in range(3): - oldlayout.itemAt(i).widget().hide() - new = states[icon_name] - oldlayout.itemAt(new).widget().show() - @pyqtSlot() - def start_or_stopVPN(self): - """ - stub for running child process with vpn - """ - if self.eip_service_started is False: - try: - self.conductor.connect() - # XXX move this to error queue - except eip_exceptions.EIPNoCommandError: - dialog = ErrorDialog() - dialog.warningMessage( - 'No suitable openvpn command found. ' - '
(Might be a permissions problem)', - 'error') - if self.debugmode: - self.startStopButton.setText('&Disconnect') - self.eip_service_started = True +# XXX +# main (leave only this here) +class LeapWindow(QMainWindow, LeapMainWindow, EIPConductorApp, + StatusAwareTrayIcon, + LogPane): - # XXX what is optimum polling interval? - # too little is overkill, too much - # will miss transition states.. + newLogLine = pyqtSignal([str]) + statusChange = pyqtSignal([object]) - self.timer.start(250.0) - return - if self.eip_service_started is True: - self.conductor.disconnect() - if self.debugmode: - self.startStopButton.setText('&Connect') - self.eip_service_started = False - self.timer.stop() - return + def __init__(self, opts): + logger.debug('init leap window') + super(LeapWindow, self).__init__() - @pyqtSlot() - def onTimerTick(self): - self.statusUpdate() + self.debugmode = getattr(opts, 'debug', False) + self.eip_service_started = False - @pyqtSlot() - def statusUpdate(self): - """ - called on timer tick - polls status and updates ui with real time - info about transferred bytes / connection state. - """ - # XXX it's too expensive to poll - # continously. move to signal events instead. + # create timer + self.timer = QTimer() - if not self.eip_service_started: - return + if self.debugmode: + self.createLogBrowser() + EIPConductorApp.__init__(self, opts=opts) - # XXX remove all access to manager layer - # from here. - if self.conductor.with_errors: - #XXX how to wait on pkexec??? - #something better that this workaround, plz!! - time.sleep(5) - print('errors. disconnect.') - self.start_or_stopVPN() # is stop + # LeapWindow init + self.createWindowHeader() - state = self.conductor.poll_connection_state() - if not state: - return + # StatusAwareTrayIcon init + self.createIconGroupBox() + self.createActions() + self.createTrayIcon() - ts, con_status, ok, ip, remote = state - self.set_statusbarMessage(con_status) - self.setToolTip() + widget = QWidget() + self.setCentralWidget(widget) - ts = time.strftime("%a %b %d %X", ts) + # add widgets to layout + mainLayout = QVBoxLayout() + mainLayout.addWidget(self.headerBox) + mainLayout.addWidget(self.statusIconBox) if self.debugmode: - self.updateTS.setText(ts) - self.status_label.setText(con_status) - self.ip_label.setText(ip) - self.remote_label.setText(remote) + mainLayout.addWidget(self.statusBox) + mainLayout.addWidget(self.loggerBox) + widget.setLayout(mainLayout) - # status i/o + # move to icons? + self.trayIcon.show() + self.setWindowTitle("LEAP Client") + self.resize(400, 300) + self.set_statusbarMessage('ready') - status = self.conductor.get_status_io() - if status and self.debugmode: - #XXX move this to systray menu indicators - ts, (tun_read, tun_write, tcp_read, tcp_write, auth_read) = status - ts = time.strftime("%a %b %d %X", ts) - self.updateTS.setText(ts) - self.tun_read_bytes.setText(tun_read) - self.tun_write_bytes.setText(tun_write) + # bind signals + # XXX move to parent classes init?? + self.trayIcon.activated.connect(self.iconActivated) + self.newLogLine.connect(lambda line: self.onLoggerNewLine(line)) + self.statusChange.connect(lambda status: self.onStatusChange(status)) + self.timer.timeout.connect(lambda: self.onTimerTick()) - def cleanupAndQuit(self): - """ - cleans state before shutting down app. - """ - # TODO:make sure to shutdown all child process / threads - # in conductor - self.conductor.cleanup() - qApp.quit() + # move to eipconductor init? + if self.debugmode: + self.startStopButton.clicked.connect( + lambda: self.start_or_stopVPN()) -- cgit v1.2.3 From 3fbc512a49923ac73d2413a083e0bb1f7e163866 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 4 Sep 2012 05:20:31 +0900 Subject: actual split of classes into own modules still a bit rough, but makes everything a bit more readable. --- src/leap/baseapp/eip.py | 175 ++++++++++++++++ src/leap/baseapp/leap_app.py | 57 +++++ src/leap/baseapp/log.py | 56 +++++ src/leap/baseapp/mainwindow.py | 466 ++--------------------------------------- src/leap/baseapp/systray.py | 150 +++++++++++++ 5 files changed, 461 insertions(+), 443 deletions(-) create mode 100644 src/leap/baseapp/eip.py create mode 100644 src/leap/baseapp/leap_app.py create mode 100644 src/leap/baseapp/log.py create mode 100644 src/leap/baseapp/systray.py diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py new file mode 100644 index 00000000..e8b9fe53 --- /dev/null +++ b/src/leap/baseapp/eip.py @@ -0,0 +1,175 @@ +import time + +from PyQt4 import QtCore + +from leap.baseapp.dialogs import ErrorDialog +from leap.eip import exceptions as eip_exceptions +from leap.eip.eipconnection import EIPConnection + + +class EIPConductorApp(object): + + def __init__(self, *args, **kwargs): + # + # conductor is in charge of all + # vpn-related configuration / monitoring. + # we pass a tuple of signals that will be + # triggered when status changes. + # + opts = kwargs.pop('opts') + config_file = getattr(opts, 'config_file', None) + + self.conductor = EIPConnection( + watcher_cb=self.newLogLine.emit, + config_file=config_file, + status_signals=(self.statusChange.emit, ), + debug=self.debugmode) + + # XXX remove skip download when sample service is ready + self.conductor.run_checks(skip_download=True) + self.error_check() + if self.conductor.autostart: + self.start_or_stopVPN() + + def error_check(self): + ####### error checking ################ + # + # bunch of self checks. + # XXX move somewhere else alltogether. + # + if self.conductor.missing_definition is True: + dialog = ErrorDialog() + dialog.criticalMessage( + 'The default ' + 'definition.json file cannot be found', + 'error') + + if self.conductor.missing_provider is True: + dialog = ErrorDialog() + dialog.criticalMessage( + 'Missing provider. Add a remote_ip entry ' + 'under section [provider] in eip.cfg', + 'error') + + if self.conductor.missing_vpn_keyfile is True: + dialog = ErrorDialog() + dialog.criticalMessage( + 'Could not find the vpn keys file', + 'error') + + # ... btw, review pending. + # os.kill of subprocess fails if we have + # some of this errors. + + if self.conductor.bad_provider is True: + dialog = ErrorDialog() + dialog.criticalMessage( + 'Bad provider entry. Check that remote_ip entry ' + 'has an IP under section [provider] in eip.cfg', + 'error') + + if self.conductor.bad_keyfile_perms is True: + dialog = ErrorDialog() + dialog.criticalMessage( + 'The vpn keys file has bad permissions', + 'error') + + if self.conductor.missing_auth_agent is True: + dialog = ErrorDialog() + dialog.warningMessage( + 'We could not find any authentication ' + 'agent in your system.
' + 'Make sure you have ' + 'polkit-gnome-authentication-agent-1 ' + 'running and try again.', + 'error') + + if self.conductor.missing_pkexec is True: + dialog = ErrorDialog() + dialog.warningMessage( + 'We could not find pkexec in your ' + 'system.
Do you want to try ' + 'setuid workaround? ' + '(DOES NOTHING YET)', + 'error') + + @QtCore.pyqtSlot() + def statusUpdate(self): + """ + called on timer tick + polls status and updates ui with real time + info about transferred bytes / connection state. + """ + # XXX it's too expensive to poll + # continously. move to signal events instead. + + if not self.eip_service_started: + return + + # XXX remove all access to manager layer + # from here. + if self.conductor.with_errors: + #XXX how to wait on pkexec??? + #something better that this workaround, plz!! + time.sleep(5) + print('errors. disconnect.') + self.start_or_stopVPN() # is stop + + state = self.conductor.poll_connection_state() + if not state: + return + + ts, con_status, ok, ip, remote = state + self.set_statusbarMessage(con_status) + self.setIconToolTip() + + ts = time.strftime("%a %b %d %X", ts) + if self.debugmode: + self.updateTS.setText(ts) + self.status_label.setText(con_status) + self.ip_label.setText(ip) + self.remote_label.setText(remote) + + # status i/o + + status = self.conductor.get_status_io() + if status and self.debugmode: + #XXX move this to systray menu indicators + ts, (tun_read, tun_write, tcp_read, tcp_write, auth_read) = status + ts = time.strftime("%a %b %d %X", ts) + self.updateTS.setText(ts) + self.tun_read_bytes.setText(tun_read) + self.tun_write_bytes.setText(tun_write) + + @QtCore.pyqtSlot() + def start_or_stopVPN(self): + """ + stub for running child process with vpn + """ + if self.eip_service_started is False: + try: + self.conductor.connect() + # XXX move this to error queue + except eip_exceptions.EIPNoCommandError: + dialog = ErrorDialog() + dialog.warningMessage( + 'No suitable openvpn command found. ' + '
(Might be a permissions problem)', + 'error') + if self.debugmode: + self.startStopButton.setText('&Disconnect') + self.eip_service_started = True + + # XXX what is optimum polling interval? + # too little is overkill, too much + # will miss transition states.. + + self.timer.start(250.0) + return + if self.eip_service_started is True: + self.conductor.disconnect() + if self.debugmode: + self.startStopButton.setText('&Connect') + self.eip_service_started = False + self.timer.stop() + return diff --git a/src/leap/baseapp/leap_app.py b/src/leap/baseapp/leap_app.py new file mode 100644 index 00000000..fb736ee3 --- /dev/null +++ b/src/leap/baseapp/leap_app.py @@ -0,0 +1,57 @@ +from PyQt4 import QtGui + +from leap.gui import mainwindow_rc + + +class MainWindow(object): + + def createWindowHeader(self): + """ + description lines for main window + """ + self.headerBox = QtGui.QGroupBox() + self.headerLabel = QtGui.QLabel("Encryption \ +Internet Proxy") + self.headerLabelSub = QtGui.QLabel("trust your \ +technolust") + + pixmap = QtGui.QPixmap(':/images/leapfrog.jpg') + frog_lbl = QtGui.QLabel() + frog_lbl.setPixmap(pixmap) + + headerLayout = QtGui.QHBoxLayout() + headerLayout.addWidget(frog_lbl) + headerLayout.addWidget(self.headerLabel) + headerLayout.addWidget(self.headerLabelSub) + headerLayout.addStretch() + self.headerBox.setLayout(headerLayout) + + def set_statusbarMessage(self, msg): + self.statusBar().showMessage(msg) + + def closeEvent(self, event): + """ + redefines close event (persistent window behaviour) + """ + if self.trayIcon.isVisible() and not self.debugmode: + QtGui.QMessageBox.information( + self, "Systray", + "The program will keep running " + "in the system tray. To " + "terminate the program, choose " + "Quit in the " + "context menu of the system tray entry.") + self.hide() + event.ignore() + if self.debugmode: + self.cleanupAndQuit() + + def cleanupAndQuit(self): + """ + cleans state before shutting down app. + """ + # TODO:make sure to shutdown all child process / threads + # in conductor + # XXX send signal instead? + self.conductor.cleanup() + QtGui.qApp.quit() diff --git a/src/leap/baseapp/log.py b/src/leap/baseapp/log.py new file mode 100644 index 00000000..139de845 --- /dev/null +++ b/src/leap/baseapp/log.py @@ -0,0 +1,56 @@ +from PyQt4 import QtGui +from PyQt4 import QtCore + + +class LogPane(object): + + def createLogBrowser(self): + """ + creates Browser widget for displaying logs + (in debug mode only). + """ + self.loggerBox = QtGui.QGroupBox() + logging_layout = QtGui.QVBoxLayout() + self.logbrowser = QtGui.QTextBrowser() + + startStopButton = QtGui.QPushButton("&Connect") + #startStopButton.clicked.connect(self.start_or_stopVPN) + self.startStopButton = startStopButton + + logging_layout.addWidget(self.logbrowser) + logging_layout.addWidget(self.startStopButton) + self.loggerBox.setLayout(logging_layout) + + # status box + + self.statusBox = QtGui.QGroupBox() + grid = QtGui.QGridLayout() + + self.updateTS = QtGui.QLabel('') + self.status_label = QtGui.QLabel('Disconnected') + self.ip_label = QtGui.QLabel('') + self.remote_label = QtGui.QLabel('') + + tun_read_label = QtGui.QLabel("tun read") + self.tun_read_bytes = QtGui.QLabel("0") + tun_write_label = QtGui.QLabel("tun write") + self.tun_write_bytes = QtGui.QLabel("0") + + grid.addWidget(self.updateTS, 0, 0) + grid.addWidget(self.status_label, 0, 1) + grid.addWidget(self.ip_label, 1, 0) + grid.addWidget(self.remote_label, 1, 1) + grid.addWidget(tun_read_label, 2, 0) + grid.addWidget(self.tun_read_bytes, 2, 1) + grid.addWidget(tun_write_label, 3, 0) + grid.addWidget(self.tun_write_bytes, 3, 1) + + self.statusBox.setLayout(grid) + + @QtCore.pyqtSlot(str) + def onLoggerNewLine(self, line): + """ + simple slot: writes new line to logger Pane. + """ + if self.debugmode: + self.logbrowser.append(line[:-1]) diff --git a/src/leap/baseapp/mainwindow.py b/src/leap/baseapp/mainwindow.py index ca9b79b3..917fc184 100644 --- a/src/leap/baseapp/mainwindow.py +++ b/src/leap/baseapp/mainwindow.py @@ -1,456 +1,31 @@ # vim: set fileencoding=utf-8 : #!/usr/bin/env python import logging -import time logging.basicConfig() logger = logging.getLogger(name=__name__) logger.setLevel(logging.DEBUG) -from PyQt4.QtGui import (QMainWindow, QWidget, QVBoxLayout, QMessageBox, - QSystemTrayIcon, QGroupBox, QLabel, QPixmap, - QHBoxLayout, QIcon, - QPushButton, QGridLayout, QAction, QMenu, - QTextBrowser, qApp) -from PyQt4.QtCore import (pyqtSlot, pyqtSignal, QTimer) +from PyQt4 import QtCore +from PyQt4 import QtGui -from leap.baseapp.dialogs import ErrorDialog -from leap.eip import exceptions as eip_exceptions -from leap.eip.eipconnection import EIPConnection +from leap.baseapp.eip import EIPConductorApp +from leap.baseapp.log import LogPane +from leap.baseapp.systray import StatusAwareTrayIcon +from leap.baseapp.leap_app import MainWindow from leap.gui import mainwindow_rc -class EIPConductorApp(object): - - def __init__(self, *args, **kwargs): - # - # conductor is in charge of all - # vpn-related configuration / monitoring. - # we pass a tuple of signals that will be - # triggered when status changes. - # - opts = kwargs.pop('opts') - config_file = getattr(opts, 'config_file', None) - - self.conductor = EIPConnection( - watcher_cb=self.newLogLine.emit, - config_file=config_file, - status_signals=(self.statusChange.emit, ), - debug=self.debugmode) - - # XXX remove skip download when sample service is ready - self.conductor.run_checks(skip_download=True) - self.error_check() - if self.conductor.autostart: - self.start_or_stopVPN() - - def error_check(self): - ####### error checking ################ - # - # bunch of self checks. - # XXX move somewhere else alltogether. - # - if self.conductor.missing_definition is True: - dialog = ErrorDialog() - dialog.criticalMessage( - 'The default ' - 'definition.json file cannot be found', - 'error') - - if self.conductor.missing_provider is True: - dialog = ErrorDialog() - dialog.criticalMessage( - 'Missing provider. Add a remote_ip entry ' - 'under section [provider] in eip.cfg', - 'error') - - if self.conductor.missing_vpn_keyfile is True: - dialog = ErrorDialog() - dialog.criticalMessage( - 'Could not find the vpn keys file', - 'error') - - # ... btw, review pending. - # os.kill of subprocess fails if we have - # some of this errors. - - if self.conductor.bad_provider is True: - dialog = ErrorDialog() - dialog.criticalMessage( - 'Bad provider entry. Check that remote_ip entry ' - 'has an IP under section [provider] in eip.cfg', - 'error') - - if self.conductor.bad_keyfile_perms is True: - dialog = ErrorDialog() - dialog.criticalMessage( - 'The vpn keys file has bad permissions', - 'error') - - if self.conductor.missing_auth_agent is True: - dialog = ErrorDialog() - dialog.warningMessage( - 'We could not find any authentication ' - 'agent in your system.
' - 'Make sure you have ' - 'polkit-gnome-authentication-agent-1 ' - 'running and try again.', - 'error') - - if self.conductor.missing_pkexec is True: - dialog = ErrorDialog() - dialog.warningMessage( - 'We could not find pkexec in your ' - 'system.
Do you want to try ' - 'setuid workaround? ' - '(DOES NOTHING YET)', - 'error') - - ############ end error checking ################### - @pyqtSlot() - def statusUpdate(self): - """ - called on timer tick - polls status and updates ui with real time - info about transferred bytes / connection state. - """ - # XXX it's too expensive to poll - # continously. move to signal events instead. - - if not self.eip_service_started: - return - - # XXX remove all access to manager layer - # from here. - if self.conductor.with_errors: - #XXX how to wait on pkexec??? - #something better that this workaround, plz!! - time.sleep(5) - print('errors. disconnect.') - self.start_or_stopVPN() # is stop - - state = self.conductor.poll_connection_state() - if not state: - return - - ts, con_status, ok, ip, remote = state - self.set_statusbarMessage(con_status) - self.setIconToolTip() - - ts = time.strftime("%a %b %d %X", ts) - if self.debugmode: - self.updateTS.setText(ts) - self.status_label.setText(con_status) - self.ip_label.setText(ip) - self.remote_label.setText(remote) - - # status i/o - - status = self.conductor.get_status_io() - if status and self.debugmode: - #XXX move this to systray menu indicators - ts, (tun_read, tun_write, tcp_read, tcp_write, auth_read) = status - ts = time.strftime("%a %b %d %X", ts) - self.updateTS.setText(ts) - self.tun_read_bytes.setText(tun_read) - self.tun_write_bytes.setText(tun_write) - - @pyqtSlot() - def start_or_stopVPN(self): - """ - stub for running child process with vpn - """ - if self.eip_service_started is False: - try: - self.conductor.connect() - # XXX move this to error queue - except eip_exceptions.EIPNoCommandError: - dialog = ErrorDialog() - dialog.warningMessage( - 'No suitable openvpn command found. ' - '
(Might be a permissions problem)', - 'error') - if self.debugmode: - self.startStopButton.setText('&Disconnect') - self.eip_service_started = True - - # XXX what is optimum polling interval? - # too little is overkill, too much - # will miss transition states.. - - self.timer.start(250.0) - return - if self.eip_service_started is True: - self.conductor.disconnect() - if self.debugmode: - self.startStopButton.setText('&Connect') - self.eip_service_started = False - self.timer.stop() - return - - -class StatusAwareTrayIcon(object): - - def createIconGroupBox(self): - """ - dummy icongroupbox - (to be removed from here -- reference only) - """ - icons = { - 'disconnected': ':/images/conn_error.png', - 'connecting': ':/images/conn_connecting.png', - 'connected': ':/images/conn_connected.png' - } - con_widgets = { - 'disconnected': QLabel(), - 'connecting': QLabel(), - 'connected': QLabel(), - } - con_widgets['disconnected'].setPixmap( - QPixmap(icons['disconnected'])) - con_widgets['connecting'].setPixmap( - QPixmap(icons['connecting'])) - con_widgets['connected'].setPixmap( - QPixmap(icons['connected'])), - self.ConnectionWidgets = con_widgets - - con_icons = { - 'disconnected': QIcon(icons['disconnected']), - 'connecting': QIcon(icons['connecting']), - 'connected': QIcon(icons['connected']) - } - self.Icons = con_icons - - self.statusIconBox = QGroupBox("Connection Status") - statusIconLayout = QHBoxLayout() - statusIconLayout.addWidget(self.ConnectionWidgets['disconnected']) - statusIconLayout.addWidget(self.ConnectionWidgets['connecting']) - statusIconLayout.addWidget(self.ConnectionWidgets['connected']) - statusIconLayout.itemAt(1).widget().hide() - statusIconLayout.itemAt(2).widget().hide() - self.statusIconBox.setLayout(statusIconLayout) - - def createTrayIcon(self): - """ - creates the tray icon - """ - self.trayIconMenu = QMenu(self) - - self.trayIconMenu.addAction(self.connectVPNAction) - self.trayIconMenu.addAction(self.dis_connectAction) - self.trayIconMenu.addSeparator() - self.trayIconMenu.addAction(self.minimizeAction) - self.trayIconMenu.addAction(self.maximizeAction) - self.trayIconMenu.addAction(self.restoreAction) - self.trayIconMenu.addSeparator() - self.trayIconMenu.addAction(self.quitAction) - - self.trayIcon = QSystemTrayIcon(self) - self.setIcon('disconnected') - self.trayIcon.setContextMenu(self.trayIconMenu) - - def createActions(self): - """ - creates actions to be binded to tray icon - """ - self.connectVPNAction = QAction("Connect to &VPN", self, - triggered=self.hide) - # XXX change action name on (dis)connect - self.dis_connectAction = QAction( - "&(Dis)connect", self, - triggered=lambda: self.start_or_stopVPN()) - self.minimizeAction = QAction("Mi&nimize", self, - triggered=self.hide) - self.maximizeAction = QAction("Ma&ximize", self, - triggered=self.showMaximized) - self.restoreAction = QAction("&Restore", self, - triggered=self.showNormal) - self.quitAction = QAction("&Quit", self, - triggered=self.cleanupAndQuit) - - def setConnWidget(self, icon_name): - #print 'changing icon to %s' % icon_name - oldlayout = self.statusIconBox.layout() - - # XXX reuse with icons - # XXX move states to StateWidget - states = {"disconnected": 0, - "connecting": 1, - "connected": 2} - - for i in range(3): - oldlayout.itemAt(i).widget().hide() - new = states[icon_name] - oldlayout.itemAt(new).widget().show() - - def setIcon(self, name): - icon = self.Icons.get(name) - self.trayIcon.setIcon(icon) - self.setWindowIcon(icon) - - def getIcon(self, icon_name): - # XXX get from connection dict - icons = {'disconnected': 0, - 'connecting': 1, - 'connected': 2} - return icons.get(icon_name, None) - - def setIconToolTip(self): - """ - get readable status and place it on systray tooltip - """ - status = self.conductor.status.get_readable_status() - self.trayIcon.setToolTip(status) - - def iconActivated(self, reason): - """ - handles left click, left double click - showing the trayicon menu - """ - #XXX there's a bug here! - #menu shows on (0,0) corner first time, - #until double clicked at least once. - if reason in (QSystemTrayIcon.Trigger, - QSystemTrayIcon.DoubleClick): - self.trayIconMenu.show() - - @pyqtSlot() - def onTimerTick(self): - self.statusUpdate() - - @pyqtSlot(object) - def onStatusChange(self, status): - """ - slot for status changes. triggers new signals for - updating icon, status bar, etc. - """ - - #print('STATUS CHANGED! (on Qt-land)') - #print('%s -> %s' % (status.previous, status.current)) - icon_name = self.conductor.get_icon_name() - self.setIcon(icon_name) - #print 'icon = ', icon_name - - # change connection pixmap widget - self.setConnWidget(icon_name) - - -class LeapMainWindow(object): - - def createWindowHeader(self): - """ - description lines for main window - """ - #XXX good candidate to refactor out! :) - self.headerBox = QGroupBox() - self.headerLabel = QLabel("Encryption \ -Internet Proxy") - self.headerLabelSub = QLabel("trust your \ -technolust") - - pixmap = QPixmap(':/images/leapfrog.jpg') - frog_lbl = QLabel() - frog_lbl.setPixmap(pixmap) - - headerLayout = QHBoxLayout() - headerLayout.addWidget(frog_lbl) - headerLayout.addWidget(self.headerLabel) - headerLayout.addWidget(self.headerLabelSub) - headerLayout.addStretch() - self.headerBox.setLayout(headerLayout) - - def set_statusbarMessage(self, msg): - self.statusBar().showMessage(msg) - - def closeEvent(self, event): - """ - redefines close event (persistent window behaviour) - """ - if self.trayIcon.isVisible() and not self.debugmode: - QMessageBox.information(self, "Systray", - "The program will keep running " - "in the system tray. To " - "terminate the program, choose " - "Quit in the " - "context menu of the system tray entry.") - self.hide() - event.ignore() - if self.debugmode: - self.cleanupAndQuit() - - def cleanupAndQuit(self): - """ - cleans state before shutting down app. - """ - # TODO:make sure to shutdown all child process / threads - # in conductor - # XXX send signal instead? - self.conductor.cleanup() - qApp.quit() - - -class LogPane(object): - - def createLogBrowser(self): - """ - creates Browser widget for displaying logs - (in debug mode only). - """ - self.loggerBox = QGroupBox() - logging_layout = QVBoxLayout() - self.logbrowser = QTextBrowser() - - startStopButton = QPushButton("&Connect") - #startStopButton.clicked.connect(self.start_or_stopVPN) - self.startStopButton = startStopButton - - logging_layout.addWidget(self.logbrowser) - logging_layout.addWidget(self.startStopButton) - self.loggerBox.setLayout(logging_layout) - - # status box - - self.statusBox = QGroupBox() - grid = QGridLayout() - - self.updateTS = QLabel('') - self.status_label = QLabel('Disconnected') - self.ip_label = QLabel('') - self.remote_label = QLabel('') - - tun_read_label = QLabel("tun read") - self.tun_read_bytes = QLabel("0") - tun_write_label = QLabel("tun write") - self.tun_write_bytes = QLabel("0") - - grid.addWidget(self.updateTS, 0, 0) - grid.addWidget(self.status_label, 0, 1) - grid.addWidget(self.ip_label, 1, 0) - grid.addWidget(self.remote_label, 1, 1) - grid.addWidget(tun_read_label, 2, 0) - grid.addWidget(self.tun_read_bytes, 2, 1) - grid.addWidget(tun_write_label, 3, 0) - grid.addWidget(self.tun_write_bytes, 3, 1) - - self.statusBox.setLayout(grid) - - @pyqtSlot(str) - def onLoggerNewLine(self, line): - """ - simple slot: writes new line to logger Pane. - """ - if self.debugmode: - self.logbrowser.append(line[:-1]) - - -# XXX -# main (leave only this here) -class LeapWindow(QMainWindow, LeapMainWindow, EIPConductorApp, +class LeapWindow(QtGui.QMainWindow, + MainWindow, EIPConductorApp, StatusAwareTrayIcon, LogPane): - newLogLine = pyqtSignal([str]) - statusChange = pyqtSignal([object]) + # move to log + newLogLine = QtCore.pyqtSignal([str]) + + # move to icons + statusChange = QtCore.pyqtSignal([object]) def __init__(self, opts): logger.debug('init leap window') @@ -459,8 +34,10 @@ class LeapWindow(QMainWindow, LeapMainWindow, EIPConductorApp, self.debugmode = getattr(opts, 'debug', False) self.eip_service_started = False - # create timer - self.timer = QTimer() + # create timer ############################## + # move to Icons init?? + self.timer = QtCore.QTimer() + ############################################# if self.debugmode: self.createLogBrowser() @@ -469,22 +46,25 @@ class LeapWindow(QMainWindow, LeapMainWindow, EIPConductorApp, # LeapWindow init self.createWindowHeader() - # StatusAwareTrayIcon init + # StatusAwareTrayIcon init ################### self.createIconGroupBox() self.createActions() self.createTrayIcon() + ############################################## - widget = QWidget() + # move to MainWindow init #################### + widget = QtGui.QWidget() self.setCentralWidget(widget) # add widgets to layout - mainLayout = QVBoxLayout() + mainLayout = QtGui.QVBoxLayout() mainLayout.addWidget(self.headerBox) mainLayout.addWidget(self.statusIconBox) if self.debugmode: mainLayout.addWidget(self.statusBox) mainLayout.addWidget(self.loggerBox) widget.setLayout(mainLayout) + ############################################### # move to icons? self.trayIcon.show() diff --git a/src/leap/baseapp/systray.py b/src/leap/baseapp/systray.py new file mode 100644 index 00000000..7ef5cb01 --- /dev/null +++ b/src/leap/baseapp/systray.py @@ -0,0 +1,150 @@ +from PyQt4 import QtCore +from PyQt4 import QtGui + +from leap.gui import mainwindow_rc + + +class StatusAwareTrayIcon(object): + + def createIconGroupBox(self): + """ + dummy icongroupbox + (to be removed from here -- reference only) + """ + icons = { + 'disconnected': ':/images/conn_error.png', + 'connecting': ':/images/conn_connecting.png', + 'connected': ':/images/conn_connected.png' + } + con_widgets = { + 'disconnected': QtGui.QLabel(), + 'connecting': QtGui.QLabel(), + 'connected': QtGui.QLabel(), + } + con_widgets['disconnected'].setPixmap( + QtGui.QPixmap(icons['disconnected'])) + con_widgets['connecting'].setPixmap( + QtGui.QPixmap(icons['connecting'])) + con_widgets['connected'].setPixmap( + QtGui.QPixmap(icons['connected'])), + self.ConnectionWidgets = con_widgets + + con_icons = { + 'disconnected': QtGui.QIcon(icons['disconnected']), + 'connecting': QtGui.QIcon(icons['connecting']), + 'connected': QtGui.QIcon(icons['connected']) + } + self.Icons = con_icons + + self.statusIconBox = QtGui.QGroupBox("Connection Status") + statusIconLayout = QtGui.QHBoxLayout() + statusIconLayout.addWidget(self.ConnectionWidgets['disconnected']) + statusIconLayout.addWidget(self.ConnectionWidgets['connecting']) + statusIconLayout.addWidget(self.ConnectionWidgets['connected']) + statusIconLayout.itemAt(1).widget().hide() + statusIconLayout.itemAt(2).widget().hide() + self.statusIconBox.setLayout(statusIconLayout) + + def createTrayIcon(self): + """ + creates the tray icon + """ + self.trayIconMenu = QtGui.QMenu(self) + + self.trayIconMenu.addAction(self.connectVPNAction) + self.trayIconMenu.addAction(self.dis_connectAction) + self.trayIconMenu.addSeparator() + self.trayIconMenu.addAction(self.minimizeAction) + self.trayIconMenu.addAction(self.maximizeAction) + self.trayIconMenu.addAction(self.restoreAction) + self.trayIconMenu.addSeparator() + self.trayIconMenu.addAction(self.quitAction) + + self.trayIcon = QtGui.QSystemTrayIcon(self) + self.setIcon('disconnected') + self.trayIcon.setContextMenu(self.trayIconMenu) + + def createActions(self): + """ + creates actions to be binded to tray icon + """ + self.connectVPNAction = QtGui.QAction("Connect to &VPN", self, + triggered=self.hide) + # XXX change action name on (dis)connect + self.dis_connectAction = QtGui.QAction( + "&(Dis)connect", self, + triggered=lambda: self.start_or_stopVPN()) + self.minimizeAction = QtGui.QAction("Mi&nimize", self, + triggered=self.hide) + self.maximizeAction = QtGui.QAction("Ma&ximize", self, + triggered=self.showMaximized) + self.restoreAction = QtGui.QAction("&Restore", self, + triggered=self.showNormal) + self.quitAction = QtGui.QAction("&Quit", self, + triggered=self.cleanupAndQuit) + + def setConnWidget(self, icon_name): + #print 'changing icon to %s' % icon_name + oldlayout = self.statusIconBox.layout() + + # XXX reuse with icons + # XXX move states to StateWidget + states = {"disconnected": 0, + "connecting": 1, + "connected": 2} + + for i in range(3): + oldlayout.itemAt(i).widget().hide() + new = states[icon_name] + oldlayout.itemAt(new).widget().show() + + def setIcon(self, name): + icon = self.Icons.get(name) + self.trayIcon.setIcon(icon) + self.setWindowIcon(icon) + + def getIcon(self, icon_name): + # XXX get from connection dict + icons = {'disconnected': 0, + 'connecting': 1, + 'connected': 2} + return icons.get(icon_name, None) + + def setIconToolTip(self): + """ + get readable status and place it on systray tooltip + """ + status = self.conductor.status.get_readable_status() + self.trayIcon.setToolTip(status) + + def iconActivated(self, reason): + """ + handles left click, left double click + showing the trayicon menu + """ + #XXX there's a bug here! + #menu shows on (0,0) corner first time, + #until double clicked at least once. + if reason in (QtGui.QSystemTrayIcon.Trigger, + QtGui.QSystemTrayIcon.DoubleClick): + self.trayIconMenu.show() + + @QtCore.pyqtSlot() + def onTimerTick(self): + self.statusUpdate() + + @QtCore.pyqtSlot(object) + def onStatusChange(self, status): + """ + slot for status changes. triggers new signals for + updating icon, status bar, etc. + """ + + #print('STATUS CHANGED! (on Qt-land)') + #print('%s -> %s' % (status.previous, status.current)) + icon_name = self.conductor.get_icon_name() + self.setIcon(icon_name) + #print 'icon = ', icon_name + + # change connection pixmap widget + self.setConnWidget(icon_name) -- cgit v1.2.3 From b0b2b342b698bbe5851e9312cd830938f8d564a5 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 4 Sep 2012 06:01:50 +0900 Subject: further cleaning of main window by moving init functions to their base classes. plus a bit of juggling with order. --- src/leap/baseapp/eip.py | 13 +++++++-- src/leap/baseapp/leap_app.py | 22 ++++++++++++++ src/leap/baseapp/mainwindow.py | 66 ++++++++++-------------------------------- src/leap/baseapp/systray.py | 11 +++++++ 4 files changed, 60 insertions(+), 52 deletions(-) diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py index e8b9fe53..a67fd916 100644 --- a/src/leap/baseapp/eip.py +++ b/src/leap/baseapp/eip.py @@ -19,6 +19,8 @@ class EIPConductorApp(object): opts = kwargs.pop('opts') config_file = getattr(opts, 'config_file', None) + self.eip_service_started = False + self.conductor = EIPConnection( watcher_cb=self.newLogLine.emit, config_file=config_file, @@ -28,8 +30,15 @@ class EIPConductorApp(object): # XXX remove skip download when sample service is ready self.conductor.run_checks(skip_download=True) self.error_check() - if self.conductor.autostart: - self.start_or_stopVPN() + + # XXX should receive "ready" signal + #if self.conductor.autostart: + #self.start_or_stopVPN() + + # move to eipconductor init? + if self.debugmode: + self.startStopButton.clicked.connect( + lambda: self.start_or_stopVPN()) def error_check(self): ####### error checking ################ diff --git a/src/leap/baseapp/leap_app.py b/src/leap/baseapp/leap_app.py index fb736ee3..1b4d7747 100644 --- a/src/leap/baseapp/leap_app.py +++ b/src/leap/baseapp/leap_app.py @@ -5,6 +5,28 @@ from leap.gui import mainwindow_rc class MainWindow(object): + def __init__(self, *args, **kwargs): + # XXX set initial visibility + # debug = no visible + + widget = QtGui.QWidget() + self.setCentralWidget(widget) + + self.createWindowHeader() + + # add widgets to layout + mainLayout = QtGui.QVBoxLayout() + mainLayout.addWidget(self.headerBox) + mainLayout.addWidget(self.statusIconBox) + if self.debugmode: + mainLayout.addWidget(self.statusBox) + mainLayout.addWidget(self.loggerBox) + widget.setLayout(mainLayout) + + self.setWindowTitle("LEAP Client") + self.resize(400, 300) + self.set_statusbarMessage('ready') + def createWindowHeader(self): """ description lines for main window diff --git a/src/leap/baseapp/mainwindow.py b/src/leap/baseapp/mainwindow.py index 917fc184..7cd02979 100644 --- a/src/leap/baseapp/mainwindow.py +++ b/src/leap/baseapp/mainwindow.py @@ -13,73 +13,39 @@ from leap.baseapp.log import LogPane from leap.baseapp.systray import StatusAwareTrayIcon from leap.baseapp.leap_app import MainWindow -from leap.gui import mainwindow_rc - class LeapWindow(QtGui.QMainWindow, MainWindow, EIPConductorApp, StatusAwareTrayIcon, LogPane): - # move to log newLogLine = QtCore.pyqtSignal([str]) - - # move to icons statusChange = QtCore.pyqtSignal([object]) def __init__(self, opts): logger.debug('init leap window') - super(LeapWindow, self).__init__() - self.debugmode = getattr(opts, 'debug', False) - self.eip_service_started = False - - # create timer ############################## - # move to Icons init?? - self.timer = QtCore.QTimer() - ############################################# + super(LeapWindow, self).__init__() if self.debugmode: self.createLogBrowser() EIPConductorApp.__init__(self, opts=opts) - - # LeapWindow init - self.createWindowHeader() - - # StatusAwareTrayIcon init ################### - self.createIconGroupBox() - self.createActions() - self.createTrayIcon() - ############################################## - - # move to MainWindow init #################### - widget = QtGui.QWidget() - self.setCentralWidget(widget) - - # add widgets to layout - mainLayout = QtGui.QVBoxLayout() - mainLayout.addWidget(self.headerBox) - mainLayout.addWidget(self.statusIconBox) - if self.debugmode: - mainLayout.addWidget(self.statusBox) - mainLayout.addWidget(self.loggerBox) - widget.setLayout(mainLayout) - ############################################### - - # move to icons? - self.trayIcon.show() - self.setWindowTitle("LEAP Client") - self.resize(400, 300) - self.set_statusbarMessage('ready') + StatusAwareTrayIcon.__init__(self) + MainWindow.__init__(self) # bind signals # XXX move to parent classes init?? self.trayIcon.activated.connect(self.iconActivated) - self.newLogLine.connect(lambda line: self.onLoggerNewLine(line)) - self.statusChange.connect(lambda status: self.onStatusChange(status)) - self.timer.timeout.connect(lambda: self.onTimerTick()) - - # move to eipconductor init? - if self.debugmode: - self.startStopButton.clicked.connect( - lambda: self.start_or_stopVPN()) + self.newLogLine.connect( + lambda line: self.onLoggerNewLine(line)) + self.statusChange.connect( + lambda status: self.onStatusChange(status)) + self.timer.timeout.connect( + lambda: self.onTimerTick()) + + # ... all ready. go! + + # could send "ready" signal instead + # eipapp should catch that + if self.conductor.autostart: + self.start_or_stopVPN() diff --git a/src/leap/baseapp/systray.py b/src/leap/baseapp/systray.py index 7ef5cb01..249a4f7e 100644 --- a/src/leap/baseapp/systray.py +++ b/src/leap/baseapp/systray.py @@ -6,6 +6,17 @@ from leap.gui import mainwindow_rc class StatusAwareTrayIcon(object): + def __init__(self, *args, **kwargs): + # StatusAwareTrayIcon init ################### + self.createIconGroupBox() + self.createActions() + self.createTrayIcon() + + self.trayIcon.show() + ############################################## + + self.timer = QtCore.QTimer() + def createIconGroupBox(self): """ dummy icongroupbox -- cgit v1.2.3 From 1826d9a0d5400c21a3f7af73eda2e843f0639271 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 4 Sep 2012 06:37:45 +0900 Subject: add little docstrings to classes --- src/leap/baseapp/eip.py | 45 ++++++++++++++++++++++++++---------------- src/leap/baseapp/leap_app.py | 4 ++++ src/leap/baseapp/log.py | 4 ++++ src/leap/baseapp/mainwindow.py | 6 ++++++ src/leap/baseapp/systray.py | 10 +++++++--- 5 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py index a67fd916..6c3249ff 100644 --- a/src/leap/baseapp/eip.py +++ b/src/leap/baseapp/eip.py @@ -8,19 +8,25 @@ from leap.eip.eipconnection import EIPConnection class EIPConductorApp(object): + """ + initializes an instance of EIPConnection, + gathers errors, and passes status-change signals + from Qt land along to the conductor. + Connects the eip connect/disconnect logic + to the switches in the app (buttons/menu items). + """ def __init__(self, *args, **kwargs): - # - # conductor is in charge of all - # vpn-related configuration / monitoring. - # we pass a tuple of signals that will be - # triggered when status changes. - # opts = kwargs.pop('opts') config_file = getattr(opts, 'config_file', None) self.eip_service_started = False + # conductor (eip connection) is in charge of all + # vpn-related configuration / monitoring. + # we pass a tuple of signals that will be + # triggered when status changes. + self.conductor = EIPConnection( watcher_cb=self.newLogLine.emit, config_file=config_file, @@ -32,20 +38,18 @@ class EIPConductorApp(object): self.error_check() # XXX should receive "ready" signal + # it is called from LeapWindow now. #if self.conductor.autostart: #self.start_or_stopVPN() - # move to eipconductor init? if self.debugmode: self.startStopButton.clicked.connect( lambda: self.start_or_stopVPN()) def error_check(self): - ####### error checking ################ - # - # bunch of self checks. - # XXX move somewhere else alltogether. - # + + # XXX refactor (by #504) + if self.conductor.missing_definition is True: dialog = ErrorDialog() dialog.criticalMessage( @@ -105,22 +109,23 @@ class EIPConductorApp(object): @QtCore.pyqtSlot() def statusUpdate(self): """ - called on timer tick polls status and updates ui with real time info about transferred bytes / connection state. + right now is triggered by a timer tick + (timer controlled by StatusAwareTrayIcon class) """ - # XXX it's too expensive to poll + # TODO I guess it's too expensive to poll # continously. move to signal events instead. + # (i.e., subscribe to connection status changes + # from openvpn manager) if not self.eip_service_started: return - # XXX remove all access to manager layer - # from here. if self.conductor.with_errors: #XXX how to wait on pkexec??? #something better that this workaround, plz!! - time.sleep(5) + time.sleep(2) print('errors. disconnect.') self.start_or_stopVPN() # is stop @@ -173,8 +178,14 @@ class EIPConductorApp(object): # too little is overkill, too much # will miss transition states.. + # XXX decouple! (timer is init by icons class). + # should bring it here? + # to its own class? + + # XXX get constant from somewhere else self.timer.start(250.0) return + if self.eip_service_started is True: self.conductor.disconnect() if self.debugmode: diff --git a/src/leap/baseapp/leap_app.py b/src/leap/baseapp/leap_app.py index 1b4d7747..def95da1 100644 --- a/src/leap/baseapp/leap_app.py +++ b/src/leap/baseapp/leap_app.py @@ -4,6 +4,10 @@ from leap.gui import mainwindow_rc class MainWindow(object): + """ + create the main window + for leap app + """ def __init__(self, *args, **kwargs): # XXX set initial visibility diff --git a/src/leap/baseapp/log.py b/src/leap/baseapp/log.py index 139de845..0c98eb94 100644 --- a/src/leap/baseapp/log.py +++ b/src/leap/baseapp/log.py @@ -3,6 +3,10 @@ from PyQt4 import QtCore class LogPane(object): + """ + a simple log pane + that writes new lines as they come + """ def createLogBrowser(self): """ diff --git a/src/leap/baseapp/mainwindow.py b/src/leap/baseapp/mainwindow.py index 7cd02979..ac7fe9c4 100644 --- a/src/leap/baseapp/mainwindow.py +++ b/src/leap/baseapp/mainwindow.py @@ -18,6 +18,12 @@ class LeapWindow(QtGui.QMainWindow, MainWindow, EIPConductorApp, StatusAwareTrayIcon, LogPane): + """ + main window for the leap app. + Initializes all of its base classes + We keep here some signal initialization + that gets tricky otherwise. + """ newLogLine = QtCore.pyqtSignal([str]) statusChange = QtCore.pyqtSignal([object]) diff --git a/src/leap/baseapp/systray.py b/src/leap/baseapp/systray.py index 249a4f7e..3fb64db1 100644 --- a/src/leap/baseapp/systray.py +++ b/src/leap/baseapp/systray.py @@ -5,16 +5,20 @@ from leap.gui import mainwindow_rc class StatusAwareTrayIcon(object): + """ + a mix of several functions needed + to create a systray and make it + get updated from conductor status + polling. + """ def __init__(self, *args, **kwargs): - # StatusAwareTrayIcon init ################### self.createIconGroupBox() self.createActions() self.createTrayIcon() - self.trayIcon.show() - ############################################## + # not sure if this really belongs here, but... self.timer = QtCore.QTimer() def createIconGroupBox(self): -- cgit v1.2.3 From 8ef9f6f6f155b4acd0a69f1611058c4f0ba07d42 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 4 Sep 2012 06:58:15 +0900 Subject: refactor icon/iconpath dict closes #331 --- src/leap/baseapp/systray.py | 61 ++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/src/leap/baseapp/systray.py b/src/leap/baseapp/systray.py index 3fb64db1..f3832473 100644 --- a/src/leap/baseapp/systray.py +++ b/src/leap/baseapp/systray.py @@ -11,6 +11,24 @@ class StatusAwareTrayIcon(object): get updated from conductor status polling. """ + states = { + "disconnected": 0, + "connecting": 1, + "connected": 2} + + iconpath = { + "disconnected": ':/images/conn_error.png', + "connecting": ':/images/conn_connecting.png', + "connected": ':/images/conn_connected.png'} + + Icons = { + 'disconnected': lambda self: QtGui.QIcon( + self.iconpath['disconnected']), + 'connecting': lambda self: QtGui.QIcon( + self.iconpath['connecting']), + 'connected': lambda self: QtGui.QIcon( + self.iconpath['connected']) + } def __init__(self, *args, **kwargs): self.createIconGroupBox() @@ -26,31 +44,22 @@ class StatusAwareTrayIcon(object): dummy icongroupbox (to be removed from here -- reference only) """ - icons = { - 'disconnected': ':/images/conn_error.png', - 'connecting': ':/images/conn_connecting.png', - 'connected': ':/images/conn_connected.png' - } con_widgets = { 'disconnected': QtGui.QLabel(), 'connecting': QtGui.QLabel(), 'connected': QtGui.QLabel(), } con_widgets['disconnected'].setPixmap( - QtGui.QPixmap(icons['disconnected'])) + QtGui.QPixmap( + self.iconpath['disconnected'])) con_widgets['connecting'].setPixmap( - QtGui.QPixmap(icons['connecting'])) + QtGui.QPixmap( + self.iconpath['connecting'])) con_widgets['connected'].setPixmap( - QtGui.QPixmap(icons['connected'])), + QtGui.QPixmap( + self.iconpath['connected'])), self.ConnectionWidgets = con_widgets - con_icons = { - 'disconnected': QtGui.QIcon(icons['disconnected']), - 'connecting': QtGui.QIcon(icons['connecting']), - 'connected': QtGui.QIcon(icons['connected']) - } - self.Icons = con_icons - self.statusIconBox = QtGui.QGroupBox("Connection Status") statusIconLayout = QtGui.QHBoxLayout() statusIconLayout.addWidget(self.ConnectionWidgets['disconnected']) @@ -99,31 +108,20 @@ class StatusAwareTrayIcon(object): triggered=self.cleanupAndQuit) def setConnWidget(self, icon_name): - #print 'changing icon to %s' % icon_name oldlayout = self.statusIconBox.layout() - # XXX reuse with icons - # XXX move states to StateWidget - states = {"disconnected": 0, - "connecting": 1, - "connected": 2} - for i in range(3): oldlayout.itemAt(i).widget().hide() - new = states[icon_name] + new = self.states[icon_name] oldlayout.itemAt(new).widget().show() def setIcon(self, name): - icon = self.Icons.get(name) + icon = self.Icons.get(name)(self) self.trayIcon.setIcon(icon) self.setWindowIcon(icon) def getIcon(self, icon_name): - # XXX get from connection dict - icons = {'disconnected': 0, - 'connecting': 1, - 'connected': 2} - return icons.get(icon_name, None) + return self.states.get(icon_name, None) def setIconToolTip(self): """ @@ -154,12 +152,7 @@ class StatusAwareTrayIcon(object): slot for status changes. triggers new signals for updating icon, status bar, etc. """ - - #print('STATUS CHANGED! (on Qt-land)') - #print('%s -> %s' % (status.previous, status.current)) icon_name = self.conductor.get_icon_name() self.setIcon(icon_name) - #print 'icon = ', icon_name - # change connection pixmap widget self.setConnWidget(icon_name) -- cgit v1.2.3 From 3b752fcfac7a18891e2f948acae0cb4781678647 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 4 Sep 2012 07:11:01 +0900 Subject: put timer constant instead of hardcoded value --- src/leap/baseapp/constants.py | 1 + src/leap/baseapp/eip.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 src/leap/baseapp/constants.py diff --git a/src/leap/baseapp/constants.py b/src/leap/baseapp/constants.py new file mode 100644 index 00000000..763df23b --- /dev/null +++ b/src/leap/baseapp/constants.py @@ -0,0 +1 @@ +TIMER_MILLISECONDS = 250.0 diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py index 6c3249ff..029ce0ba 100644 --- a/src/leap/baseapp/eip.py +++ b/src/leap/baseapp/eip.py @@ -3,6 +3,7 @@ import time from PyQt4 import QtCore from leap.baseapp.dialogs import ErrorDialog +from leap.baseapp import constants from leap.eip import exceptions as eip_exceptions from leap.eip.eipconnection import EIPConnection @@ -182,8 +183,7 @@ class EIPConductorApp(object): # should bring it here? # to its own class? - # XXX get constant from somewhere else - self.timer.start(250.0) + self.timer.start(constants.TIMER_MILLISECONDS) return if self.eip_service_started is True: -- cgit v1.2.3