class WingedBox(QWidget): def __init__(self): QWidget.__init__(self) self.setWindowTitle('WingedBox') pixmap = QPixmap(config.images['icon']) self.setWindowIcon(QIcon(pixmap)) self.user = '' self.password = '' self._vbox = QVBoxLayout(self) self._login = Login(self, HOME_WINGED_PATH) self._vbox.addWidget(self._login) self._winged = Winged(self, pref) self._winged.setVisible(False) self._vbox.addWidget(self._winged) #SystemTray Menu self._menu = QMenu('WingedBox') self._myfiles = self._menu.addAction(QIcon(config.images['icon']), 'Files') self._myfiles.setEnabled(False) self._init = self._menu.addAction(self.style().standardIcon(QStyle.SP_DialogOkButton), 'Init Session') self._close = self._menu.addAction(self.style().standardIcon(QStyle.SP_DialogCloseButton), 'Close Session') self._close.setVisible(False) self._menu.addSeparator() self._properties = self._menu.addAction('Preferences') self._menu.addSeparator() exit = self._menu.addAction(self.style().standardIcon(QStyle.SP_TitleBarCloseButton), 'Exit') #SystemTray self._tray = QSystemTrayIcon(QIcon(pixmap), self) self._tray.setToolTip('WingedBox') self._tray.setVisible(True) self._tray.setContextMenu(self._menu) #Signal -> Slot self.connect(exit, SIGNAL("triggered()"), sys.exit) self.connect(self._myfiles, SIGNAL("triggered()"), self.show) self.connect(self._init, SIGNAL("triggered()"), self.show) self.connect(self._properties, SIGNAL("triggered()"), self.show_properties) self.connect(self._close, SIGNAL("triggered()"), self.disconnect) def show_properties(self): self.prop = preferences.Preferences(self, pref) self.prop.show() def disconnect(self): self.user = '' self.password = '' self._myfiles.setEnabled(False) self._init.setVisible(True) self._close.setVisible(False) self._winged.hide() self._login.show() def closeEvent(self, event): event.ignore() self.hide()
class KerberusSystray(QWidget): def __init__(self): QWidget.__init__(self) icono = 'kerby-activo.ico' pixmap = QPixmap(icono) ##setear el nombre de la ventana self.setWindowTitle('Kerberus Control Parental') #colocar el icono cargado a la ventana self.setWindowIcon(QIcon(pixmap)) ##creamos objeto Style para hacer uso de los iconos de Qt self.style = self.style() self.filtradoHabilitado = True if not os.path.isfile('dontShowMessage'): self.mostrarMensaje = True self.noMostrarMasMensaje() else: self.mostrarMensaje = False #Menu self.menu = QMenu('Kerberus') #accion deshabilitar filtrado self.deshabilitarFiltradoAction = self.menu.addAction( self.style.standardIcon(QStyle.SP_DialogNoButton), 'Deshabilitar Filtrado' ) #accion habilitar filtrado self.habilitarFiltradoAction = self.menu.addAction( self.style.standardIcon(QStyle.SP_DialogYesButton), 'Habilitar Filtrado' ) self.habilitarFiltradoAction.setVisible(False) #cambiar password self.cambiarPasswordAction = self.menu.addAction( self.style.standardIcon(QStyle.SP_BrowserReload), 'Cambiar password de administrador' ) #accion salir self.exitAction = self.menu.addAction( self.style.standardIcon(QStyle.SP_TitleBarCloseButton), 'Salir') #SIGNAL->SLOT QObject.connect( self.exitAction, SIGNAL("triggered()"), lambda: sys.exit() ) QObject.connect( self.menu, SIGNAL("clicked()"), lambda: self.menu.popup(QCursor.pos()) ) QObject.connect( self.deshabilitarFiltradoAction, SIGNAL("triggered()"), self.deshabilitarFiltradoWindow ) QObject.connect( self.habilitarFiltradoAction, SIGNAL("triggered()"), self.habilitarFiltradoWindow ) QObject.connect( self.cambiarPasswordAction, SIGNAL("triggered()"), self.cambiarPasswordWindow ) #SystemTray #self.tray = QSystemTrayIcon(QIcon(pixmap), self) self.tray = QSystemTrayIcon(self.style.standardIcon(QStyle.SP_DialogYesButton), self) self.tray.setToolTip('Kerberus Control Parental - Activado') self.tray.setContextMenu(self.menu) self.tray.setVisible(True) QObject.connect( self.tray, SIGNAL("messageClicked()"), self.noMostrarMasMensaje ) if self.mostrarMensaje: self.tray.showMessage( u'Kerberus Control Parental', u'Filtro de Protección para menores de edad Activado', 2000 ) #def closeEvent(self, event): #event.ignore() #self.hide() def noMostrarMasMensaje(self): try: open('dontShowMessage','a').close() except IOError: print 'No se pudo crear el archivo dontShowMessage' def deshabilitarFiltradoWindow(self): webbrowser.open( 'http://inicio.kerberus.com.ar/!DeshabilitarFiltrado!', new=2 ) self.habilitarFiltradoAction.setVisible(True) self.deshabilitarFiltradoAction.setVisible(False) self.tray.setIcon(self.style.standardIcon(QStyle.SP_DialogNoButton)) self.tray.setToolTip('Kerberus Control Parental') def habilitarFiltradoWindow(self): webbrowser.open( 'http://inicio.kerberus.com.ar/!HabilitarFiltrado!', new=2 ) self.habilitarFiltradoAction.setVisible(False) self.deshabilitarFiltradoAction.setVisible(True) self.tray.setIcon(self.style.standardIcon(QStyle.SP_DialogYesButton)) self.tray.setToolTip('Kerberus Control Parental - Activado') def cambiarPasswordWindow(self): webbrowser.open( 'http://inicio.kerberus.com.ar/!CambiarPassword!', new=2 )
class MainWindow(QMainWindow): groups = dict() typeQListWidgetHeader = 1000 showHostsInGroups = False currentGroupName = None # used to simple detect currently selected group to show menu def __init__(self): super(MainWindow, self).__init__() self.config = Config() self.db = Database(self.config.getConnectionString()) cryptoKey = self.getCryptoKey() self.hosts = Hosts(self.db, cryptoKey) # menu used for each host self.hostMenu = QMenu() self.editAction = QAction(QIcon(':/ico/edit.svg'), "Edit", self.hostMenu) self.editAction.triggered.connect(self.editHost) self.hostMenu.addAction(self.editAction) # menu used for headers of groups self.groupsHeaderMenu = QMenu() self.editGroupAction = QAction(QIcon(':/ico/edit.svg'), "Edit group", self.groupsHeaderMenu) self.editGroupAction.triggered.connect(self.editGroup) self.deleteGroupAction = QAction(QIcon(':/ico/remove.svg'), "Delete group", self.groupsHeaderMenu) self.deleteGroupAction.triggered.connect(self.deleteGroup) self.groupsHeaderMenu.addAction(self.editGroupAction) self.groupsHeaderMenu.addAction(self.deleteGroupAction) self.duplicateAction = QAction(QIcon(':/ico/copy.svg'), "Duplicate", self.hostMenu) self.duplicateAction.triggered.connect(self.duplicateHost) self.hostMenu.addAction(self.duplicateAction) # todo: confirm for delete action self.deleteAction = QAction(QIcon(':/ico/remove.svg'), "Delete", self.hostMenu) self.deleteAction.triggered.connect(self.deleteHost) self.hostMenu.addAction(self.deleteAction) self.connectFramelessMenu = actions.generateScreenChoseMenu( self.hostMenu, self.connectFrameless, ':/ico/frameless.svg', "Connect frameless") self.hostMenu.addMenu(self.connectFramelessMenu) self.assignGroupAction = QAction("Assign group", self.hostMenu) self.assignGroupAction.triggered.connect(self.assignGroup) self.hostMenu.addAction(self.assignGroupAction) # setup main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # when top level changed, we changing dock title bar self.dockWidgetTileBar = DockWidgetTitleBar() self.ui.hostsDock.setTitleBarWidget(self.dockWidgetTileBar) self.ui.hostsDock.topLevelChanged.connect(self.dockLevelChanged) # set global menu self.globalMenu = QMenu() self.globalMenu.addAction(QIcon(':/ico/add.svg'), 'Add host', self.addHost) # groups menu self.groupsMenu = QMenu("Groups") self.groupsMenu.aboutToShow.connect(self.setGroupsMenu) self.globalMenu.addMenu(self.groupsMenu) # disable menu indicator self.ui.menu.setStyleSheet( "QPushButton::menu-indicator {image: none;}") self.positionMenu = QMenu("Dock position") self.positionMenu.addAction( "Left", lambda: self.setDockPosition(Qt.LeftDockWidgetArea)) self.positionMenu.addAction( "Right", lambda: self.setDockPosition(Qt.RightDockWidgetArea)) self.positionMenu.addAction("Float", self.setDockFloat) self.globalMenu.addMenu(self.positionMenu) self.globalMenu.addAction('Change tray icon visibility', self.changeTrayIconVisibility) self.globalMenu.addAction('Settings', self.showSettings) self.globalMenu.addAction('Quit', self.close) self.ui.menu.setMenu(self.globalMenu) # set events on hosts list self.ui.hostsList.itemDoubleClicked.connect(self.slotConnectHost) self.ui.hostsList.itemClicked.connect(self.slotShowHost) self.ui.hostsList.customContextMenuRequested.connect( self.slotShowHostContextMenu) # set tab widget self.tabWidget = MyTabWidget() self.setCentralWidget(self.tabWidget) self.tabWidget.setContextMenuPolicy(Qt.CustomContextMenu) self.tabWidget.customContextMenuRequested.connect( self.showCentralWidgetContextMenu) # set tray icon self.tray = QSystemTrayIcon(QIcon(":/ico/myrdp.svg")) self.tray.activated.connect(self.trayActivated) self.trayMenu = QMenu() self.trayMenu.addAction("Hide tray icon", self.changeTrayIconVisibility) self.connectHostMenuTray = ConnectHostMenu(self.hosts) self.connectHostMenuTray.triggered.connect( self.connectHostFromTrayMenu) self.trayMenu.addMenu(self.connectHostMenuTray) self.trayMenu.addAction("Quit", self.close) self.tray.setContextMenu(self.trayMenu) self.restoreSettings() # host list self.ui.filter.textChanged.connect(self.setHostList) self.setHostList() def getCryptoKey(self, passphrase=None): try: return self.config.getPrivateKey(passphrase) except ValueError: passwordDialog = PasswordDialog() retCode = passwordDialog.exec_() if retCode == QtGui.QDialog.Accepted: return self.getCryptoKey(passwordDialog.getPassword()) else: raise SystemError("Password required") def showSettings(self): settingsWidget = self.findChild(QWidget, "settings") if settingsWidget is None: self.settingsWidget = SettingsPage() self.settingsWidget.setObjectName("settings") self.tabWidget.insertTab(0, self.settingsWidget, QIcon(":/ico/settings.svg"), 'Settings') index = self.tabWidget.indexOf(self.settingsWidget) self.tabWidget.setCurrentIndex(index) def connectHostFromMenu(self, action): self.connectHost(unicode(action.text())) def connectHostFromTrayMenu(self, action): tabPage = self.connectHost(unicode(action.text())) if not self.isVisible(): self.tabWidget.setDetached(True, tabPage) def trayActivated(self, reason): if reason != QSystemTrayIcon.Trigger: return if self.isVisible(): self.hide() else: self.show() self.activateWindow() def changeTrayIconVisibility(self): if self.tray.isVisible(): self.tray.hide() if not self.isVisible(): self.show() else: self.tray.show() def refreshGroups(self): groupList = self.hosts.getGroupsList() for group in groupList: if group not in self.groups: # add new groups as visible self.groups[group] = True # remove not existing groups keysToDelete = set(self.groups.keys()) - set(groupList) for key in keysToDelete: self.groups.pop(key) def assignGroup(self): groups = self.hosts.getGroupsList() assignGroupDialog = AssignGroupDialog(groups) groupToAssign = assignGroupDialog.assign() if groupToAssign is not False: # None could be used to unassign the group groupToAssign = None if groupToAssign.isEmpty() else unicode( groupToAssign) for hostName in self.getSelectedHosts(): self.hosts.assignGroup(hostName, groupToAssign) self.db.tryCommit() self.setHostList() def setGroupsMenu(self): self.groupsMenu.clear() addGroupAction = self.groupsMenu.addAction('Add group') addGroupAction.triggered.connect(self.addGroup) deleteGroupAction = self.groupsMenu.addAction('Delete group') deleteGroupAction.triggered.connect(self.showDeleteGroupDialog) showHostsInGroupsAction = self.groupsMenu.addAction( 'Show host list in groups') showHostsInGroupsAction.triggered.connect(self.changeHostListView) showHostsInGroupsAction.setCheckable(True) showHostsInGroupsAction.setChecked(self.showHostsInGroups) self.groupsMenu.addSeparator() for group, checked in self.groups.items(): action = QAction(group, self.groupsMenu) action.setCheckable(True) action.setChecked(checked) action.triggered.connect(self.groupsVisibilityChanged) self.groupsMenu.addAction(action) def addGroup(self): groupConfigDialog = GroupConfigDialog(self.hosts.groups) resp = groupConfigDialog.add() self._processHostSubmit(resp) def groupsVisibilityChanged(self, checked): currentGroup = unicode(self.sender().text()) self.groups[currentGroup] = checked self.setHostList() def setDockPosition(self, dockWidgetArea): if self.ui.hostsDock.isFloating(): self.ui.hostsDock.setFloating(False) self.addDockWidget(dockWidgetArea, self.ui.hostsDock) def setDockFloat(self): if self.ui.hostsDock.isFloating(): return # default title bar must be set before is float because sometimes window make strange crash self.ui.hostsDock.setTitleBarWidget(None) self.ui.hostsDock.setFloating(True) def dockLevelChanged(self, isFloating): if isFloating: # changing title bar widget if is not none, probably true will be only once on start with saved float state if self.ui.hostsDock.titleBarWidget(): self.ui.hostsDock.setTitleBarWidget(None) else: self.ui.hostsDock.setTitleBarWidget(self.dockWidgetTileBar) def showFramelessWidget(self): self.t.show() self.t.setGeometry(self.frameGeometry()) def getCurrentHostListItemName(self): return self.ui.hostsList.currentItem().text() def getSelectedHosts(self): return [host.text() for host in self.ui.hostsList.selectedItems()] def findHostItemByName(self, name): result = self.ui.hostsList.findItems(name, Qt.MatchExactly) resultLen = len(result) if resultLen != 1: # should be only one host logger.error("Host not found. Got %d results" % resultLen) return result[0] def showCentralWidgetContextMenu(self, pos): menu = QMenu() title = self.ui.hostsDock.windowTitle() hostsDockAction = menu.addAction(title) hostsDockAction.setCheckable(True) hostsDockAction.setChecked(self.ui.hostsDock.isVisible()) hostsDockAction.triggered.connect(self.changeHostsDockWidgetVisibility) hostsDockAction = menu.addAction("Tray icon") hostsDockAction.setCheckable(True) hostsDockAction.setChecked(self.tray.isVisible()) hostsDockAction.triggered.connect(self.changeTrayIconVisibility) connectHostMenuTray = ConnectHostMenu(self.hosts, "Connect") connectHostMenuTray.triggered.connect(self.connectHostFromMenu) menu.addMenu(connectHostMenuTray) menu.exec_(self.tabWidget.mapToGlobal(pos)) def changeHostListView(self, checked): self.showHostsInGroups = checked self.setHostList() def changeHostsDockWidgetVisibility(self): isVisible = self.ui.hostsDock.isVisible() self.ui.hostsDock.setVisible(not isVisible) def isHostListHeader(self, item): if not item or item.type() == self.typeQListWidgetHeader: return True return False def slotShowHostContextMenu(self, pos): def changeMenusVisibility(isEnabled): self.connectFramelessMenu.setEnabled(isEnabled) self.editAction.setEnabled(isEnabled) self.duplicateAction.setEnabled(isEnabled) # ignore context menu for group headers item = self.ui.hostsList.itemAt(pos) if self.isHostListHeader(item): item = self.ui.hostsList.itemAt(pos) widgetItem = self.ui.hostsList.itemWidget(item) if widgetItem: self.currentGroupName = widgetItem.text() # yea I'm so dirty if self.currentGroupName != unassignedGroupName: self.groupsHeaderMenu.exec_( self.ui.hostsList.mapToGlobal(pos)) return if len(self.ui.hostsList.selectedItems()) == 1: # single menu changeMenusVisibility(True) else: changeMenusVisibility(False) self.hostMenu.exec_(self.ui.hostsList.mapToGlobal(pos)) def _processHostSubmit(self, resp): if resp["code"]: self.setHostList() hostName = resp.get("name") if hostName: hostItem = self.findHostItemByName(hostName) self.slotConnectHost(hostItem) def addHost(self): hostDialog = HostConfigDialog(self.hosts) self._processHostSubmit(hostDialog.add()) def editHost(self): hostDialog = HostConfigDialog(self.hosts) resp = hostDialog.edit(self.getCurrentHostListItemName()) self._processHostSubmit(resp) def editGroup(self): groupConfigDialog = GroupConfigDialog(self.hosts.groups) resp = groupConfigDialog.edit(self.currentGroupName) self._processHostSubmit(resp) def deleteGroup(self): retCode = self.showOkCancelMessageBox( "Do you want to remove selected group? All assigned hosts " "to this group will be unassigned.", "Confirmation") if retCode == QMessageBox.Cancel: return self.hosts.deleteGroup(self.currentGroupName) self.setHostList() def showDeleteGroupDialog(self): deleteGroupDialog = DeleteGroupDialog(self.hosts) deleteGroupDialog.deleteGroup() self.setHostList() def duplicateHost(self): hostDialog = HostConfigDialog(self.hosts) resp = hostDialog.duplicate(self.getCurrentHostListItemName()) self._processHostSubmit(resp) def deleteHost(self): retCode = self.showOkCancelMessageBox( "Do you want to remove selected hosts?", "Confirmation") if retCode == QMessageBox.Cancel: return for host in self.getSelectedHosts(): self.hosts.delete(host) self.setHostList() def connectFrameless(self, screenIndex=None): self.connectHost(self.getCurrentHostListItemName(), frameless=True, screenIndex=screenIndex) # Fix to release keyboard from QX11EmbedContainer, when we leave widget through wm border def leaveEvent(self, event): keyG = QWidget.keyboardGrabber() if keyG is not None: keyG.releaseKeyboard() event.accept() # needed? def setHostList(self): """ set hosts list in list view """ self.ui.hostsList.clear() self.refreshGroups() hostFilter = self.ui.filter.text() if self.showHostsInGroups: self.showHostListInGroups(hostFilter) else: self.showHostList(hostFilter) def showHostList(self, hostFilter): groupFilter = [ group for group, visibility in self.groups.items() if visibility ] hosts = self.hosts.getHostsListByHostNameAndGroup( hostFilter, groupFilter) self.ui.hostsList.addItems(hosts) def showHostListInGroups(self, hostFilter): hosts = self.hosts.getGroupedHostNames(hostFilter) for group, hostsList in hosts.items(): if self.groups.get(group, True): if group is None: group = unassignedGroupName groupHeader = QtGui.QListWidgetItem( type=self.typeQListWidgetHeader) groupLabel = QtGui.QLabel(unicode(group)) groupLabel.setProperty('class', 'group-title') self.ui.hostsList.addItem(groupHeader) self.ui.hostsList.setItemWidget(groupHeader, groupLabel) self.ui.hostsList.addItems(hostsList) def slotShowHost(self, item): # on one click we activating tab and showing options self.tabWidget.activateTab(item) def slotConnectHost(self, item): if self.isHostListHeader(item): return self.connectHost(unicode(item.text())) def connectHost(self, hostId, frameless=False, screenIndex=None): hostId = unicode(hostId) # sometimes hostId comes as QString tabPage = self.tabWidget.createTab(hostId) tabPage.reconnectionNeeded.connect(self.connectHost) if frameless: self.tabWidget.detachFrameless(tabPage, screenIndex) try: execCmd, opts = self.getCmd(tabPage, hostId) except LookupError: logger.error(u"Host {} not found.".format(hostId)) return ProcessManager.start(hostId, tabPage, execCmd, opts) return tabPage def getCmd(self, tabPage, hostName): host = self.hosts.get(hostName) # set tabPage widget width, height = tabPage.setSizeAndGetCurrent() # 1et widget winId to embed rdesktop winId = tabPage.x11.winId() # set remote desktop client, at this time works only with freerdp remoteClientType, remoteClientOptions = self.config.getRdpClient() remoteClient = ClientFactory(remoteClientType, **remoteClientOptions) remoteClient.setWindowParameters(winId, width, height) remoteClient.setUserAndPassword(host.user, host.password) remoteClient.setAddress(host.address) return remoteClient.getComposedCommand() def saveSettings(self): self.config.setValue("geometry", self.saveGeometry()) self.config.setValue("windowState", self.saveState()) self.config.setValue('trayIconVisibility', self.tray.isVisible()) self.config.setValue('mainWindowVisibility', self.isVisible()) self.config.setValue('groups', self.groups) self.config.setValue('showHostsInGroups', self.showHostsInGroups) def restoreSettings(self): try: self.restoreGeometry( self.config.getValue("geometry").toByteArray()) self.restoreState( self.config.getValue("windowState").toByteArray()) except Exception: logger.debug("No settings to restore") # restore tray icon state trayIconVisibility = self.config.getValue('trayIconVisibility', "true").toBool() self.tray.setVisible(trayIconVisibility) self.showHostsInGroups = self.config.getValue('showHostsInGroups', 'false').toBool() if self.tray.isVisible(): mainWindowVisibility = self.config.getValue( 'mainWindowVisibility', "true").toBool() self.setVisible(mainWindowVisibility) else: # it tray icon is not visible, always show main window self.show() self.groups = { unicode(k): v for k, v in self.config.getValue('groups', {}).toPyObject().items() } def closeEvent(self, event): if not ProcessManager.hasActiveProcess: self.saveSettings() QCoreApplication.exit() return ret = self.showOkCancelMessageBox("Are you sure do you want to quit?", "Exit confirmation") if ret == QMessageBox.Cancel: event.ignore() return self.saveSettings() ProcessManager.killemall() event.accept() QCoreApplication.exit() def showOkCancelMessageBox(self, messageBoxText, windowTitle): msgBox = QMessageBox(self, text=messageBoxText) msgBox.setWindowTitle(windowTitle) msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msgBox.setIcon(QMessageBox.Question) return msgBox.exec_()
class MainWindow(QMainWindow): groups = dict() typeQListWidgetHeader = 1000 showHostsInGroups = False currentGroupName = None # used to simple detect currently selected group to show menu def __init__(self): super(MainWindow, self).__init__() self.config = Config() self.db = Database(self.config.getConnectionString()) cryptoKey = self.getCryptoKey() self.hosts = Hosts(self.db, cryptoKey) # menu used for each host self.hostMenu = QMenu() self.editAction = QAction(QIcon(':/ico/edit.svg'), "Edit", self.hostMenu) self.editAction.triggered.connect(self.editHost) self.hostMenu.addAction(self.editAction) # menu used for headers of groups self.groupsHeaderMenu = QMenu() self.editGroupAction = QAction(QIcon(':/ico/edit.svg'), "Edit group", self.groupsHeaderMenu) self.editGroupAction.triggered.connect(self.editGroup) self.deleteGroupAction = QAction(QIcon(':/ico/remove.svg'), "Delete group", self.groupsHeaderMenu) self.deleteGroupAction.triggered.connect(self.deleteGroup) self.groupsHeaderMenu.addAction(self.editGroupAction) self.groupsHeaderMenu.addAction(self.deleteGroupAction) self.duplicateAction = QAction(QIcon(':/ico/copy.svg'), "Duplicate", self.hostMenu) self.duplicateAction.triggered.connect(self.duplicateHost) self.hostMenu.addAction(self.duplicateAction) # todo: confirm for delete action self.deleteAction = QAction(QIcon(':/ico/remove.svg'), "Delete", self.hostMenu) self.deleteAction.triggered.connect(self.deleteHost) self.hostMenu.addAction(self.deleteAction) self.connectFramelessMenu = actions.generateScreenChoseMenu(self.hostMenu, self.connectFrameless, ':/ico/frameless.svg', "Connect frameless") self.hostMenu.addMenu(self.connectFramelessMenu) self.assignGroupAction = QAction("Assign group", self.hostMenu) self.assignGroupAction.triggered.connect(self.assignGroup) self.hostMenu.addAction(self.assignGroupAction) # setup main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # when top level changed, we changing dock title bar self.dockWidgetTileBar = DockWidgetTitleBar() self.ui.hostsDock.setTitleBarWidget(self.dockWidgetTileBar) self.ui.hostsDock.topLevelChanged.connect(self.dockLevelChanged) # set global menu self.globalMenu = QMenu() self.globalMenu.addAction(QIcon(':/ico/add.svg'), 'Add host', self.addHost) # groups menu self.groupsMenu = QMenu("Groups") self.groupsMenu.aboutToShow.connect(self.setGroupsMenu) self.globalMenu.addMenu(self.groupsMenu) # disable menu indicator self.ui.menu.setStyleSheet("QPushButton::menu-indicator {image: none;}") self.positionMenu = QMenu("Dock position") self.positionMenu.addAction("Left", lambda: self.setDockPosition(Qt.LeftDockWidgetArea)) self.positionMenu.addAction("Right", lambda: self.setDockPosition(Qt.RightDockWidgetArea)) self.positionMenu.addAction("Float", self.setDockFloat) self.globalMenu.addMenu(self.positionMenu) self.globalMenu.addAction('Change tray icon visibility', self.changeTrayIconVisibility) self.globalMenu.addAction('Settings', self.showSettings) self.globalMenu.addAction('Quit', self.close) self.ui.menu.setMenu(self.globalMenu) # set events on hosts list self.ui.hostsList.itemDoubleClicked.connect(self.slotConnectHost) self.ui.hostsList.itemClicked.connect(self.slotShowHost) self.ui.hostsList.customContextMenuRequested.connect(self.slotShowHostContextMenu) # set tab widget self.tabWidget = MyTabWidget() self.setCentralWidget(self.tabWidget) self.tabWidget.setContextMenuPolicy(Qt.CustomContextMenu) self.tabWidget.customContextMenuRequested.connect(self.showCentralWidgetContextMenu) # set tray icon self.tray = QSystemTrayIcon(QIcon(":/ico/myrdp.svg")) self.tray.activated.connect(self.trayActivated) self.trayMenu = QMenu() self.trayMenu.addAction("Hide tray icon", self.changeTrayIconVisibility) self.connectHostMenuTray = ConnectHostMenu(self.hosts) self.connectHostMenuTray.triggered.connect(self.connectHostFromTrayMenu) self.trayMenu.addMenu(self.connectHostMenuTray) self.trayMenu.addAction("Quit", self.close) self.tray.setContextMenu(self.trayMenu) self.restoreSettings() # host list self.ui.filter.textChanged.connect(self.setHostList) self.setHostList() def getCryptoKey(self, passphrase=None): try: return self.config.getPrivateKey(passphrase) except ValueError: passwordDialog = PasswordDialog() retCode = passwordDialog.exec_() if retCode == QtGui.QDialog.Accepted: return self.getCryptoKey(passwordDialog.getPassword()) else: raise SystemError("Password required") def showSettings(self): settingsWidget = self.findChild(QWidget, "settings") if settingsWidget is None: self.settingsWidget = SettingsPage() self.settingsWidget.setObjectName("settings") self.tabWidget.insertTab(0, self.settingsWidget, QIcon(":/ico/settings.svg"), 'Settings') index = self.tabWidget.indexOf(self.settingsWidget) self.tabWidget.setCurrentIndex(index) def connectHostFromMenu(self, action): self.connectHost(unicode(action.text())) def connectHostFromTrayMenu(self, action): tabPage = self.connectHost(unicode(action.text())) if not self.isVisible(): self.tabWidget.setDetached(True, tabPage) def trayActivated(self, reason): if reason != QSystemTrayIcon.Trigger: return if self.isVisible(): self.hide() else: self.show() self.activateWindow() def changeTrayIconVisibility(self): if self.tray.isVisible(): self.tray.hide() if not self.isVisible(): self.show() else: self.tray.show() def refreshGroups(self): groupList = self.hosts.getGroupsList() for group in groupList: if group not in self.groups: # add new groups as visible self.groups[group] = True # remove not existing groups keysToDelete = set(self.groups.keys()) - set(groupList) for key in keysToDelete: self.groups.pop(key) def assignGroup(self): groups = self.hosts.getGroupsList() assignGroupDialog = AssignGroupDialog(groups) groupToAssign = assignGroupDialog.assign() if groupToAssign is not False: # None could be used to unassign the group groupToAssign = None if groupToAssign.isEmpty() else unicode(groupToAssign) for hostName in self.getSelectedHosts(): self.hosts.assignGroup(hostName, groupToAssign) self.db.tryCommit() self.setHostList() def setGroupsMenu(self): self.groupsMenu.clear() addGroupAction = self.groupsMenu.addAction('Add group') addGroupAction.triggered.connect(self.addGroup) deleteGroupAction = self.groupsMenu.addAction('Delete group') deleteGroupAction.triggered.connect(self.showDeleteGroupDialog) showHostsInGroupsAction = self.groupsMenu.addAction('Show host list in groups') showHostsInGroupsAction.triggered.connect(self.changeHostListView) showHostsInGroupsAction.setCheckable(True) showHostsInGroupsAction.setChecked(self.showHostsInGroups) self.groupsMenu.addSeparator() for group, checked in self.groups.items(): action = QAction(group, self.groupsMenu) action.setCheckable(True) action.setChecked(checked) action.triggered.connect(self.groupsVisibilityChanged) self.groupsMenu.addAction(action) def addGroup(self): groupConfigDialog = GroupConfigDialog(self.hosts.groups) resp = groupConfigDialog.add() self._processHostSubmit(resp) def groupsVisibilityChanged(self, checked): currentGroup = unicode(self.sender().text()) self.groups[currentGroup] = checked self.setHostList() def setDockPosition(self, dockWidgetArea): if self.ui.hostsDock.isFloating(): self.ui.hostsDock.setFloating(False) self.addDockWidget(dockWidgetArea, self.ui.hostsDock) def setDockFloat(self): if self.ui.hostsDock.isFloating(): return # default title bar must be set before is float because sometimes window make strange crash self.ui.hostsDock.setTitleBarWidget(None) self.ui.hostsDock.setFloating(True) def dockLevelChanged(self, isFloating): if isFloating: # changing title bar widget if is not none, probably true will be only once on start with saved float state if self.ui.hostsDock.titleBarWidget(): self.ui.hostsDock.setTitleBarWidget(None) else: self.ui.hostsDock.setTitleBarWidget(self.dockWidgetTileBar) def showFramelessWidget(self): self.t.show() self.t.setGeometry(self.frameGeometry()) def getCurrentHostListItemName(self): return self.ui.hostsList.currentItem().text() def getSelectedHosts(self): return [host.text() for host in self.ui.hostsList.selectedItems()] def findHostItemByName(self, name): result = self.ui.hostsList.findItems(name, Qt.MatchExactly) resultLen = len(result) if resultLen != 1: # should be only one host logger.error("Host not found. Got %d results" % resultLen) return result[0] def showCentralWidgetContextMenu(self, pos): menu = QMenu() title = self.ui.hostsDock.windowTitle() hostsDockAction = menu.addAction(title) hostsDockAction.setCheckable(True) hostsDockAction.setChecked(self.ui.hostsDock.isVisible()) hostsDockAction.triggered.connect(self.changeHostsDockWidgetVisibility) hostsDockAction = menu.addAction("Tray icon") hostsDockAction.setCheckable(True) hostsDockAction.setChecked(self.tray.isVisible()) hostsDockAction.triggered.connect(self.changeTrayIconVisibility) connectHostMenuTray = ConnectHostMenu(self.hosts, "Connect") connectHostMenuTray.triggered.connect(self.connectHostFromMenu) menu.addMenu(connectHostMenuTray) menu.exec_(self.tabWidget.mapToGlobal(pos)) def changeHostListView(self, checked): self.showHostsInGroups = checked self.setHostList() def changeHostsDockWidgetVisibility(self): isVisible = self.ui.hostsDock.isVisible() self.ui.hostsDock.setVisible(not isVisible) def isHostListHeader(self, item): if not item or item.type() == self.typeQListWidgetHeader: return True return False def slotShowHostContextMenu(self, pos): def changeMenusVisibility(isEnabled): self.connectFramelessMenu.setEnabled(isEnabled) self.editAction.setEnabled(isEnabled) self.duplicateAction.setEnabled(isEnabled) # ignore context menu for group headers item = self.ui.hostsList.itemAt(pos) if self.isHostListHeader(item): item = self.ui.hostsList.itemAt(pos) widgetItem = self.ui.hostsList.itemWidget(item) if widgetItem: self.currentGroupName = widgetItem.text() # yea I'm so dirty if self.currentGroupName != unassignedGroupName: self.groupsHeaderMenu.exec_(self.ui.hostsList.mapToGlobal(pos)) return if len(self.ui.hostsList.selectedItems()) == 1: # single menu changeMenusVisibility(True) else: changeMenusVisibility(False) self.hostMenu.exec_(self.ui.hostsList.mapToGlobal(pos)) def _processHostSubmit(self, resp): if resp["code"]: self.setHostList() hostName = resp.get("name") if hostName: hostItem = self.findHostItemByName(hostName) self.slotConnectHost(hostItem) def addHost(self): hostDialog = HostConfigDialog(self.hosts) self._processHostSubmit(hostDialog.add()) def editHost(self): hostDialog = HostConfigDialog(self.hosts) resp = hostDialog.edit(self.getCurrentHostListItemName()) self._processHostSubmit(resp) def editGroup(self): groupConfigDialog = GroupConfigDialog(self.hosts.groups) resp = groupConfigDialog.edit(self.currentGroupName) self._processHostSubmit(resp) def deleteGroup(self): retCode = self.showOkCancelMessageBox("Do you want to remove selected group? All assigned hosts " "to this group will be unassigned.", "Confirmation") if retCode == QMessageBox.Cancel: return self.hosts.deleteGroup(self.currentGroupName) self.setHostList() def showDeleteGroupDialog(self): deleteGroupDialog = DeleteGroupDialog(self.hosts) deleteGroupDialog.deleteGroup() self.setHostList() def duplicateHost(self): hostDialog = HostConfigDialog(self.hosts) resp = hostDialog.duplicate(self.getCurrentHostListItemName()) self._processHostSubmit(resp) def deleteHost(self): retCode = self.showOkCancelMessageBox("Do you want to remove selected hosts?", "Confirmation") if retCode == QMessageBox.Cancel: return for host in self.getSelectedHosts(): self.hosts.delete(host) self.setHostList() def connectFrameless(self, screenIndex=None): self.connectHost(self.getCurrentHostListItemName(), frameless=True, screenIndex=screenIndex) # Fix to release keyboard from QX11EmbedContainer, when we leave widget through wm border def leaveEvent(self, event): keyG = QWidget.keyboardGrabber() if keyG is not None: keyG.releaseKeyboard() event.accept() # needed? def setHostList(self): """ set hosts list in list view """ self.ui.hostsList.clear() self.refreshGroups() hostFilter = self.ui.filter.text() if self.showHostsInGroups: self.showHostListInGroups(hostFilter) else: self.showHostList(hostFilter) def showHostList(self, hostFilter): groupFilter = [group for group, visibility in self.groups.items() if visibility] hosts = self.hosts.getHostsListByHostNameAndGroup(hostFilter, groupFilter) self.ui.hostsList.addItems(hosts) def showHostListInGroups(self, hostFilter): hosts = self.hosts.getGroupedHostNames(hostFilter) for group, hostsList in hosts.items(): if self.groups.get(group, True): if group is None: group = unassignedGroupName groupHeader = QtGui.QListWidgetItem(type=self.typeQListWidgetHeader) groupLabel = QtGui.QLabel(unicode(group)) groupLabel.setProperty('class', 'group-title') self.ui.hostsList.addItem(groupHeader) self.ui.hostsList.setItemWidget(groupHeader, groupLabel) self.ui.hostsList.addItems(hostsList) def slotShowHost(self, item): # on one click we activating tab and showing options self.tabWidget.activateTab(item) def slotConnectHost(self, item): if self.isHostListHeader(item): return self.connectHost(unicode(item.text())) def connectHost(self, hostId, frameless=False, screenIndex=None): hostId = unicode(hostId) # sometimes hostId comes as QString tabPage = self.tabWidget.createTab(hostId) tabPage.reconnectionNeeded.connect(self.connectHost) if frameless: self.tabWidget.detachFrameless(tabPage, screenIndex) try: execCmd, opts = self.getCmd(tabPage, hostId) except LookupError: logger.error(u"Host {} not found.".format(hostId)) return ProcessManager.start(hostId, tabPage, execCmd, opts) return tabPage def getCmd(self, tabPage, hostName): host = self.hosts.get(hostName) # set tabPage widget width, height = tabPage.setSizeAndGetCurrent() # 1et widget winId to embed rdesktop winId = tabPage.x11.winId() # set remote desktop client, at this time works only with freerdp remoteClientType, remoteClientOptions = self.config.getRdpClient() remoteClient = ClientFactory(remoteClientType, **remoteClientOptions) remoteClient.setWindowParameters(winId, width, height) remoteClient.setUserAndPassword(host.user, host.password) remoteClient.setAddress(host.address) return remoteClient.getComposedCommand() def saveSettings(self): self.config.setValue("geometry", self.saveGeometry()) self.config.setValue("windowState", self.saveState()) self.config.setValue('trayIconVisibility', self.tray.isVisible()) self.config.setValue('mainWindowVisibility', self.isVisible()) self.config.setValue('groups', self.groups) self.config.setValue('showHostsInGroups', self.showHostsInGroups) def restoreSettings(self): try: self.restoreGeometry(self.config.getValue("geometry").toByteArray()) self.restoreState(self.config.getValue("windowState").toByteArray()) except Exception: logger.debug("No settings to restore") # restore tray icon state trayIconVisibility = self.config.getValue('trayIconVisibility', "true").toBool() self.tray.setVisible(trayIconVisibility) self.showHostsInGroups = self.config.getValue('showHostsInGroups', 'false').toBool() if self.tray.isVisible(): mainWindowVisibility = self.config.getValue('mainWindowVisibility', "true").toBool() self.setVisible(mainWindowVisibility) else: # it tray icon is not visible, always show main window self.show() self.groups = {unicode(k): v for k, v in self.config.getValue('groups', {}).toPyObject().items()} def closeEvent(self, event): if not ProcessManager.hasActiveProcess: self.saveSettings() QCoreApplication.exit() return ret = self.showOkCancelMessageBox("Are you sure do you want to quit?", "Exit confirmation") if ret == QMessageBox.Cancel: event.ignore() return self.saveSettings() ProcessManager.killemall() event.accept() QCoreApplication.exit() def showOkCancelMessageBox(self, messageBoxText, windowTitle): msgBox = QMessageBox(self, text=messageBoxText) msgBox.setWindowTitle(windowTitle) msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msgBox.setIcon(QMessageBox.Question) return msgBox.exec_()
class MainWindow(QMainWindow): def __init__(self, config): super(MainWindow, self).__init__() self.config = Config(config) db = Database(self.config.getConnectionString()) db.create() self.hosts = Hosts(db) # menu used for each host self.hostMenu = QMenu() self.hostMenu.addAction(QIcon(":/ico/edit.svg"), "Edit", self.editHost) self.hostMenu.addAction(QIcon(":/ico/remove.svg"), "Delete", self.deleteHost) actions.addActionWithScreenChose( self.hostMenu, self.connectFrameless, ":/ico/frameless.svg", "Connect frameless" ) # setup main window self.ui = Ui_MainWindow() self.ui.setupUi(self) # when top level changed, we changing dock title bar self.dockWidgetTileBar = DockWidgetTitleBar() self.ui.hostsDock.setTitleBarWidget(self.dockWidgetTileBar) self.ui.hostsDock.topLevelChanged.connect(self.dockLevelChanged) # set global menu self.globalMenu = QMenu() self.globalMenu.addAction(QIcon(":/ico/add.svg"), "Add host", self.addHost) # disable menu indicator self.ui.menu.setStyleSheet("QPushButton::menu-indicator {image: none;}") self.positionMenu = QMenu("Dock position") self.positionMenu.addAction("Left", lambda: self.setDockPosition(Qt.LeftDockWidgetArea)) self.positionMenu.addAction("Right", lambda: self.setDockPosition(Qt.RightDockWidgetArea)) self.positionMenu.addAction("Float", self.setDockFloat) self.globalMenu.addMenu(self.positionMenu) self.globalMenu.addAction("Change tray icon visibility", self.changeTrayIconVisibility) self.globalMenu.addAction("Quit", self.close) self.ui.menu.setMenu(self.globalMenu) # set events on hosts list self.ui.hostsList.itemDoubleClicked.connect(self.slotConnectHost) self.ui.hostsList.itemClicked.connect(self.slotShowHost) self.ui.hostsList.customContextMenuRequested.connect(self.slotShowHostContextMenu) # set tab widget self.tabWidget = MyTabWidget() self.setCentralWidget(self.tabWidget) # set tray icon self.tray = QSystemTrayIcon(QIcon(":/ico/myrdp.svg")) self.tray.activated.connect(self.trayActivated) self.trayMenu = QMenu() self.trayMenu.addAction("Hide tray icon", self.changeTrayIconVisibility) self.trayMenu.addAction("Quit", self.close) self.tray.setContextMenu(self.trayMenu) # host list self.ui.filter.textChanged.connect(self.setHostList) self.setHostList() self.restoreSettings() def trayActivated(self, reason): if reason != QSystemTrayIcon.Trigger: return if self.isVisible(): self.hide() else: self.show() self.activateWindow() def changeTrayIconVisibility(self): if self.tray.isVisible(): self.tray.hide() if not self.isVisible(): self.show() else: self.tray.show() def setDockPosition(self, dockWidgetArea): if self.ui.hostsDock.isFloating(): self.ui.hostsDock.setFloating(False) self.addDockWidget(dockWidgetArea, self.ui.hostsDock) def setDockFloat(self): if self.ui.hostsDock.isFloating(): return # default title bar must be set before is float because sometimes window make strange crash self.ui.hostsDock.setTitleBarWidget(None) self.ui.hostsDock.setFloating(True) def dockLevelChanged(self, isFloating): if isFloating: # changing title bar widget if is not none, probably true will be only once on start with saved float state if self.ui.hostsDock.titleBarWidget(): self.ui.hostsDock.setTitleBarWidget(None) else: self.ui.hostsDock.setTitleBarWidget(self.dockWidgetTileBar) def showFramelessWidget(self): self.t.show() self.t.setGeometry(self.frameGeometry()) def getCurrentHostListItemName(self): return self.ui.hostsList.currentItem().text() def findHostItemByName(self, name): result = self.ui.hostsList.findItems(name, Qt.MatchExactly) resultLen = len(result) if resultLen != 1: # should be only one host logging.error("Host not found. Got %d results" % resultLen) return result[0] def slotShowHostContextMenu(self, pos): """ slot needed to show menu in proper position, or i'm doing something wrong """ self.hostMenu.exec_(self.ui.hostsList.mapToGlobal(pos)) def addHost(self): hostDialog = HostConfigDialog(self.hosts) resp = hostDialog.add() if resp["code"]: self.setHostList() hostName = resp.get("name") if hostName: hostItem = self.findHostItemByName(hostName) self.slotConnectHost(hostItem) def editHost(self): hostDialog = HostConfigDialog(self.hosts) resp = hostDialog.edit(self.getCurrentHostListItemName()) if resp["code"]: self.setHostList() def deleteHost(self): self.hosts.delete(self.getCurrentHostListItemName()) self.setHostList() def connectFrameless(self, screenIndex=None): self.connectHost(self.getCurrentHostListItemName(), frameless=True, screenIndex=screenIndex) # Fix to release keyboard from QX11EmbedContainer, when we leave widget through wm border def leaveEvent(self, event): keyG = QWidget.keyboardGrabber() if keyG is not None: keyG.releaseKeyboard() event.accept() # needed? def setHostList(self): """ set hosts list in list view """ self.ui.hostsList.clear() self.ui.hostsList.addItems(self.hosts.getFilteredHostsNames(self.ui.filter.text())) def slotShowHost(self, item): # on one click we activating tab and showing options self.tabWidget.activateTab(item) def slotConnectHost(self, item): self.connectHost(unicode(item.text())) def connectHost(self, hostId, frameless=False, screenIndex=None): hostId = unicode(hostId) # sometimes hostId comes as QString tabPage = self.tabWidget.createTab(hostId) tabPage.reconnectionNeeded.connect(self.connectHost) if frameless: self.tabWidget.detachFrameless(tabPage, screenIndex) execCmd, opts = self.getCmd(tabPage, hostId) ProcessManager.start(hostId, tabPage, execCmd, opts) def getCmd(self, tabPage, hostName): host = self.hosts.get(hostName) # set tabPage widget width, height = tabPage.setSizeAndGetCurrent() # 1et widget winId to embed rdesktop winId = tabPage.x11.winId() # set remote desktop client, at this time works only with freerdp remoteClientType, remoteClientOptions = self.config.getRdpClient() remoteClient = ClientFactory(remoteClientType, **remoteClientOptions) remoteClient.setWindowParameters(winId, width, height) remoteClient.setUserAndPassword(host.user, host.password) remoteClient.setAddress(host.address) return remoteClient.getComposedCommand() def saveSettings(self): settings = QSettings("MyRDP") settings.setValue("geometry", self.saveGeometry()) settings.setValue("windowState", self.saveState()) settings.setValue("trayIconVisibility", self.tray.isVisible()) def restoreSettings(self): settings = QSettings("MyRDP") try: self.restoreGeometry(settings.value("geometry").toByteArray()) self.restoreState(settings.value("windowState").toByteArray()) except Exception: logging.debug("No settings to restore") # restore tray icon state trayIconVisibility = settings.value("trayIconVisibility").toBool() self.tray.setVisible(trayIconVisibility) def closeEvent(self, event): if not ProcessManager.hasActiveProcess: self.saveSettings() return msgBox = QMessageBox(self, text="Are you sure do you want to quit?") msgBox.setWindowTitle("Exit confirmation") msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) ret = msgBox.exec_() if ret == QMessageBox.Cancel: event.ignore() return self.saveSettings() ProcessManager.killemall() event.accept()
class QChatWindow(QMainWindow): newClientSignal = pyqtSignal(str) clientReadySignal = pyqtSignal(str) smpRequestSignal = pyqtSignal(int, str, str, int) handleErrorSignal = pyqtSignal(str, int) sendMessageToTabSignal = pyqtSignal(str, str, str) def __init__(self, restartCallback, connectionManager=None, messageQueue=None): QMainWindow.__init__(self) self.restartCallback = restartCallback self.connectionManager = connectionManager self.messageQueue = messageQueue self.newClientSignal.connect(self.newClientSlot) self.clientReadySignal.connect(self.clientReadySlot) self.smpRequestSignal.connect(self.smpRequestSlot) self.handleErrorSignal.connect(self.handleErrorSlot) self.sendMessageToTabSignal.connect(self.sendMessageToTab) self.chatTabs = QTabWidget(self) self.chatTabs.setTabsClosable(True) self.chatTabs.setMovable(True) self.chatTabs.tabCloseRequested.connect(self.closeTab) self.chatTabs.currentChanged.connect(self.tabChanged) self.statusBar = self.statusBar() self.systemTrayIcon = QSystemTrayIcon(self) self.systemTrayIcon.setVisible(True) self.__setMenubar() vbox = QVBoxLayout() vbox.addWidget(self.chatTabs) # Add the completeted layout to the window self.centralWidget = QWidget() self.centralWidget.setLayout(vbox) self.setCentralWidget(self.centralWidget) qtUtils.resizeWindow(self, 700, 400) qtUtils.centerWindow(self) # Title and icon self.setWindowTitle("Cryptully") self.setWindowIcon(QIcon(qtUtils.getAbsoluteImagePath('icon.png'))) def connectedToServer(self): # Add an initial tab once connected to the server self.addNewTab() def newClient(self, nick): # This function is called from a bg thread. Send a signal to get on the UI thread self.newClientSignal.emit(nick) @pyqtSlot(str) def newClientSlot(self, nick): nick = str(nick) # Show a system notifcation of the new client if not the current window if not self.isActiveWindow(): qtUtils.showDesktopNotification(self.systemTrayIcon, "Chat request from %s" % nick, '') # Show the accept dialog accept = QAcceptDialog.getAnswer(self, nick) if not accept: self.connectionManager.newClientRejected(nick) return # If nick already has a tab, reuse it if self.isNickInTabs(nick): self.getTabByNick(nick)[0].enable() else: self.addNewTab(nick) self.connectionManager.newClientAccepted(nick) def addNewTab(self, nick=None): newTab = QChatTab(self, nick) self.chatTabs.addTab(newTab, nick if nick is not None else "New Chat") self.chatTabs.setCurrentWidget(newTab) newTab.setFocus() def clientReady(self, nick): # Use a signal to call the client ready slot on the UI thread since # this function is called from a background thread self.clientReadySignal.emit(nick) @pyqtSlot(str) def clientReadySlot(self, nick): nick = str(nick) tab, tabIndex = self.getTabByNick(nick) self.chatTabs.setTabText(tabIndex, nick) tab.showNowChattingMessage() # Set the window title if the tab is the selected tab if tabIndex == self.chatTabs.currentIndex(): self.setWindowTitle(nick) def smpRequest(self, type, nick, question='', errno=0): self.smpRequestSignal.emit(type, nick, question, errno) @pyqtSlot(int, str, str, int) def smpRequestSlot(self, type, nick, question='', errno=0): if type == constants.SMP_CALLBACK_REQUEST: answer, clickedButton = QSMPRespondDialog.getAnswer(nick, question) if clickedButton == constants.BUTTON_OKAY: self.connectionManager.respondSMP(str(nick), str(answer)) elif type == constants.SMP_CALLBACK_COMPLETE: QMessageBox.information(self, "%s Authenticated" % nick, "Your chat session with %s has been succesfully authenticated. The conversation is verfied as secure." % nick) elif type == constants.SMP_CALLBACK_ERROR: if errno == errors.ERR_SMP_CHECK_FAILED: QMessageBox.warning(self, errors.TITLE_PROTOCOL_ERROR, errors.PROTOCOL_ERROR % (nick)) elif errno == errors.ERR_SMP_MATCH_FAILED: QMessageBox.critical(self, errors.TITLE_SMP_MATCH_FAILED, errors.SMP_MATCH_FAILED) def handleError(self, nick, errno): self.handleErrorSignal.emit(nick, errno) @pyqtSlot(str, int) def handleErrorSlot(self, nick, errno): # If no nick was given, disable all tabs nick = str(nick) if nick == '': self.__disableAllTabs() else: try: tab = self.getTabByNick(nick)[0] tab.resetOrDisable() except: self.__disableAllTabs() if errno == errors.ERR_CONNECTION_ENDED: QMessageBox.warning(self, errors.TITLE_CONNECTION_ENDED, errors.CONNECTION_ENDED % (nick)) elif errno == errors.ERR_NICK_NOT_FOUND: QMessageBox.information(self, errors.TITLE_NICK_NOT_FOUND, errors.NICK_NOT_FOUND % (nick)) tab.nick = None elif errno == errors.ERR_CONNECTION_REJECTED: QMessageBox.warning(self, errors.TITLE_CONNECTION_REJECTED, errors.CONNECTION_REJECTED % (nick)) tab.nick = None elif errno == errors.ERR_BAD_HANDSHAKE: QMessageBox.warning(self, errors.TITLE_PROTOCOL_ERROR, errors.PROTOCOL_ERROR % (nick)) elif errno == errors.ERR_CLIENT_EXISTS: QMessageBox.information(self, errors.TITLE_CLIENT_EXISTS, errors.CLIENT_EXISTS % (nick)) elif errno == errors.ERR_SELF_CONNECT: QMessageBox.warning(self, errors.TITLE_SELF_CONNECT, errors.SELF_CONNECT) elif errno == errors.ERR_SERVER_SHUTDOWN: QMessageBox.critical(self, errors.TITLE_SERVER_SHUTDOWN, errors.SERVER_SHUTDOWN) elif errno == errors.ERR_ALREADY_CONNECTED: QMessageBox.information(self, errors.TITLE_ALREADY_CONNECTED, errors.ALREADY_CONNECTED % (nick)) elif errno == errors.ERR_INVALID_COMMAND: QMessageBox.warning(self, errors.TITLE_INVALID_COMMAND, errors.INVALID_COMMAND % (nick)) elif errno == errors.ERR_NETWORK_ERROR: QMessageBox.critical(self, errors.TITLE_NETWORK_ERROR, errors.NETWORK_ERROR) elif errno == errors.ERR_BAD_HMAC: QMessageBox.critical(self, errors.TITLE_BAD_HMAC, errors.BAD_HMAC) elif errno == errors.ERR_BAD_DECRYPT: QMessageBox.warning(self, errors.TITLE_BAD_DECRYPT, errors.BAD_DECRYPT) elif errno == errors.ERR_KICKED: QMessageBox.critical(self, errors.TITLE_KICKED, errors.KICKED) elif errno == errors.ERR_NICK_IN_USE: QMessageBox.warning(self, errors.TITLE_NICK_IN_USE, errors.NICK_IN_USE) self.restartCallback() elif errno == errors.ERR_MESSAGE_REPLAY: QMessageBox.critical(self, errors.TITLE_MESSAGE_REPLAY, errors.MESSAGE_REPLAY) elif errno == errors.ERR_MESSAGE_DELETION: QMessageBox.critical(self, errors.TITLE_MESSAGE_DELETION, errors.MESSAGE_DELETION) elif errno == errors.ERR_PROTOCOL_VERSION_MISMATCH: QMessageBox.critical(self, errors.TITLE_PROTOCOL_VERSION_MISMATCH, errors.PROTOCOL_VERSION_MISMATCH) self.restartCallback() else: QMessageBox.warning(self, errors.TITLE_UNKNOWN_ERROR, errors.UNKNOWN_ERROR % (nick)) def __disableAllTabs(self): for i in range(0, self.chatTabs.count()): curTab = self.chatTabs.widget(i) curTab.resetOrDisable() def postMessage(self, command, sourceNick, payload): self.sendMessageToTabSignal.emit(command, sourceNick, payload) @pyqtSlot(str, str, str) def sendMessageToTab(self, command, sourceNick, payload): # If a typing command, update the typing status in the tab, otherwise # show the message in the tab tab, tabIndex = self.getTabByNick(sourceNick) if command == constants.COMMAND_TYPING: # Show the typing status in the status bar if the tab is the selected tab if tabIndex == self.chatTabs.currentIndex(): payload = int(payload) if payload == constants.TYPING_START: self.statusBar.showMessage("%s is typing" % sourceNick) elif payload == constants.TYPING_STOP_WITHOUT_TEXT: self.statusBar.showMessage('') elif payload == constants.TYPING_STOP_WITH_TEXT: self.statusBar.showMessage("%s has entered text" % sourceNick) elif command == constants.COMMAND_SMP_0: print('got request for smp in tab %d' % (tabIndex)) else: tab.appendMessage(payload, constants.RECEIVER) # Update the unread message count if the message is not intended for the currently selected tab if tabIndex != self.chatTabs.currentIndex(): tab.unreadCount += 1 self.chatTabs.setTabText(tabIndex, "%s (%d)" % (tab.nick, tab.unreadCount)) else: # Clear the typing status if the current tab self.statusBar.showMessage('') # Show a system notifcation of the new message if not the current window or tab or the # scrollbar of the tab isn't at the bottom chatLogScrollbar = tab.widgetStack.widget(2).chatLog.verticalScrollBar() if not self.isActiveWindow() or tabIndex != self.chatTabs.currentIndex() or \ chatLogScrollbar.value() != chatLogScrollbar.maximum(): qtUtils.showDesktopNotification(self.systemTrayIcon, sourceNick, payload) @pyqtSlot(int) def tabChanged(self, index): # Reset the unread count for the tab when it's switched to tab = self.chatTabs.widget(index) # Change the window title to the nick if tab is None or tab.nick is None: self.setWindowTitle("Cryptully") else: self.setWindowTitle(tab.nick) if tab is not None and tab.unreadCount != 0: tab.unreadCount = 0 self.chatTabs.setTabText(index, tab.nick) @pyqtSlot(int) def closeTab(self, index): tab = self.chatTabs.widget(index) self.connectionManager.closeChat(tab.nick) self.chatTabs.removeTab(index) # Show a new tab if there are now no tabs left if self.chatTabs.count() == 0: self.addNewTab() def getTabByNick(self, nick): for i in range(0, self.chatTabs.count()): curTab = self.chatTabs.widget(i) if curTab.nick == nick: return (curTab, i) return None def isNickInTabs(self, nick): for i in range(0, self.chatTabs.count()): curTab = self.chatTabs.widget(i) if curTab.nick == nick: return True return False def __setMenubar(self): newChatIcon = QIcon(qtUtils.getAbsoluteImagePath('new_chat.png')) helpIcon = QIcon(qtUtils.getAbsoluteImagePath('help.png')) exitIcon = QIcon(qtUtils.getAbsoluteImagePath('exit.png')) menuIcon = QIcon(qtUtils.getAbsoluteImagePath('menu.png')) newChatAction = QAction(newChatIcon, '&New chat', self) authChatAction = QAction(newChatIcon, '&Authenticate chat', self) helpAction = QAction(helpIcon, 'Show &help', self) exitAction = QAction(exitIcon, '&Exit', self) newChatAction.triggered.connect(lambda: self.addNewTab()) authChatAction.triggered.connect(self.__showAuthDialog) helpAction.triggered.connect(self.__showHelpDialog) exitAction.triggered.connect(self.__exit) newChatAction.setShortcut('Ctrl+N') helpAction.setShortcut('Ctrl+H') exitAction.setShortcut('Ctrl+Q') optionsMenu = QMenu() optionsMenu.addAction(newChatAction) optionsMenu.addAction(authChatAction) optionsMenu.addAction(helpAction) optionsMenu.addAction(exitAction) optionsMenuButton = QToolButton() newChatButton = QToolButton() exitButton = QToolButton() newChatButton.clicked.connect(lambda: self.addNewTab()) exitButton.clicked.connect(self.__exit) optionsMenuButton.setIcon(menuIcon) newChatButton.setIcon(newChatIcon) exitButton.setIcon(exitIcon) optionsMenuButton.setMenu(optionsMenu) optionsMenuButton.setPopupMode(QToolButton.InstantPopup) toolbar = QToolBar(self) toolbar.addWidget(optionsMenuButton) toolbar.addWidget(newChatButton) toolbar.addWidget(exitButton) self.addToolBar(Qt.LeftToolBarArea, toolbar) def __showAuthDialog(self): client = self.connectionManager.getClient(self.chatTabs.currentWidget().nick) if client is None: QMessageBox.information(self, "Not Available", "You must be chatting with someone before you can authenticate the connection.") return try: question, answer, clickedButton = QSMPInitiateDialog.getQuestionAndAnswer() except AttributeError: QMessageBox.information(self, "Not Available", "Encryption keys are not available until you are chatting with someone") if clickedButton == constants.BUTTON_OKAY: client.initiateSMP(str(question), str(answer)) def __showHelpDialog(self): QHelpDialog(self).show() def __exit(self): if QMessageBox.Yes == QMessageBox.question(self, "Confirm Exit", "Are you sure you want to exit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No): qtUtils.exitApp()
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setupUi(self) self._init_menu() self._init_actions() self._init_models() self.lt = ListenThread(self.add_new_device) self._install_akmods = False self._main_pko = PackageKitQt() # right frame self.comboBoxModules.currentIndexChanged.connect(self._handle_select_module) self.buttonBoxDetails.clicked.connect(self._handle_rbb) self.__debug_mode__ = True self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setIcon(QIcon(":/img/gears")) self.trayIcon.activated.connect(self._handle_tray_icon_activate) self.hide() def closeEvent(self, event): event.ignore() self.hide() def _init_menu(self): self.actionFileExit.triggered.connect(self._handle_exit) self.listView.setContextMenuPolicy(Qt.ActionsContextMenu) self.listViewActions.setContextMenuPolicy(Qt.ActionsContextMenu) # context menu for listView for act in self.menuDevices.actions(): self.listView.addAction(act) # context menu for listViewActions for act in self.menuActions.actions(): self.listViewActions.addAction(act) def _init_actions(self): self.actionDevicesDisable.triggered.connect(self._handle_disable_device) self.actionDevicesDisableAll.triggered.connect(self._handle_disable_all) self.actionActionsDelete.triggered.connect(self._handle_remove_current_action) self.actionActionsClear.triggered.connect(self._handle_clean_actions) self.actionActionsApply.triggered.connect(self._handle_apply_actions) def _init_models(self): self.model = DevicesListModel(self) self.listView.setModel(self.model) self.listView.selectionModel().currentChanged.connect(self._handle_select_item) self.act_model = ActionsModel(self) self.listViewActions.setModel(self.act_model) self.listViewActions.selectionModel().currentChanged.connect(self._handle_action_select_item) self.act_model.actionDeleted.connect(self.model.reset_changes) self.model.dataChanged.connect(self._handle_data_changed_in_model) def _init_pk(self): if self._main_pko is None: self._main_pko = PackageKitQt() def add_new_device(self, dev): self.model.add_new_device(dev) self.show_notification() def disen_device_notif(self, device_id, disable=False): if disable: self.lt.disable_device_notif(device_id) else: self.lt.enable_device_notif(device_id) def show_notification(self): # if self.isHidden(): # self.show() self.trayIcon.setVisible(True) def start_listen(self): self.lt.start() def _right_frame_apply(self, idx): d = self.model.device_by_index(idx) sel_cb_idx = self.comboBoxModules.currentIndex() sel_module = str(self.comboBoxModules.itemData(sel_cb_idx).toString()) if sel_module == d.current_driver(): return self.act_model.remove_actions_by_devid(d.device_id(), sel_module) pkgsi = d.packages_to_install(sel_module) pkgsr = d.packages_to_remove(sel_module) if len(pkgsi) > 0: self.act_model.add_new_action(d.device_id(), sel_module, pkgsi, 0) if len(pkgsr) > 0: rem_mds = str(", ").join(d.device_modules(sel_module)) self.act_model.add_new_action(d.device_id(), rem_mds, pkgsr, 1) d.set_selected_driver(sel_module) self.set_right_frame(idx) def _current_device(self, idx=None): cur_idx = idx if idx is None: cur_idx = self.listView.selectionModel().currentIndex() return self.model.device_by_index(cur_idx) def set_right_frame(self, idx): self.comboBoxModules.clear() d = self._current_device(idx) curdrv = QString() if d.current_driver() is None or len(d.current_driver()) == 0: curdrv = self.tr("Not installed") else: curdrv = QString("- %s -" % d.current_driver()) self.comboBoxModules.addItem(curdrv, QString(d.current_driver())) self.lineEditName.setText(d.device_name()) our_sel_idx = -1 for m in d.device_modules(): devmod = QString() if m == d.current_driver(): continue elif m == d.selected_driver(): devmod = QString("* %s *" % m) our_sel_idx = self.comboBoxModules.count() else: devmod = QString("%s" % m) self.comboBoxModules.addItem(devmod, QString(m)) if our_sel_idx != -1: self.comboBoxModules.setCurrentIndex(our_sel_idx) def _do_resolve_packages(self, pkgs, to_remove=False): if len(pkgs) == 0: return [] self.debug_print(pkgs) self._init_pk() pkc = self._main_pko.getTransaction() pkc.SetHints() filt = "none" if to_remove: filt = "installed" pkc.SearchNames(filt, pkgs) if pkc.has_error(): return [] pkg_ids = pkc.get_package_ids() return pkg_ids pkc.Resolve(filt, pkgs) if pkc.has_error(): return [] pkg_ids = pkc.get_package_ids() return pkg_ids def _do_install_packages(self, pkgs): if len(pkgs) == 0: return print "Begin install packages" self._init_pk() pkc = self._main_pko.getTransaction() # PackageKitClient() pkc.SetHints() pkc.InstallPackages(False, pkgs) if pkc.has_error(): err_code, err_msg = pkc.error() self.debug_print("Error: [%s] %s" % (err_code, err_msg)) def _do_remove_packages(self, pkgs): if len(pkgs) == 0: return print "Begin remove packages" self._init_pk() pkc = self._main_pko.getTransaction() pkc.RemovePackages(pkgs, True, True) if pkc.has_error(): err_code, err_msg = pkc.error() self.debug_print("Error: [%s] %s" % (err_code, err_msg)) def _do_only_ids(self, pkgs): res_ids = [] if pkgs is None: return res_ids if len(pkgs) == 0: return res_ids print pkgs for installed, id, summary in pkgs: res_ids.append(id) return res_ids def _debug_print_pkg_ids(self, pkg_ids): if pkg_ids is None: return elif len(pkg_ids) == 0: return for pkg_id in pkg_ids: self.debug_print("+ Installed: %s" % (pkg_id)) def _do_act(self): pkgs_to_install, pkgs_to_remove = self.act_model.get_packages(self._install_akmods) if len(pkgs_to_install) + len(pkgs_to_remove) == 0: QMessageBox.information(self, self.tr("Empty actions"), self.tr("Nothing to install/remove")) return # Resolve all packages pkg_ids_install = self._do_resolve_packages(pkgs_to_install) pkg_ids_remove = self._do_resolve_packages(pkgs_to_remove, True) self.debug_print("To install: %s" % pkg_ids_install) self.debug_print("To remove: %s" % pkg_ids_remove) self._do_remove_packages(pkg_ids_remove) self._do_install_packages(pkg_ids_install) print "Packages to install:" self._debug_print_pkg_ids(pkg_ids_install) print "Packages to remove:" self._debug_print_pkg_ids(pkg_ids_remove) res = QMessageBox.question( self, self.tr("Operations done"), self.tr("All operations applied. You may reboot a system. Reboot now?"), QMessageBox.Yes and QMessageBox.No, QMessageBox.Yes, ) if res == QMessageBox.Yes: print "rebooting" def debug_print(self, msg): if not self.__debug_mode__: return print (msg) # slots def _handle_data_changed_in_model(self, begin_idx, end_idx): cur_idx = self.listView.selectionModel().currentIndex() if not cur_idx.isValid(): return cur_row = cur_idx.row() row_range = range(begin_idx.row(), end_idx.row()) if len(row_range) == 0: row_range = [begin_idx.row()] if cur_row in row_range: self.set_right_frame(cur_idx) def _handle_remove_current_action(self): cur_idx = self.listViewActions.selectionModel().currentIndex() self.act_model.removeRows(cur_idx.row(), 1) def _handle_clean_actions(self): self.act_model.clearRows() def _handle_disable_all(self): devs = self.model.disable_all_devices() self.disen_device_notif(devs, True) self.trayIcon.hide() def _handle_disable_device(self): cur_idx = self.listView.selectionModel().currentIndex() this_is_hide_item = self.model.index_is_hide(cur_idx) need_id = self.model.index_hide(cur_idx, not this_is_hide_item) self._handle_select_item(cur_idx, cur_idx) self.disen_device_notif(need_id, not this_is_hide_item) def _handle_exit(self): self.hide() def _handle_action_select_item(self, cur_idx, prev_idx): self.actionActionsDelete.setEnabled(cur_idx.isValid()) def _handle_select_item(self, current_idx, prev_idx): self.actionDevicesDisable.setEnabled(current_idx.isValid()) if not current_idx.isValid(): return if self.model.index_is_hide(current_idx): self.actionDevicesDisable.setText(self.tr("&Enable notification")) else: self.actionDevicesDisable.setText(self.tr("&Disable notification")) self.set_right_frame(current_idx) def _handle_rbb(self, but): cur_idx = self.listView.selectionModel().currentIndex() if self.buttonBoxDetails.buttonRole(but) == QDialogButtonBox.ResetRole: self.set_right_frame(cur_idx) elif self.buttonBoxDetails.buttonRole(but) == QDialogButtonBox.ApplyRole: self._right_frame_apply(cur_idx) def _handle_select_module(self, module_index): if module_index == -1: self.labelDetails.setText("") return selection_module_name = str(self.comboBoxModules.itemData(module_index).toString()) d = self._current_device() pkgsi = d.packages_to_install(selection_module_name) pkgsr = d.packages_to_remove(selection_module_name) detail_html = QString("<h4>%1 </h4>").arg(self.tr("For installing this module need:")) if len(pkgsi) > 0: detail_html += QString("<p>%1 <ul>").arg(self.tr("Packages to install:")) for p in pkgsi: detail_html += QString("<li>%1</li>").arg(p) detail_html += QString("</ul></p>") if len(pkgsr) > 0: detail_html += QString("<p>%1 <ul>").arg(self.tr("Packages to remove: ")) for p in pkgsr: detail_html += QString("<li>%1</li>").arg(p) detail_html += QString("</ul></p>") self.labelDetails.setText(detail_html) def _handle_apply_actions(self): if self.act_model.pkgs_to_install_exist(): result = QMessageBox.question( self, self.tr("Install akmods too"), self.tr("Do you have install also akmod (automated kernel module) packages too?"), QMessageBox.Yes and QMessageBox.No, QMessageBox.Yes, ) if result == QMessageBox.Yes: self._install_akmods = True self.setEnabled(False) self._do_act() self.setEnabled(True) def _handle_tray_icon_activate(self, reason): if self.isHidden(): self.show() else: self.hide()
class QChatWindow(QMainWindow): newClientSignal = pyqtSignal(str) clientReadySignal = pyqtSignal(str) smpRequestSignal = pyqtSignal(int, str, str, int) handleErrorSignal = pyqtSignal(str, int) sendMessageToTabSignal = pyqtSignal(str, str, str) def __init__(self, restartCallback, connectionManager=None, messageQueue=None): QMainWindow.__init__(self) self.restartCallback = restartCallback self.connectionManager = connectionManager self.messageQueue = messageQueue self.newClientSignal.connect(self.newClientSlot) self.clientReadySignal.connect(self.clientReadySlot) self.smpRequestSignal.connect(self.smpRequestSlot) self.handleErrorSignal.connect(self.handleErrorSlot) self.sendMessageToTabSignal.connect(self.sendMessageToTab) self.chatTabs = QTabWidget(self) self.chatTabs.setTabsClosable(True) self.chatTabs.setMovable(True) self.chatTabs.tabCloseRequested.connect(self.closeTab) self.chatTabs.currentChanged.connect(self.tabChanged) self.statusBar = self.statusBar() self.systemTrayIcon = QSystemTrayIcon(self) self.systemTrayIcon.setVisible(True) self.__setMenubar() vbox = QVBoxLayout() vbox.addWidget(self.chatTabs) # Add the completeted layout to the window self.centralWidget = QWidget() self.centralWidget.setLayout(vbox) self.setCentralWidget(self.centralWidget) qtUtils.resizeWindow(self, 700, 400) qtUtils.centerWindow(self) # Title and icon self.setWindowTitle("Cryptully") self.setWindowIcon(QIcon(qtUtils.getAbsoluteImagePath('icon.png'))) def connectedToServer(self): # Add an initial tab once connected to the server self.addNewTab() def newClient(self, nick): # This function is called from a bg thread. Send a signal to get on the UI thread self.newClientSignal.emit(nick) @pyqtSlot(str) def newClientSlot(self, nick): nick = str(nick) # Show a system notifcation of the new client if not the current window if not self.isActiveWindow(): qtUtils.showDesktopNotification(self.systemTrayIcon, "Chat request from %s" % nick, '') # Show the accept dialog accept = QAcceptDialog.getAnswer(self, nick) if not accept: self.connectionManager.newClientRejected(nick) return # If nick already has a tab, reuse it if self.isNickInTabs(nick): self.getTabByNick(nick)[0].enable() else: self.addNewTab(nick) self.connectionManager.newClientAccepted(nick) def addNewTab(self, nick=None): newTab = QChatTab(self, nick) self.chatTabs.addTab(newTab, nick if nick is not None else "New Chat") self.chatTabs.setCurrentWidget(newTab) newTab.setFocus() def clientReady(self, nick): # Use a signal to call the client ready slot on the UI thread since # this function is called from a background thread self.clientReadySignal.emit(nick) @pyqtSlot(str) def clientReadySlot(self, nick): nick = str(nick) tab, tabIndex = self.getTabByNick(nick) self.chatTabs.setTabText(tabIndex, nick) tab.showNowChattingMessage() # Set the window title if the tab is the selected tab if tabIndex == self.chatTabs.currentIndex(): self.setWindowTitle(nick) def smpRequest(self, type, nick, question='', errno=0): self.smpRequestSignal.emit(type, nick, question, errno) @pyqtSlot(int, str, str, int) def smpRequestSlot(self, type, nick, question='', errno=0): if type == constants.SMP_CALLBACK_REQUEST: answer, clickedButton = QSMPRespondDialog.getAnswer(nick, question) if clickedButton == constants.BUTTON_OKAY: self.connectionManager.respondSMP(str(nick), str(answer)) elif type == constants.SMP_CALLBACK_COMPLETE: QMessageBox.information( self, "%s Authenticated" % nick, "Your chat session with %s has been succesfully authenticated. The conversation is verfied as secure." % nick) elif type == constants.SMP_CALLBACK_ERROR: if errno == errors.ERR_SMP_CHECK_FAILED: QMessageBox.warning(self, errors.TITLE_PROTOCOL_ERROR, errors.PROTOCOL_ERROR % (nick)) elif errno == errors.ERR_SMP_MATCH_FAILED: QMessageBox.critical(self, errors.TITLE_SMP_MATCH_FAILED, errors.SMP_MATCH_FAILED) def handleError(self, nick, errno): self.handleErrorSignal.emit(nick, errno) @pyqtSlot(str, int) def handleErrorSlot(self, nick, errno): # If no nick was given, disable all tabs nick = str(nick) if nick == '': self.__disableAllTabs() else: try: tab = self.getTabByNick(nick)[0] tab.resetOrDisable() except: self.__disableAllTabs() if errno == errors.ERR_CONNECTION_ENDED: QMessageBox.warning(self, errors.TITLE_CONNECTION_ENDED, errors.CONNECTION_ENDED % (nick)) elif errno == errors.ERR_NICK_NOT_FOUND: QMessageBox.information(self, errors.TITLE_NICK_NOT_FOUND, errors.NICK_NOT_FOUND % (nick)) tab.nick = None elif errno == errors.ERR_CONNECTION_REJECTED: QMessageBox.warning(self, errors.TITLE_CONNECTION_REJECTED, errors.CONNECTION_REJECTED % (nick)) tab.nick = None elif errno == errors.ERR_BAD_HANDSHAKE: QMessageBox.warning(self, errors.TITLE_PROTOCOL_ERROR, errors.PROTOCOL_ERROR % (nick)) elif errno == errors.ERR_CLIENT_EXISTS: QMessageBox.information(self, errors.TITLE_CLIENT_EXISTS, errors.CLIENT_EXISTS % (nick)) elif errno == errors.ERR_SELF_CONNECT: QMessageBox.warning(self, errors.TITLE_SELF_CONNECT, errors.SELF_CONNECT) elif errno == errors.ERR_SERVER_SHUTDOWN: QMessageBox.critical(self, errors.TITLE_SERVER_SHUTDOWN, errors.SERVER_SHUTDOWN) elif errno == errors.ERR_ALREADY_CONNECTED: QMessageBox.information(self, errors.TITLE_ALREADY_CONNECTED, errors.ALREADY_CONNECTED % (nick)) elif errno == errors.ERR_INVALID_COMMAND: QMessageBox.warning(self, errors.TITLE_INVALID_COMMAND, errors.INVALID_COMMAND % (nick)) elif errno == errors.ERR_NETWORK_ERROR: QMessageBox.critical(self, errors.TITLE_NETWORK_ERROR, errors.NETWORK_ERROR) elif errno == errors.ERR_BAD_HMAC: QMessageBox.critical(self, errors.TITLE_BAD_HMAC, errors.BAD_HMAC) elif errno == errors.ERR_BAD_DECRYPT: QMessageBox.warning(self, errors.TITLE_BAD_DECRYPT, errors.BAD_DECRYPT) elif errno == errors.ERR_KICKED: QMessageBox.critical(self, errors.TITLE_KICKED, errors.KICKED) elif errno == errors.ERR_NICK_IN_USE: QMessageBox.warning(self, errors.TITLE_NICK_IN_USE, errors.NICK_IN_USE) self.restartCallback() elif errno == errors.ERR_MESSAGE_REPLAY: QMessageBox.critical(self, errors.TITLE_MESSAGE_REPLAY, errors.MESSAGE_REPLAY) elif errno == errors.ERR_MESSAGE_DELETION: QMessageBox.critical(self, errors.TITLE_MESSAGE_DELETION, errors.MESSAGE_DELETION) elif errno == errors.ERR_PROTOCOL_VERSION_MISMATCH: QMessageBox.critical(self, errors.TITLE_PROTOCOL_VERSION_MISMATCH, errors.PROTOCOL_VERSION_MISMATCH) self.restartCallback() else: QMessageBox.warning(self, errors.TITLE_UNKNOWN_ERROR, errors.UNKNOWN_ERROR % (nick)) def __disableAllTabs(self): for i in range(0, self.chatTabs.count()): curTab = self.chatTabs.widget(i) curTab.resetOrDisable() def postMessage(self, command, sourceNick, payload): self.sendMessageToTabSignal.emit(command, sourceNick, payload) @pyqtSlot(str, str, str) def sendMessageToTab(self, command, sourceNick, payload): # If a typing command, update the typing status in the tab, otherwise # show the message in the tab tab, tabIndex = self.getTabByNick(sourceNick) if command == constants.COMMAND_TYPING: # Show the typing status in the status bar if the tab is the selected tab if tabIndex == self.chatTabs.currentIndex(): payload = int(payload) if payload == constants.TYPING_START: self.statusBar.showMessage("%s is typing" % sourceNick) elif payload == constants.TYPING_STOP_WITHOUT_TEXT: self.statusBar.showMessage('') elif payload == constants.TYPING_STOP_WITH_TEXT: self.statusBar.showMessage("%s has entered text" % sourceNick) elif command == constants.COMMAND_SMP_0: print(('got request for smp in tab %d' % (tabIndex))) else: tab.appendMessage(payload, constants.RECEIVER) # Update the unread message count if the message is not intended for the currently selected tab if tabIndex != self.chatTabs.currentIndex(): tab.unreadCount += 1 self.chatTabs.setTabText( tabIndex, "%s (%d)" % (tab.nick, tab.unreadCount)) else: # Clear the typing status if the current tab self.statusBar.showMessage('') # Show a system notifcation of the new message if not the current window or tab or the # scrollbar of the tab isn't at the bottom chatLogScrollbar = tab.widgetStack.widget( 2).chatLog.verticalScrollBar() if not self.isActiveWindow() or tabIndex != self.chatTabs.currentIndex() or \ chatLogScrollbar.value() != chatLogScrollbar.maximum(): qtUtils.showDesktopNotification(self.systemTrayIcon, sourceNick, payload) @pyqtSlot(int) def tabChanged(self, index): # Reset the unread count for the tab when it's switched to tab = self.chatTabs.widget(index) # Change the window title to the nick if tab is None or tab.nick is None: self.setWindowTitle("Cryptully") else: self.setWindowTitle(tab.nick) if tab is not None and tab.unreadCount != 0: tab.unreadCount = 0 self.chatTabs.setTabText(index, tab.nick) @pyqtSlot(int) def closeTab(self, index): tab = self.chatTabs.widget(index) self.connectionManager.closeChat(tab.nick) self.chatTabs.removeTab(index) # Show a new tab if there are now no tabs left if self.chatTabs.count() == 0: self.addNewTab() def getTabByNick(self, nick): for i in range(0, self.chatTabs.count()): curTab = self.chatTabs.widget(i) if curTab.nick == nick: return (curTab, i) return None def isNickInTabs(self, nick): for i in range(0, self.chatTabs.count()): curTab = self.chatTabs.widget(i) if curTab.nick == nick: return True return False def __setMenubar(self): newChatIcon = QIcon(qtUtils.getAbsoluteImagePath('new_chat.png')) helpIcon = QIcon(qtUtils.getAbsoluteImagePath('help.png')) exitIcon = QIcon(qtUtils.getAbsoluteImagePath('exit.png')) menuIcon = QIcon(qtUtils.getAbsoluteImagePath('menu.png')) newChatAction = QAction(newChatIcon, '&New chat', self) authChatAction = QAction(newChatIcon, '&Authenticate chat', self) helpAction = QAction(helpIcon, 'Show &help', self) exitAction = QAction(exitIcon, '&Exit', self) newChatAction.triggered.connect(lambda: self.addNewTab()) authChatAction.triggered.connect(self.__showAuthDialog) helpAction.triggered.connect(self.__showHelpDialog) exitAction.triggered.connect(self.__exit) newChatAction.setShortcut('Ctrl+N') helpAction.setShortcut('Ctrl+H') exitAction.setShortcut('Ctrl+Q') optionsMenu = QMenu() optionsMenu.addAction(newChatAction) optionsMenu.addAction(authChatAction) optionsMenu.addAction(helpAction) optionsMenu.addAction(exitAction) optionsMenuButton = QToolButton() newChatButton = QToolButton() exitButton = QToolButton() newChatButton.clicked.connect(lambda: self.addNewTab()) exitButton.clicked.connect(self.__exit) optionsMenuButton.setIcon(menuIcon) newChatButton.setIcon(newChatIcon) exitButton.setIcon(exitIcon) optionsMenuButton.setMenu(optionsMenu) optionsMenuButton.setPopupMode(QToolButton.InstantPopup) toolbar = QToolBar(self) toolbar.addWidget(optionsMenuButton) toolbar.addWidget(newChatButton) toolbar.addWidget(exitButton) self.addToolBar(Qt.LeftToolBarArea, toolbar) def __showAuthDialog(self): client = self.connectionManager.getClient( self.chatTabs.currentWidget().nick) if client is None: QMessageBox.information( self, "Not Available", "You must be chatting with someone before you can authenticate the connection." ) return try: question, answer, clickedButton = QSMPInitiateDialog.getQuestionAndAnswer( ) except AttributeError: QMessageBox.information( self, "Not Available", "Encryption keys are not available until you are chatting with someone" ) if clickedButton == constants.BUTTON_OKAY: client.initiateSMP(str(question), str(answer)) def __showHelpDialog(self): QHelpDialog(self).show() def __exit(self): if QMessageBox.Yes == QMessageBox.question( self, "Confirm Exit", "Are you sure you want to exit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No): qtUtils.exitApp()
class KerberusSystray(QWidget): def __init__(self): self.chequeos_activos = True self.ultimo_estado_kerberus = True QWidget.__init__(self) #icono = 'kerby-activo.ico' #pixmap = QPixmap(icono) self.style = self.style() ##setear el nombre de la ventana self.setWindowTitle('Kerberus Control Parental') #colocar el icono cargado a la ventana self.setWindowIcon(self.style.standardIcon( QStyle.SP_DialogYesButton)) self.filtradoHabilitado = True if not os.path.isfile('dontShowMessage'): self.mostrarMensaje = True self.noMostrarMasMensaje() else: self.mostrarMensaje = False #Menu self.menu = QMenu('Kerberus') #accion configurar Dominios self.configurarDominiosAction = self.menu.addAction( 'Permitir/Denegar dominios' ) #accion deshabilitar filtrado self.deshabilitarFiltradoAction = self.menu.addAction( 'Deshabilitar Filtrado' ) #accion habilitar filtrado self.habilitarFiltradoAction = self.menu.addAction( 'Habilitar Filtrado' ) self.habilitarFiltradoAction.setVisible(False) #cambiar password self.cambiarPasswordAction = self.menu.addAction( 'Cambiar password de administrador' ) #recordar password self.recordarPasswordAction = self.menu.addAction( 'Recordar password del administrador' ) #accion salir self.exitAction = self.menu.addAction( 'Salir') #SIGNAL->SLOT QObject.connect( self.exitAction, SIGNAL("triggered()"), #lambda: sys.exit() self.salir ) # esta conexion es utilizada para refrezcar el icono en caso de # que se desactive/active kerberus QObject.connect( self, SIGNAL("update()"), #lambda: sys.exit() self.setIconStatus ) QObject.connect( self.menu, SIGNAL("clicked()"), lambda: self.menu.popup(QCursor.pos()) ) QObject.connect( self.deshabilitarFiltradoAction, SIGNAL("triggered()"), self.deshabilitarFiltradoWindow ) QObject.connect( self.habilitarFiltradoAction, SIGNAL("triggered()"), self.habilitarFiltradoWindow ) QObject.connect( self.cambiarPasswordAction, SIGNAL("triggered()"), self.cambiarPasswordWindow ) QObject.connect( self.configurarDominiosAction, SIGNAL("triggered()"), self.configurarDominios ) QObject.connect( self.recordarPasswordAction, SIGNAL("triggered()"), self.recordarPasswordWindow ) #SystemTray #self.tray = QSystemTrayIcon(QIcon(pixmap), self) self.tray = QSystemTrayIcon(self.style.standardIcon( QStyle.SP_DialogYesButton), self) self.tray.setToolTip('Kerberus Control Parental - Activado') self.tray.setContextMenu(self.menu) self.tray.setVisible(True) QObject.connect( self.tray, SIGNAL("messageClicked()"), self.noMostrarMasMensaje ) if self.mostrarMensaje: self.tray.showMessage( u'Kerberus Control Parental', u'Filtro de Protección para menores de edad Activado', 2000 ) # Lanzo el thead que verifica si esta activo o no kerberus self.t = threading.Thread(target=self.chequeosPeriodicos) self.t.start() def chequeosPeriodicos(self): while self.chequeos_activos: time.sleep(3) status = self.checkKerberusStatus() if status != self.ultimo_estado_kerberus: self.ultimo_estado_kerberus = status self.emit(SIGNAL('update()')) def setIconStatus(self): if self.ultimo_estado_kerberus: self.habilitarFiltradoAction.setVisible(False) self.deshabilitarFiltradoAction.setVisible(True) self.tray.setIcon(self.style.standardIcon( QStyle.SP_DialogYesButton)) self.tray.setToolTip('Kerberus Control Parental - Activado') self.tray.showMessage( u'Kerberus Control Parental', u'Filtro de Protección para menores de edad Activado', 2000 ) else: self.habilitarFiltradoAction.setVisible(True) self.deshabilitarFiltradoAction.setVisible(False) self.tray.setIcon(self.style.standardIcon( QStyle.SP_DialogNoButton)) self.tray.setToolTip('Kerberus Control Parental - Inactivo') self.tray.showMessage( u'Kerberus Control Parental', u'Filtro de Protección para menores de edad Desactivado', 2000 ) def salir(self): self.chequeos_activos = False sys.exit() def configurarDominios(self): admin = adminPanel() admin.show() def noMostrarMasMensaje(self): try: open('dontShowMessage', 'a').close() except IOError: print 'No se pudo crear el archivo dontShowMessage' def deshabilitarFiltradoWindow(self): url = 'http://%s:%s/!DeshabilitarFiltrado!' % ('inicio.kerberus.com.ar', '80') webbrowser.open( url, new=2 ) def checkKerberusStatus(self): try: url = 'http://%s:%s/' % (config.BIND_ADDRESS, config.BIND_PORT) con = httplib.HTTPConnection(config.BIND_ADDRESS, config.BIND_PORT) con.request(method='KERBERUSESTADO', url=url) respuesta = con.getresponse().read() return respuesta == 'Activo' except: return False def habilitarFiltradoWindow(self): url = "http://%s:%s/!HabilitarFiltrado!" % ('inicio.kerberus.com.ar', '80') webbrowser.open( url, new=2 ) def cambiarPasswordWindow(self): url = "http://%s:%s/!CambiarPassword!" % ('inicio.kerberus.com.ar', '80') webbrowser.open( url, new=2 ) def recordarPasswordWindow(self): url = "http://%s:%s/!RecordarPassword!" % ('inicio.kerberus.com.ar', '80') webbrowser.open( url, new=2 )