import logging from PyQt4 import QtCore from PyQt4 import QtGui from leap import __branding as BRANDING from leap import __version__ as VERSION from leap.gui import mainwindow_rc logger = logging.getLogger(__name__) class StatusAwareTrayIconMixin(object): """ a mix of several functions needed to create a systray and make it 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() self.createActions() self.createTrayIcon() #logger.debug('showing tray icon................') self.trayIcon.show() # not sure if this really belongs here, but... self.timer = QtCore.QTimer() def createIconGroupBox(self): """ dummy icongroupbox (to be removed from here -- reference only) """ con_widgets = { 'disconnected': QtGui.QLabel(), 'connecting': QtGui.QLabel(), 'connected': QtGui.QLabel(), } con_widgets['disconnected'].setPixmap( QtGui.QPixmap( self.iconpath['disconnected'])) con_widgets['connecting'].setPixmap( QtGui.QPixmap( self.iconpath['connecting'])) con_widgets['connected'].setPixmap( QtGui.QPixmap( self.iconpath['connected'])), self.ConnectionWidgets = con_widgets self.statusIconBox = QtGui.QGroupBox("EIP 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.leapConnStatus = QtGui.QLabel("disconnected") statusIconLayout.addWidget(self.leapConnStatus) self.statusIconBox.setLayout(statusIconLayout) def createTrayIcon(self): """ creates the tray icon """ self.trayIconMenu = QtGui.QMenu(self) self.trayIconMenu.addAction(self.connAct) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.detailsAct) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.aboutAct) # we should get this hidden inside the "about" dialog # (as a little button maybe) #self.trayIconMenu.addAction(self.aboutQtAct) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QtGui.QSystemTrayIcon(self) self.setIcon('disconnected') self.trayIcon.setContextMenu(self.trayIconMenu) #self.trayIconMenu.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) #self.trayIconMenu.customContextMenuRequested.connect( #self.on_context_menu) def bad(self): logger.error('this should not be called') def createActions(self): """ creates actions to be binded to tray icon """ # XXX change action name on (dis)connect self.connAct = QtGui.QAction("Encryption ON turn &off", self, triggered=lambda: self.start_or_stopVPN()) self.detailsAct = QtGui.QAction("&Details...", self, triggered=self.detailsWin) self.aboutAct = QtGui.QAction("&About", self, triggered=self.about) self.aboutQtAct = QtGui.QAction("About Q&t", self, triggered=QtGui.qApp.aboutQt) self.quitAction = QtGui.QAction("&Quit", self, triggered=self.cleanupAndQuit) def toggleEIPAct(self): # this is too simple by now. # XXX We need to get the REAL info for Encryption state. # (now is ON as soon as vpn launched) # XXX get STATUS CONSTANTS INSTEAD icon_status = self.conductor.status.get_state_icon() if icon_status == "connected": self.connAct.setEnabled(True) self.connAct.setText('Encryption ON turn o&ff') return if icon_status == "disconnected": self.connAct.setEnabled(True) self.connAct.setText('Encryption OFF turn &on') return if icon_status == "connecting": self.connAct.setDisabled(True) self.connAct.setText('connecting...') return def detailsWin(self): visible = self.isVisible() if visible: self.hide() else: self.show() def about(self): # move to widget flavor = BRANDING.get('short_name', None) content = ("LEAP client
" "(version %s)
" % VERSION) if flavor: content = content + ('
Flavor: %s
' % flavor) content = content + ( "
" "https://leap.se") QtGui.QMessageBox.about(self, "About", content) def setConnWidget(self, icon_name): oldlayout = self.statusIconBox.layout() for i in range(3): oldlayout.itemAt(i).widget().hide() new = self.states[icon_name] oldlayout.itemAt(new).widget().show() def setIcon(self, name): icon_fun = self.Icons.get(name) if icon_fun and callable(icon_fun): icon = icon_fun(self) self.trayIcon.setIcon(icon) def getIcon(self, icon_name): return self.states.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 """ if reason in (QtGui.QSystemTrayIcon.Trigger, QtGui.QSystemTrayIcon.DoubleClick): context_menu = self.trayIcon.contextMenu() # for some reason, context_menu.show() # is failing in a way beyond my understanding. # (not working the first time it's clicked). # this works however. context_menu.exec_(self.trayIcon.geometry().center()) @QtCore.pyqtSlot() def onTimerTick(self): self.statusUpdate() @QtCore.pyqtSlot(object) def onOpenVPNStatusChange(self, status): """ updates icon, according to the openvpn status change. """ icon_name = self.conductor.get_icon_name() # XXX refactor. Use QStateMachine if icon_name in ("disconnected", "connected"): self.eipStatusChange.emit(icon_name) if icon_name in ("connecting"): # let's see how it matches leap_status_name = self.conductor.get_leap_status() self.eipStatusChange.emit(leap_status_name) self.setIcon(icon_name) # change connection pixmap widget self.setConnWidget(icon_name) @QtCore.pyqtSlot(str) def onEIPConnStatusChange(self, newstatus): """ slot for EIP status changes not to be confused with onOpenVPNStatusChange. this only updates the non-debug LEAP Status line next to the connection icon. """ # XXX move bold to style sheet self.leapConnStatus.setText( "%s" % newstatus)