class main(QDialog): #托盘 def __init__(self): super().__init__() self.loadMenu() self.initUI() def loadMenu(self): menuItems = [] menuItems.append({"text": "启动", "icon": "./icon/launch.png", "event": self.show, "hot": "D"}) menuItems.append({"text": "退出", "icon": "./icon/car.png", "event": self.close, "hot": "Q"}) self.trayIconMenu = QMenu(self) for i in menuItems: tmp = QAction(QIcon(i["icon"]), i["text"],self, triggered=i["event"]) tmp.setShortcut(self.tr(i["hot"])) self.trayIconMenu.addAction(tmp) def initUI(self): self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setIcon(QIcon("./icon/car.png")) self.trayIcon.setContextMenu(self.trayIconMenu) self.trayIcon.show() self.setWindowIcon(QIcon("./icon/car.png")) self.setGeometry(300, 300, 180, 300) self.setWindowTitle('窗体标题') def closeEvent(self, event): if self.trayIcon.isVisible(): self.trayIcon.hide()
class MainWindow(QMainWindow): def __init__(self, app): QMainWindow.__init__(self) # Config app icon self.app_icon = QtGui.QIcon("assets/icon.png") self.setWindowIcon(self.app_icon) self.tray_icon = QSystemTrayIcon(self.app_icon, self) self.tray_icon.activated.connect(self.tray_icon_event) menu = QMenu(self) quit_action = menu.addAction("Fechar aplicação") quit_action.triggered.connect(app.quit) tray_menu = QMenu() tray_menu.addAction(quit_action) self.tray_icon.setContextMenu(tray_menu) self.tray_icon.show() def tray_icon_event(self, reason): if reason == QSystemTrayIcon.DoubleClick: if not self.isVisible(): self.show() def hide_window_and_tray(self): if self.tray_icon.isVisible(): self.tray_icon.hide() if self.isVisible(): self.hide() # When user clicks on window quit button def closeEvent(self, event): event.ignore() self.hide() self.tray_icon.showMessage("File upload", "Aplicação foi minimizada", QSystemTrayIcon.Information, 1000)
class Main(QDialog): def __init__(self): super().__init__() self.loadMenu() self.initUI() def loadMenu(self): menuItems = [] # 菜单列表 menuItems.append({ "text": "启动", "icon": "./icons/set.png", "event": self.show, "hot": "D" }) menuItems.append({ "text": "退出", "icon": "./icons/close.png", "event": self.close, "hot": "Q" }) self.trayIconMenu = QMenu(self) # 创建菜单 #遍历绑定 显示的文字、图标、热键和点击事件 #热键可能是无效的 我这里只是为了显示效果而已 for i in menuItems: tmp = QAction(QIcon(i["icon"]), i["text"], self, triggered=i["event"]) tmp.setShortcut(self.tr(i["hot"])) self.trayIconMenu.addAction(tmp) def initUI(self): self.trayIcon = QSystemTrayIcon(self) # <===创建通知栏托盘图标 self.trayIcon.setIcon(QIcon("./joyrun/request/pic.ico")) #<===设置托盘图标 self.trayIcon.setContextMenu(self.trayIconMenu) #<===创建右键连接菜单 self.trayIcon.show() #<====显示托盘 self.setWindowIcon(QIcon("./joyrun/request/pic.ico")) #<===设置窗体图标 self.setGeometry(300, 300, 180, 300) # <===设置窗体打开位置与宽高 self.setWindowTitle('窗体标题') self.show() #<====显示窗体 # self.hide()#<====隐藏窗体 # 默认不显示窗体 # 重写窗体关闭事件,让其点击关闭时隐藏 def closeEvent(self, event): if self.trayIcon.isVisible(): self.trayIcon.hide()
class main(QDialog): def __init__(self): super().__init__() self.trayIcon = QSystemTrayIcon(self) # <===创建通知栏托盘图标 self.trayIconMenu = QMenu(self) # 创建菜单 self.loadMenu() self.initUI() # self.setWindowFlags(QtCore.Qt.WindowMinimizeButtonHint) # self.tray.activated.connect(self.TuoPanEvent) #设置托盘点击事件处理函数 def loadMenu(self): menuItems = [{"text": "启动", "icon": "icons/set.png", "event": self.show, "hot": "D"}, {"text": "退出", "icon": "icons/switch.png", "event": self.close, "hot": "Q"}] # 菜单列表 # 遍历绑定 显示的文字、图标、热键和点击事件 # 热键可能是无效的 我这里只是为了显示效果而已 for i in menuItems: tmp = QAction(QIcon(i["icon"]), i["text"], self, triggered=i["event"]) tmp.setShortcut(self.tr(i["hot"])) self.trayIconMenu.addAction(tmp) def initUI(self): self.trayIcon.setIcon(QIcon("icons/alert.png")) # <===设置托盘图标 self.trayIcon.setContextMenu(self.trayIconMenu) # <===创建右键连接菜单 self.trayIcon.show() # <====显示托盘 # self.trayIcon.setToolTip('aa') self.setWindowIcon(QIcon("icons/alert.png")) # <===设置窗体图标 self.setGeometry(900, 300, 180, 300) # <===设置窗体打开位置与宽高 self.setWindowTitle('订单列表') # self.show() # <====显示窗体 # self.hide()#<====隐藏窗体 # 默认不显示窗体 def close(self): sys.exit(0) # 重写窗体关闭事件,让其点击关闭时隐藏 def closeEvent(self, event): if self.trayIcon.isVisible(): self.hide() event.ignore() else: event.accept()
class DlgMain(QDialog): def addSystemTray(self): minimizeAction = QAction("Mi&nimize", self, triggered=self.hide) maximizeAction = QAction("Ma&ximize", self, triggered=self.showMaximized) restoreAction = QAction("&Restore", self, triggered=self.showNormal) quitAction = QAction("&Quit", self, triggered=self.close) self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(minimizeAction) self.trayIconMenu.addAction(maximizeAction) self.trayIconMenu.addAction(restoreAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setIcon(QIcon("skin/icons/logo.png")) self.setWindowIcon(QIcon("skin/icons/logo.png")) self.trayIcon.setContextMenu(self.trayIconMenu) self.trayIcon.show() sys.exit(self.exec_()) def closeEvent(self, event): if self.trayIcon.isVisible(): self.trayIcon.hide()
class bridgePanel(QMainWindow, QObject): start = pyqtSignal() stop = pyqtSignal() def __init__(self, app): super().__init__() self.bridgetreasureChest = bridgetreasureChest.bridgetreasureChest() self.app = app self.translate = QCoreApplication.translate self.__v2rayshellVersion = "20180511" self.__windowTitile = "V2Ray-shell" + " " + self.__v2rayshellVersion self.runv2raycore = False self.iconStart = QIcon() self.iconStop = QIcon() self.__iconSize = QSize(32, 32) self.iconStart.addPixmap(QPixmap(filePath + "/icons/start.png"), QIcon.Normal, QIcon.On) self.iconStop.addPixmap(QPixmap(filePath + "/icons/stop.png"), QIcon.Disabled, QIcon.On) self.currentRowRightClicked = False self.v2rayshellTrayIcon = QSystemTrayIcon() self.v2rayshellTrayIcon.setIcon(self.iconStart) self.v2rayshellTrayIcon.show() self.radioButtonGroup = QButtonGroup() self.setV2RayshellLanguage() self.trytimes = self.bridgetreasureChest.getConnectiontrytimes() self.interval = self.bridgetreasureChest.getConnectioninterval() self.proxyTryConnect = proxyTryconnect() if v2rayshellDebug: self.proxyTryConnect.setresetTime(6, 3) else: self.proxyTryConnect.setresetTime(self.interval, self.trytimes) self.labelBridge = (self.translate("bridgePanel", "Start/Stop"), self.translate("bridgePanel", "Host Name"), self.translate("bridgePanel", "Config Name"), self.translate("bridgePanel", "Proxy"), self.translate("bridgePanel", "Time Lag")) self.checkTrayicon = QTimer() self.createBridgePanel() def createBridgePanel(self): self.setWindowTitle(self.__windowTitile) self.setWindowIcon(self.iconStart) menubar = self.menuBar() self.statusBar() self.actionNewV2rayConfigFile = QAction( self.translate("bridgePanel", "Add V2Ray-core Config File"), self) self.actionNewV2rayConfigFile.setShortcut(QKeySequence.New) self.actionNewV2rayConfigFile.setStatusTip( self.translate("bridgePanel", "Add V2Ray-core Config File")) self.actionSaveV2rayshellConfigFile = QAction( self.translate("bridgePanel", "Save V2Ray-shell Config File"), self) self.actionSaveV2rayshellConfigFile.setShortcut(QKeySequence.Save) self.actionSaveV2rayshellConfigFile.setStatusTip( self.translate("bridgePanel", "Save V2Ray-shell Config File")) self.actionReloadV2rayshellConfigFile = QAction( self.translate("bridgePanel", "Open V2Ray-shell Config File"), self) self.actionReloadV2rayshellConfigFile.setShortcut(QKeySequence.Open) self.actionReloadV2rayshellConfigFile.setStatusTip( self.translate("bridgePanel", "Open V2Ray-shell Config File")) self.actionQuitV2rayshellPanel = QAction( self.translate("bridgePanel", "Quit"), self) if sys.platform.startswith('win'): self.actionQuitV2rayshellPanel.setShortcut("Ctrl+Q") else: self.actionQuitV2rayshellPanel.setShortcut(QKeySequence.Quit) self.actionQuitV2rayshellPanel.setStatusTip( self.translate("bridgePanel", "Quit V2Ray-shell")) fileMenu = menubar.addMenu(self.translate("bridgePanel", "&File")) fileMenu.addAction(self.actionNewV2rayConfigFile) fileMenu.addSeparator() fileMenu.addAction(self.actionReloadV2rayshellConfigFile) fileMenu.addAction(self.actionSaveV2rayshellConfigFile) fileMenu.addSeparator() fileMenu.addAction(self.actionQuitV2rayshellPanel) self.texteditBridge = QTextEdit(self) self.texteditBridge.setReadOnly(True) self.tableWidgetBridge = QTableWidget() self.tableWidgetBridge.setRowCount(0) self.tableWidgetBridge.setColumnCount(5) self.tableWidgetBridge.setHorizontalHeaderLabels(self.labelBridge) self.tableWidgetBridge.setSelectionMode( QAbstractItemView.SingleSelection) self.tableWidgetBridge.setSelectionBehavior( QAbstractItemView.SelectRows) self.tableWidgetBridge.setEditTriggers( QAbstractItemView.NoEditTriggers) #self.tableWidgetBridge.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.tableWidgetBridge.setContextMenuPolicy(Qt.CustomContextMenu) self.popMenu = popMenu = QMenu(self.tableWidgetBridge) self.actionpopMenuAddV2rayConfigFile = QAction( self.translate("bridgePanel", "Add V2Ray Config File"), self) self.actionpopMenuAddV2rayConfigFile.setShortcut("Ctrl+n") self.actionpopMenuEditV2rayConfigFile = QAction( self.translate("bridgePanel", "Edit V2Ray Config File"), self) self.actionpopMenuProxyCheckTimeLag = QAction( self.translate("bridgePanel", "Proxy Time Lag Check..."), self) self.actionpopMenuDeleteRow = QAction( self.translate("bridgePanel", "Delete"), self) popMenu.addAction(self.actionpopMenuAddV2rayConfigFile) popMenu.addAction(self.actionpopMenuEditV2rayConfigFile) popMenu.addAction(self.actionpopMenuProxyCheckTimeLag) popMenu.addAction(self.actionpopMenuDeleteRow) self.actionopenV2rayshellPreferencesPanel = QAction( self.translate("bridgePanel", "preferences"), self) self.actionopenV2rayshellPreferencesPanel.setStatusTip( self.translate("bridgePanel", "Setting V2Ray-shell")) optionMenu = menubar.addMenu(self.translate("bridgePanel", "&options")) optionMenu.addAction(self.actionpopMenuProxyCheckTimeLag) optionMenu.addAction(self.actionopenV2rayshellPreferencesPanel) helpMenu = menubar.addMenu(self.translate("bridgePanel", "&help")) self.actioncheckv2raycoreupdate = QAction( self.translate("bridgePanel", "check V2Ray-core update"), self) self.actionv2rayshellBugreport = QAction( self.translate("bridgePanel", "Bug Report"), self) self.actionaboutv2rayshell = QAction( self.translate("bridgePanel", "About"), self) helpMenu.addAction(self.actioncheckv2raycoreupdate) helpMenu.addAction(self.actionv2rayshellBugreport) helpMenu.addAction(self.actionaboutv2rayshell) toolBar = QToolBar() self.actionV2rayStart = QAction(self.translate("bridgePanel", "Start")) self.actionV2rayStart.setIcon(self.style().standardIcon( getattr(QStyle, "SP_MediaPlay"))) self.actionV2rayStop = QAction(self.translate("bridgePanel", "Stop")) self.actionV2rayStop.setIcon(self.style().standardIcon( getattr(QStyle, "SP_MediaStop"))) toolBar.addAction(self.actionV2rayStart) toolBar.addAction(self.actionV2rayStop) self.addToolBar(toolBar) self.trayIconMenu = QMenu() self.v2rayshellTrayIcon.setContextMenu(self.trayIconMenu) self.trayIconMenushowhidePanel = QAction( self.translate("bridgePanel", "Show/Hide")) self.trayIconMenuclosePanel = QAction( self.translate("bridgePanel", "Quit")) self.trayIconMenu.addAction(self.trayIconMenushowhidePanel) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.trayIconMenuclosePanel) self.splitterBridge = QSplitter(Qt.Vertical) self.splitterBridge.addWidget(self.tableWidgetBridge) self.splitterBridge.addWidget(self.texteditBridge) self.setCentralWidget(self.splitterBridge) self.createBridgePanelSignals() self.onloadV2rayshellConfigFile(init=True) self.onv2raycoreStart() self.autocheckv2raycoreUpdate() def createBridgePanelSignals(self): self.actionNewV2rayConfigFile.triggered.connect( self.tableWidgetBridgeAddNewV2rayConfigFile) self.actionReloadV2rayshellConfigFile.triggered.connect( self.onloadV2rayshellConfigFile) self.actionSaveV2rayshellConfigFile.triggered.connect( self.onsaveV2rayshellConfigFile) self.actionopenV2rayshellPreferencesPanel.triggered.connect( self.createBridgepreferencesPanel) self.actionpopMenuAddV2rayConfigFile.triggered.connect( self.tableWidgetBridgeAddNewV2rayConfigFile) self.actionpopMenuEditV2rayConfigFile.triggered.connect( self.oncreatenauticalChartPanel) self.actionpopMenuDeleteRow.triggered.connect( self.tableWidgetBridgeDelete) self.actionpopMenuProxyCheckTimeLag.triggered.connect( self.onproxyserverTimeLagTest) self.actioncheckv2raycoreupdate.triggered.connect( self.onopenv2rayupdatePanel) self.actionv2rayshellBugreport.triggered.connect(self.bugReportPanel) self.actionQuitV2rayshellPanel.triggered.connect(self.close) self.actionV2rayStart.triggered.connect(self.onv2raycoreStart) self.actionV2rayStop.triggered.connect(self.onv2raycoreStop) self.actionaboutv2rayshell.triggered.connect(self.about) self.radioButtonGroup.buttonClicked.connect(self.onradioButtonClicked) self.tableWidgetBridge.cellDoubleClicked.connect( self.ontableWidgetBridgecellDoubleClicked) self.tableWidgetBridge.customContextMenuRequested.connect( self.ontableWidgetBridgeRightClicked) self.v2rayshellTrayIcon.activated.connect(self.restorebridgePanel) self.trayIconMenushowhidePanel.triggered.connect( self.onsystemTrayIconMenushowhidebridgePanel) self.trayIconMenuclosePanel.triggered.connect(self.close) self.proxyTryConnect.reconnectproxy.connect(self.swapNextConfigFile) self.start.connect(self.onupdateinstallFinishedstartNewV2raycore) self.stop.connect(self.onv2raycoreStop) self.checkTrayicon.timeout.connect(self.checkSystemTrayIconStatus) self.checkTrayicon.start(1000 * 60) def checkSystemTrayIconStatus(self): """ maybe auto startup will missing trayicon """ if not self.v2rayshellTrayIcon.isVisible(): self.v2rayshellTrayIcon.show() else: self.checkTrayicon.disconnect() def setV2RayshellLanguage(self): self.trans = QTranslator() language = self.bridgetreasureChest.getLanguage() allLanguages = self.bridgetreasureChest.getAllLanguage() if language and allLanguages: if language in allLanguages: self.trans.load(allLanguages[language]) self.app.installTranslator(self.trans) def autocheckv2raycoreUpdate(self): self.bridgeSingal = (self.start, self.stop) self.v2rayshellautoUpdate = updatePanel.autoCheckUpdate( bridgetreasureChest=self.bridgetreasureChest, bridgeSingal=self.bridgeSingal) def event(self, event): if (event.type() == QEvent.WindowStateChange and self.isMinimized()): self.setWindowFlags(self.windowFlags() & ~Qt.Tool) self.v2rayshellTrayIcon.show() return True else: return super(bridgePanel, self).event(event) def onsystemTrayIconMenushowhidebridgePanel(self): if self.isHidden(): self.showNormal() elif self.isVisible(): self.hide() def restorebridgePanel(self, reason): if reason == QSystemTrayIcon.Trigger: if self.isVisible(): self.hide() elif self.isHidden(): self.showNormal() def close(self): super(bridgePanel, self).close() self.v2rayshellTrayIcon.hide() self.onv2raycoreStop() def closeEvent(self, event): self.close() event.accept() sys.exit(self.app.exec_()) def onv2raycoreStop(self): if (self.runv2raycore): self.runv2raycore.stop.emit() try: # force stop checking proxy time lag del self.autoCheckTimer except Exception: pass def onupdateinstallFinishedstartNewV2raycore(self): self.onloadV2rayshellConfigFile(init=True) self.onv2raycoreStart() def onv2raycoreStart(self): currentActiveRow = False rowCount = self.tableWidgetBridge.rowCount() for i in range(rowCount): currentActiveRow = self.tableWidgetBridge.cellWidget(i, 0) if currentActiveRow.isChecked(): self.texteditBridge.clear() option = self.tableWidgetBridge.item(i, 2) if option: option = '-config="{}" -format=json'.format(option.text()) else: option = "" filePath = self.bridgetreasureChest.getV2raycoreFilePath() if (not filePath): filePath = "v2ray" self.runv2raycore = runV2raycore.runV2raycore( outputTextEdit=self.texteditBridge, v2rayPath=filePath, v2rayOption=option, bridgetreasureChest=self.bridgetreasureChest) self.runv2raycore.start.emit() self.autocheckProxy(i) break else: del currentActiveRow def autocheckProxy(self, row): # TODO """ Frequent access to the server may cause suspicion of DDOS attacks, which may put the VPS server at risk. """ enableAutoCheck = self.bridgetreasureChest.getConnectionEnable() if (enableAutoCheck): self.proxyStatus = proxyTest.proxyStatus() self.autoCheckTimer = QTimer() invervalTime = self.bridgetreasureChest.getConnectioninterval() timeout = self.bridgetreasureChest.getConnectiontimeout() proxyAddress = self.getProxyAddressFromTableWidget(row) if proxyAddress: self.autoCheckTimer.timeout.connect( lambda: self.startCheckProxy(timeout=timeout, proxyAddress=proxyAddress, row=row, proxyStatus=self.proxyStatus)) self.bridgetreasureChest.setProxy(proxyAddress) if v2rayshellDebug: self.autoCheckTimer.start(6000) else: self.autoCheckTimer.start(1000 * invervalTime) self.autoCheckTimer.singleShot( 100, lambda: self.startCheckProxy(timeout=timeout, proxyAddress=proxyAddress, row=row, proxyStatus=self.proxyStatus)) def setTableWidgetTimelag(self, row, proxyStatus): newlabelTimelag = self.setlabelTimeLagColor(proxyStatus) oldlabelTimelag = self.tableWidgetBridge.cellWidget(row, 4) del oldlabelTimelag self.tableWidgetBridge.setCellWidget(row, 4, newlabelTimelag) self.tableWidgetBridge.resizeColumnsToContents() def startCheckProxy(self, timeout, proxyAddress, row, proxyStatus): if (proxyAddress): proxyStatus.clear() proxyStatus.signal.connect( lambda: self.setTableWidgetTimelag(row, proxyStatus)) self.proxy = proxyTest.proxyTest(proxyprotocol=proxyAddress[0], proxyhostname=proxyAddress[1], proxyhostport=int( proxyAddress[2]), getproxyStatus=proxyStatus, timeout=int(timeout)) def setlabelTimeLagColor(self, proxyStatus=False): labelTimeLag = QLabel() if (proxyStatus and not proxyStatus.getProxyError()): labelFont = QFont() labelFont.setPointSize(12) labelFont.setBold(True) labelTimeLag.setFont(labelFont) forestGreen = "QLabel {color: rgb(34, 139, 34)}" darkOrange = "QLabel {color: rgb(255, 140, 0)}" red = "QLabel {color: rgb(194,24,7)}" if (proxyStatus.getElapsedTime() < 260): labelTimeLag.setStyleSheet(forestGreen) elif (proxyStatus.getElapsedTime() > 420): labelTimeLag.setStyleSheet(red) else: labelTimeLag.setStyleSheet(darkOrange) labelTimeLag.setText("{} ms".format( str(proxyStatus.getElapsedTime()))) return labelTimeLag elif (proxyStatus and proxyStatus.getProxyError()): labelTimeLag.setText("{}:{}".format( proxyStatus.getProxyErrorString(), proxyStatus.getProxyErrorCode())) self.proxyTryConnect.trytimesDecrease() return labelTimeLag def swapNextConfigFile(self): self.onv2raycoreStop() try: self.trytimes = self.bridgetreasureChest.getConnectiontrytimes() self.interval = self.bridgetreasureChest.getConnectioninterval() self.proxyTryConnect.stopperiodicCheckProxyStatus() if v2rayshellDebug: self.proxyTryConnect.setresetTime(6, 3) else: self.proxyTryConnect.setresetTime(self.interval, self.trytimes) except Exception: self.proxyTryConnect.setresetTime(60, 3) if (self.bridgetreasureChest.connectionisSwitch()): # swap next row's configFile buttons = self.radioButtonGroup.buttons() buttonsNumber = len(buttons) activeRow = False for i in range(buttonsNumber): if buttons[i].isChecked(): buttons[i].setChecked(False) if i == buttonsNumber - 1: buttons[0].setChecked(True) activeRow = 0 break else: buttons[i + 1].setChecked(True) activeRow = i + 1 break # change the row icons for i in range(buttonsNumber): widget = self.tableWidgetBridge.cellWidget(i, 0) if (widget): widget.setIcon(self.iconStop) widget.setIconSize(self.__iconSize) if (widget.isChecked()): pass widget = self.tableWidgetBridge.cellWidget(activeRow, 0) if widget: widget.setIcon(self.iconStart) widget.setIconSize(self.__iconSize) self.onv2raycoreStart() def onopenv2rayupdatePanel(self): currentActiveRow = False rowCount = self.tableWidgetBridge.rowCount() currentRow = False for i in range(rowCount): currentActiveRow = self.tableWidgetBridge.cellWidget(i, 0) if currentActiveRow.isChecked(): currentRow = i break if (currentActiveRow and currentActiveRow.isChecked()): proxy = self.tableWidgetBridge.item(currentRow, 3) proxy = proxy.text().split(":") protocol = QNetworkProxy.Socks5Proxy if (proxy[0] == "socks"): protocol = QNetworkProxy.Socks5Proxy elif (proxy[0] == "http"): protocol = QNetworkProxy.HttpProxy hostName = proxy[1] hostPort = int(proxy[2]) v2rayAPI = updatePanel.v2rayAPI() self.createupdatePanel = updatePanel.v2rayUpdatePanel( v2rayapi=v2rayAPI, protocol=protocol, proxyhostName=hostName, port=hostPort, bridgetreasureChest=self.bridgetreasureChest) self.createupdatePanel.createPanel() self.createupdatePanel.setAttribute(Qt.WA_DeleteOnClose) self.createupdatePanel.setWindowIcon(self.iconStart) self.createupdatePanel.setWindowTitle( self.translate("bridgePanel", "Check V2Ray-core update")) self.createupdatePanel.resize(QSize(1024, 320)) self.createupdatePanel.move( QApplication.desktop().screen().rect().center() - self.createupdatePanel.rect().center()) self.createupdatePanel.show() self.createupdatePanel.exec_() else: self.noPoxyServerRunning() def ontableWidgetBridgeRightClicked(self, pos): index = self.tableWidgetBridge.indexAt(pos) clickedRow.rightClickedRow = index.row() clickedRow.mousePos = QCursor().pos() self.popMenu.move(QCursor().pos()) self.popMenu.show() def ontableWidgetBridgecellDoubleClicked(self, row, column): if (column == 1): hostName, ok = QInputDialog.getText( self, self.translate("bridgePanel", 'Host Name'), self.translate("bridgePanel", 'Enter Host Name:')) if (ok): self.tableWidgetBridge.setItem(row, column, QTableWidgetItem(str(hostName))) self.tableWidgetBridge.resizeColumnsToContents() elif (column == 2): fileNames = self.onopenV2rayConfigJSONFile() if (fileNames): for fileName in fileNames: self.tableWidgetBridge.setItem( row, column, QTableWidgetItem(str(fileName))) self.tableWidgetBridge.resizeColumnsToContents() elif (column == 3): self.onproxyserverTimeLagTest() elif (column == 4): self.onproxyserverTimeLagTest() def getProxyAddressFromTableWidget(self, row): proxy = self.tableWidgetBridge.item(row, 3) try: proxy = proxy.text().split(":") except Exception: return False if (proxy[0] == "socks"): proxy[0] = QNetworkProxy.Socks5Proxy elif (proxy[0] == "http"): proxy[0] = QNetworkProxy.HttpProxy if len(proxy) < 3: return False else: return proxy def onproxyserverTimeLagTest(self): proxyStatus = proxyTest.proxyStatus() """ right clicked mouse button pop a menu check proxy """ currentActiveRow = False rowCount = self.tableWidgetBridge.rowCount() currentRow = False for i in range(rowCount): currentActiveRow = self.tableWidgetBridge.cellWidget(i, 0) if currentActiveRow.isChecked(): currentRow = i break if (currentActiveRow and currentActiveRow.isChecked()): proxy = self.getProxyAddressFromTableWidget(currentRow) if not proxy: return protocol = proxy[0] hostName = proxy[1] hostPort = int(proxy[2]) proxy = proxyTest.proxyTestPanel(proxyhostname=hostName, proxyhostport=hostPort, proxyprotocol=protocol, getproxyStatus=proxyStatus) proxy.createproxyTestPanel() proxy.setAttribute(Qt.WA_DeleteOnClose) proxy.setWindowTitle( self.translate("bridgePanel", "Proxy Time Lag Check")) proxy.setWindowIcon(self.iconStart) proxy.resize(QSize(600, 480)) proxy.move(QApplication.desktop().screen().rect().center() - proxy.rect().center()) proxy.show() proxy.exec_() else: self.noPoxyServerRunning() def noPoxyServerRunning(self): warningPanel = QDialog() warningPanel.setAttribute(Qt.WA_DeleteOnClose) warningPanel.setWindowTitle(self.translate("bridgePanel", "Warnnig...")) warningPanel.setWindowIcon(self.iconStop) labelMsg = QLabel( self.translate( "bridgePanel", "There no any server is running, \n[File]->[Add V2Ray-core Config File] (Ctrl+n) add a config.json." )) vbox = QVBoxLayout() vbox.addWidget(labelMsg) warningPanel.setLayout(vbox) warningPanel.move(QApplication.desktop().screen().rect().center() - warningPanel.rect().center()) warningPanel.open() warningPanel.exec_() def getProxyAddressFromJSONFile(self, filePath): from bridgehouse.editMap.port import treasureChest, openV2rayJSONFile tempTreasureChest = treasureChest.treasureChest() openV2rayJSONFile.openV2rayJSONFile( filePath, tempTreasureChest, disableLog=True).initboundJSONData() inbound = tempTreasureChest.getInbound() if (inbound): try: protocol = inbound["protocol"] ipAddress = inbound["listen"] port = inbound["port"] if (protocol == "socks" or protocol == "http"): return "{}:{}:{}".format(protocol, ipAddress, port) else: return False except Exception: return False else: return False def onradioButtonClicked(self, e): rowCount = self.tableWidgetBridge.rowCount() # radioButtonClickedRow = 0 for i in range(rowCount): widget = self.tableWidgetBridge.cellWidget(i, 0) if (widget): widget.setIcon(self.iconStop) widget.setIconSize(self.__iconSize) if (widget.isChecked()): # radioButtonClickedRow = i pass e.setIcon(self.iconStart) e.setIconSize(self.__iconSize) def onloadV2rayshellConfigFile(self, init=False): """ when the script first start, and auto load v2ray-shell config file. """ if init: self.settingv2rayshelltableWidget() else: def openV2rayshellConfigFile(): options = QFileDialog.Options() filePath, _ = QFileDialog.getOpenFileName( self, self.translate("bridgePanel", "Open V2Ray-sehll Config File"), "", "V2Ray-shell config file (*.v2rayshell)", options=options) if (filePath): self.bridgetreasureChest.clear() self.tableWidgetBridge.setRowCount(0) self.bridgetreasureChest.initbridgeJSONData( v2rayshellConfigFileName=filePath) self.settingv2rayshelltableWidget() openV2rayshellConfigFile() def onopenV2rayConfigJSONFile(self): """ open a new v2ray config file to tabelWidget """ options = QFileDialog.Options() filePaths, _ = QFileDialog.getOpenFileNames( self, self.translate("bridgePanel", "Open V2Ray-core Config File"), "", """ V2Ray config file (*.json);; All File (*);; """, options=options) if (filePaths): return filePaths else: return False def createBridgepreferencesPanel(self): self.createpreferencesPanel = bridgePreference.bridgepreferencesPanel( self.bridgetreasureChest) self.createpreferencesPanel.setAttribute(Qt.WA_DeleteOnClose) self.createpreferencesPanel.createpreferencesPanel() self.createpreferencesPanel.setWindowIcon(self.iconStart) self.createpreferencesPanel.move( QApplication.desktop().screen().rect().center() - self.createpreferencesPanel.rect().center()) self.createpreferencesPanel.open() self.createpreferencesPanel.exec_() def settingv2rayshelltableWidget(self): v2rayConfigFiles = self.bridgetreasureChest.getV2raycoreconfigFiles() if not v2rayConfigFiles: return v2rayConfigFilesNumber = len(v2rayConfigFiles) if (v2rayConfigFilesNumber): self.tableWidgetBridge.setRowCount(0) for i in range(v2rayConfigFilesNumber): try: enable = bool(v2rayConfigFiles[i]["enable"]) hostName = str(v2rayConfigFiles[i]["hostName"]) configFileName = str(v2rayConfigFiles[i]["configFileName"]) except Exception: pass radioButtonStopStart = QRadioButton(self) radioButtonStopStart.setIcon( self.iconStop if not enable else self.iconStart) radioButtonStopStart.setChecked(True if enable else False) radioButtonStopStart.setIconSize(self.__iconSize) self.radioButtonGroup.addButton(radioButtonStopStart) self.tableWidgetBridge.setRowCount(i + 1) self.tableWidgetBridge.setCellWidget(i, 0, radioButtonStopStart) self.tableWidgetBridge.setItem(i, 1, QTableWidgetItem(hostName)) self.tableWidgetBridge.setItem( i, 2, QTableWidgetItem(configFileName)) self.tableWidgetBridge.setItem( i, 3, QTableWidgetItem( self.getProxyAddressFromJSONFile(configFileName))) self.tableWidgetBridge.resizeColumnsToContents() #self.tableWidgetBridge.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) def onsaveV2rayshellConfigFile(self): self.bridgetreasureChest.clearconfigFiles() rowCount = self.tableWidgetBridge.rowCount() for i in range(rowCount): enable = self.tableWidgetBridge.cellWidget(i, 0) if enable and enable.isChecked(): enable = True else: enable = False hostName = self.tableWidgetBridge.item(i, 1) if hostName: hostName = hostName.text() else: hostName = "" config = self.tableWidgetBridge.item(i, 2) if config: config = config.text() else: config = "" self.bridgetreasureChest.setV2raycoreconfigFiles( enable, hostName, configFileName=config) self.bridgetreasureChest.save.emit() def oncreatenauticalChartPanel(self): v2rayConfigFileName = self.tableWidgetBridge.item( clickedRow.rightClickedRow, 2) if (v2rayConfigFileName): nc = nauticalChartPanel.nauticalChartPanel( v2rayConfigFileName.text()) nc.setAttribute(Qt.WA_DeleteOnClose) nc.createPanel() nc.setWindowTitle( self.translate("bridgePanel", "V2Ray config file edit")) nc.setWindowIcon(self.iconStart) nc.setGeometry(0, 0, 1024, 768) # move widget to center nc.move(QApplication.desktop().screen().rect().center() - nc.rect().center()) nc.show() nc.exec_() def tableWidgetBridgeAddNewV2rayConfigFile(self): configFileNames = self.onopenV2rayConfigJSONFile() if (configFileNames): for configFileName in configFileNames: rowCount = self.tableWidgetBridge.rowCount() radioButtonStopStart = QRadioButton(self) radioButtonStopStart.setIcon(self.iconStop) radioButtonStopStart.setIconSize(self.__iconSize) self.radioButtonGroup.addButton(radioButtonStopStart) self.tableWidgetBridge.setRowCount(rowCount + 1) self.tableWidgetBridge.setCellWidget(rowCount, 0, radioButtonStopStart) self.tableWidgetBridge.setItem(rowCount, 1, QTableWidgetItem("")) self.tableWidgetBridge.setItem( rowCount, 2, QTableWidgetItem(configFileName)) self.tableWidgetBridge.setItem( rowCount, 3, QTableWidgetItem( self.getProxyAddressFromJSONFile(configFileName))) self.tableWidgetBridge.resizeColumnsToContents() else: pass def tableWidgetBridgeDelete(self): self.tableWidgetBridge.removeRow(clickedRow.rightClickedRow) def validateV2rayJSONFile(self, JSONData): """ simply validate a V2Ray json file. """ try: JSONData["inbound"] JSONData["outbound"] except KeyError: return False else: return True def about(self): NineteenEightySeven = QLabel( self.translate( "bridgePanel", """Across the Great Wall, we can reach every corner in the world.""" )) ### Crossing the Great Wall to Join the World Timeless = QLabel( self.translate( "bridgePanel", """You weren't thinking about that when you were creating it.\nBecause if you did? You never would have gone through with it.""" )) DwayneRichardHipp = QLabel( self.translate( "bridgePanel", """May you do good and not evil.\nMay you find forgiveness for yourself and forgive others.\nMay you share freely, never taking more than you give.""" )) vbox = QVBoxLayout() vbox.addWidget(NineteenEightySeven) vbox.addWidget(Timeless) vbox.addWidget(DwayneRichardHipp) dialogAbout = QDialog() dialogAbout.setAttribute(Qt.WA_DeleteOnClose) dialogAbout.setWindowTitle( self.translate("bridgePanel", "About V2Ray-shell")) dialogAbout.setWindowIcon(self.iconStart) dialogAbout.move(QApplication.desktop().screen().rect().center() - dialogAbout.rect().center()) dialogAbout.setLayout(vbox) dialogAbout.open() dialogAbout.exec_() def bugReportPanel(self): self.bugReport = bugReport.bugReport() self.bugReport.setAttribute(Qt.WA_DeleteOnClose) self.bugReport.setWindowTitle( self.translate("bridgePanel", "Bug Report")) self.bugReport.setWindowIcon(self.iconStart) self.bugReport.createPanel() self.bugReport.show() self.bugReport.setGeometry(250, 150, 1024, 768)
class mainInterface(QWidget): # 定义信号 processFinished = pyqtSignal(dict) def __init__(self): """ 初始化 :return: null """ # 超类初始化 super().__init__() # UI初始化 self.ui = Ui_mainWidget() self.ui.setupUi(self) self.grabKeyboard() self.setMouseTracking(True) self.setWindowFlags(Qt.FramelessWindowHint) self.setWindowIcon(QIcon('OCR.ico')) # 初始化相机 self.camera = QCamera() self.imageCapture = QCameraImageCapture(self.camera) self.viewsetting = QCameraViewfinderSettings() self.initimplement() # 初始化标题栏 self.initTitleBar() # 初始化系统托盘 self.tray = QSystemTrayIcon() self.tray.setIcon(QIcon('OCR.ico')) self.initTray() # OCR识别部分 self.OCR = ocr() self.OCR.setappid('1257206643') self.OCR.setsecretid('AKIDFTddWEg9Ncsz0sE7oOpBNOExdDdeCUJ3') self.OCR.setsecretkey('FQitsgUND8yfrZK0RrBMOJB5tWhCm5Ol') # 初始化登录部分 self.logWidget = QWidget() self.logui = Ui_Form() self.logui.setupUi(self.logWidget) self.logWidget.setWindowFlags(Qt.FramelessWindowHint) self.logWidget.setWindowModality(Qt.ApplicationModal) self.logui.close_btn.clicked.connect(self.logWidget.close) # 初始化变量 self.mousePressd = False self.mousePoint = None self.result = {} self.isFirst = False self.ocrType = ocrType.ocr_general # 默认为印刷体识别 # 初始化字定义信号连接 self.processFinished.connect(self.updateOCRInfo) self.ui.btn_login.clicked.connect(self.logWidget.show) self.ui.comboBox_choose.currentIndexChanged.connect(self.changeocrType) def initTitleBar(self): """ 初始化标题栏 :return: null """ self.ui.frame.setStyleSheet( "QFrame#frame{background-color:rgb(244, 76, 76);}") self.ui.label_logo.setStyleSheet( "QLabel{border-image:url(./image/ocr.png);}") self.ui.label_title.setStyleSheet( "QLabel{border-image:url(./image/iOCR.png);}") self.ui.comboBox_choose.setStyleSheet( "QComboBox {border-radius:15px;border: 2px solid #4AFFF4;font-family:'楷体';font-size:20px;}" "QComboBox QAbstractItemView::item{height:50px;width:200px;}" "QComboBox::down-arrow{image: url(./image/arrow.png);width:25px;height:25px;}" "QComboBox::drop-down {subcontrol-origin: padding;subcontrol-position:top right;border:none;margin-right:30px;}" "QComboBox::down-arrow:hover{image:url(./image/arrow_hover.png);width:25px;height:25px;}" "QComboBox::down-arrow:on {top: 1px;left: 1px;}") self.ui.comboBox_choose.insertItem(0, ' 印刷体识别') self.ui.comboBox_choose.insertItem(1, ' 手写体识别') self.ui.comboBox_choose.insertItem(2, ' 身份证识别') self.ui.comboBox_choose.insertItem(3, ' 名片识别') self.ui.comboBox_choose.insertItem(4, ' 银行卡识别') self.ui.btn_login.setStyleSheet( "QPushButton{border-image:url(./image/default-portrait.png);}") self.ui.btn_setting.setStyleSheet( "QPushButton{border-image:url(./image/settings.png);}" "QPushButton:hover{border-image:url(./image/qcconfig-hover.png);}") self.ui.btn_min.setStyleSheet( "QPushButton{border-image:url(./image/mini_new.png);}" "QPushButton:hover{border-image:url(./image/mini_hover_new.png);}") self.ui.btn_close.setStyleSheet( "QPushButton{border-image:url(./image/close.png);}" "QPushButton:hover{border-image:url(./image/close-hover.png);}") self.ui.checkBox_cam.setStyleSheet( "QCheckBox{spacing: 5px;font-size: 24px;vertical-align:middle}" "QCheckBox::indicator { width: 45px;height: 45px;}" "QCheckBox::indicator::unchecked {image: url(./image/close_cam.png);}" "QCheckBox::indicator::checked { image: url(./image/open_cam.png);}" ) self.ui.captureBtn.setStyleSheet( "QPushButton{border-style: outset;border-width: 2px;border-color: rgb(82,215,100);border-radius: 5px;font-size: 24px;}" "QPushButton:pressed{background-color: rgb(176,215,181);border-style: inset;}" ) self.ui.checkBox_cam.setChecked(True) self.ui.btn_close.clicked.connect(lambda: qApp.quit()) self.ui.btn_min.clicked.connect(self.miniToTray) self.ui.checkBox_cam.stateChanged.connect(self.camControl) def initTray(self): """ 初始化系统托盘信息 :return: """ tray_menu = QMenu() restoreAction = QAction('&Show', self) quitAction = QAction('&Quit', self) tray_menu.addAction(restoreAction) tray_menu.addAction(quitAction) self.tray.setContextMenu(tray_menu) restoreAction.triggered.connect(self.trayActivatedEvent) quitAction.triggered.connect(qApp.quit) self.tray.activated.connect(self.trayActivatedEvent) def initimplement(self): """ 初始化实现端口 :return: ui """ camInfo = QCameraInfo(self.camera) if camInfo.defaultCamera().isNull(): QMessageBox.warning(self, 'Warning', 'No available camera!', QMessageBox.Ok) return -1 else: self.ui.caputurePhoto.setText(camInfo.description()) self.camera.setViewfinder(self.ui.cameraShow) self.camera.setCaptureMode(QCamera.CaptureStillImage) self.camera.load() resolution = self.camera.supportedViewfinderResolutions() if len(resolution) != 0: if QSize(640, 480) in resolution: self.viewsetting.setResolution(QSize(640, 480)) elif QSize(640, 360) in resolution: self.viewsetting.setResolution(QSize(640, 360)) else: self.viewsetting.setResolution(resolution[0]) self.camera.setViewfinderSettings(self.viewsetting) # ------------------------------Note-------------------------------- # 此种方法利用摄像头准备捕捉图像的状态来进行捕捉图像,readyForCapture # 为true时,才进行捕捉图像,详见下面捕捉函数槽函数。这种方法将进行不 # 停的捕捉,将每次捕捉的相邻图像进行图像相似度判断。当图像相似度低于 # 阈值时,认定为新图像,才进行OCR识别。否则仅捕捉图像而不进行识别。当 # 然捕捉图像速度过于太快时,可以用定时器,每隔0.5秒,去检查readyFor # Capture状态位,进而有效控制程序资源。 # 本应用中采用按键捕捉方式,非自动,详见下面按键捕捉事件 # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ # self.imageCapture.readyForCaptureChanged.connect(self.captureimage) self.camera.start() self.ui.caputurePhoto.setScaledContents(True) self.imageCapture.setCaptureDestination( QCameraImageCapture.CaptureToBuffer) self.imageCapture.imageCaptured.connect(self.displayimage) self.ui.captureBtn.clicked.connect(self.openDlg) def displayimage(self, num, image): """ 显示图形 :param num: number :param image: image :return: null """ self.ui.caputurePhoto.setPixmap(QPixmap.fromImage(image)) self.ui.ocrInfo.setText('识别中......') t = threading.Thread(target=self.ocrForImage, args=(image, )) t.start() # def captureimage(self, state): # """ # 捕捉图像槽函数 # :state: 状态变量 # :return: null # """ # if state is True: # self.camera.searchAndLock() # self.imageCapture.capture() # self.camera.unlock() # def saveimage(self): # """ # 保存图像槽函数 # :return: null # """ # pix = QPixmap(self.ui.caputurePhoto.pixmap()) # if pix: # pix.save(r'D://1.png', 'PNG') # QMessageBox.information(self, 'Message', 'Capture Successfully', QMessageBox.Ok) def ocrForImage(self, image): """ 为截取的图片进行ocr识别 :param image: QImage :return: null """ # Note:子线程里不能对ui界面做改动,ui界面修改只能在主线程中修改,下面注释的做法是错误的 # self.ui.ocrInfo.setText('识别中......') byte = QByteArray() buffer = QBuffer(byte) buffer.open(QIODevice.WriteOnly) image.save(buffer, 'PNG') if self.ocrType == ocrType.ocr_general: self.result = self.OCR.client.general_detect( CIBuffers([byte.data()])) elif self.ocrType == ocrType.ocr_handwriting: self.result = self.OCR.client.handwriting_detect( CIBuffers([byte.data()])) elif self.ocrType == ocrType.ocr_idcard: self.result = self.OCR.client.idcard_detect( CIBuffers([byte.data()]), 0) elif self.ocrType == ocrType.ocr_namecard: self.result = self.OCR.client.namecard_detect( CIBuffers([byte.data()]), 0) elif self.ocrType == ocrType.ocr_bankcard: self.result = self.OCR.client.bankcard_detect( CIBuffers([byte.data()])) else: pass self.processFinished.emit(self.result) def updateOCRInfo(self, res): """ 将ocr识别结果显示在信息框中 :param res: :return: """ if self.ocrType == ocrType.ocr_general or self.ocrType == ocrType.ocr_handwriting: if res['code'] == 0 and res['message'] == 'OK': self.ui.ocrInfo.setText('OK') ocrInfo = [] for i in range(len(self.result['data']['items'])): ocrInfo.append( self.result['data']['items'][i]['itemstring']) self.ui.ocrInfo.setText(''.join(ocrInfo)) else: self.ui.ocrInfo.setText('识别失败!') elif self.ocrType == ocrType.ocr_bankcard: if res['code'] == 0 and res['message'] == 'OK': self.ui.ocrInfo.setText('OK') ocrInfo = [] for i in range(len(self.result['data']['items'])): ocrInfo.append(self.result['data']['items'][i]['item']) ocrInfo.append(':') ocrInfo.append( self.result['data']['items'][i]['itemstring']) ocrInfo.append('\n') self.ui.ocrInfo.setText(''.join(ocrInfo)) else: self.ui.ocrInfo.setText('识别失败!') elif self.ocrType == ocrType.ocr_idcard: if res['result_list'][0]['code'] == 0 and res['result_list'][0][ 'message'] == 'OK': self.ui.ocrInfo.setText('OK') ocrInfo = [] ocrInfo_keys = list( self.result['result_list'][0]['data'].keys()) ocrInfo_values = list( self.result['result_list'][0]['data'].values()) for i in range( len(self.result['result_list'][0]['data']) // 2): ocrInfo.append(ocrInfo_keys[i]) ocrInfo.append(':') ocrInfo.append(ocrInfo_values[i]) ocrInfo.append('\n') self.ui.ocrInfo.setText(''.join(ocrInfo)) else: self.ui.ocrInfo.setText('识别失败!') elif self.ocrType == ocrType.ocr_namecard: if res['result_list'][0]['code'] == 0 and res['result_list'][0][ 'message'] == 'OK': self.ui.ocrInfo.setText('OK') ocrInfo = [] for i in range(len(self.result['result_list'][0]['data'])): ocrInfo.append( self.result['result_list'][0]['data'][i]['item']) ocrInfo.append(':') ocrInfo.append( self.result['result_list'][0]['data'][i]['value']) ocrInfo.append('\n') self.ui.ocrInfo.setText(''.join(ocrInfo)) else: self.ui.ocrInfo.setText('识别失败!') else: pass def camControl(self, state): """ 槽函数 控制相机开关 :param state: checkbox开关状态 :return:null """ if state == Qt.Unchecked: self.ui.cameraShow.setUpdatesEnabled(False) elif state == Qt.Checked: self.ui.cameraShow.setUpdatesEnabled(True) else: return -1 def miniToTray(self): """ 槽函数 最小化到系统托盘 :return:null """ if not self.tray.isVisible(): self.tray.show() if self.tray.isVisible(): if self.isFirst is False: QMessageBox.information( self, "Systray", "The program will keep running in the " "system tray. To terminate the program, " "choose <b>Quit</b> in the context menu " "of the system tray entry.") self.isFirst = True self.hide() def trayActivatedEvent(self, reason): """ 槽函数 响应点击托盘图标 :param reason: 响应原因 :return: null """ if reason == QSystemTrayIcon.Context: pass else: self.tray.hide() self.show() def openDlg(self): """ 槽函数 打开对话框选取文件 :return:文件名 """ filename, filetype = QFileDialog.getOpenFileName( self, '选取图片', path.expanduser('~'), "Image Files (*.png *.jpg *.bmp)") if filename: if QFile(filename).size() >= 6291456: QMessageBox.information(self, '打开图片', '选择图片大于6MB大小,暂不支持识别,请重新选择。') self.openDlg() else: self.displayimage(0, QImage(filename)) def keyPressEvent(self, e): """ 槽函数 键盘按键响应事件 :param e: 按键事件 :return: null """ if e.key() == Qt.Key_Space: if self.imageCapture.isReadyForCapture(): self.camera.searchAndLock() self.imageCapture.capture() self.camera.unlock() def mouseMoveEvent(self, e): """ 槽函数 定义鼠标移动事件 :param e: QMouseEvent :return: null """ if (e.buttons() == Qt.LeftButton) and self.mousePressd: self.move(e.globalPos() - self.mousePoint) e.accept() def mousePressEvent(self, e): """ 槽函数 定义鼠标按下事件 :param e: QMouseEvent :return: null """ if e.button() == Qt.LeftButton: self.mousePressd = True self.mousePoint = e.globalPos() - self.pos() e.accept() def mouseReleaseEvent(self, e): """ 槽函数 定义鼠标松开事件 :param e: QMouseEvent :return: null """ self.mousePressd = False def changeocrType(self, index): """ 槽函数 改变ocr识别类型 :param index: int :return: null """ if index == 0: self.ocrType = ocrType.ocr_general elif index == 1: self.ocrType = ocrType.ocr_handwriting elif index == 2: self.ocrType = ocrType.ocr_idcard elif index == 3: self.ocrType = ocrType.ocr_namecard elif index == 4: self.ocrType = ocrType.ocr_bankcard else: pass
class MainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) # 定义主界面 self.setWindowIcon(QIcon('.\\clip.ico')) # 主窗口的默认有右键菜单,默认的右键菜单不满足要求,因此采用以下语句停用 self.setContextMenuPolicy(Qt.NoContextMenu) self.setEnabled(True) self.setWindowFlags(Qt.FramelessWindowHint) self.setStyleSheet('QMainWindow { background-color: #F0F0F0 }' 'QWidget { font-family: \'Microsoft YaHei UI\' }') # 桌面控件 desktop_widget = QApplication.desktop() desk_height = desktop_widget.height() desk_width = desktop_widget.width() self.window_width = 400 self.window_height = 300 self.setGeometry(int((desk_width - self.window_width) / 2), int((desk_height - self.window_height) / 2), self.window_width, self.window_height) # self.setWindowState(Qt.WindowMaximized) self.setMinimumSize(QSize(self.window_width, self.window_height)) # 获得系统得剪切板 self.clip_board = QApplication.clipboard() # 连接数据库 self.db_conn = sqlite3.connect('clip_board_database.db') self.db_cursor = self.db_conn.cursor() # 是否置顶 self.is_top = False # 是否移动 self.on_move = False # 窗口状态 self.window_status = '' # 记录鼠标的世界坐标 self.mouse_start_point = None # 记录窗体的形状数据 self.window_geo = None # 托盘图标 self.tray_icon = QSystemTrayIcon(QIcon('.\\clip.ico'), self) self.tray_icon.setToolTip('自定义粘贴板') # 托盘菜单 self.tray_icon_menu = QMenu(self) restore_action = QAction('还原', self) restore_action.triggered.connect(self.showNormal) quit_action = QAction('退出', self) quit_action.triggered.connect(QApplication.quit) self.tray_icon.activated.connect(self.restore_win) self.tray_icon_menu.addActions([restore_action, quit_action]) self.tray_icon.setContextMenu(self.tray_icon_menu) self.tray_icon.show() # 布局,创建子控件 self.central_widget = QWidget(self) self.v_layout_main_window = QVBoxLayout(self.central_widget) self.v_layout_main_window.setContentsMargins(0, 0, 0, 0) self.v_layout_main_window.setSpacing(0) self.widget_title = QWidget(self.central_widget) self.horizontalLayout = QHBoxLayout(self.widget_title) spacer_item = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacer_item) self.horizontalLayout.setSpacing(0) self.tool_button_top = QToolButton(self.central_widget) self.tool_button_top.setIcon(QIcon('.\\top.ico')) self.tool_button_top.setIconSize(QSize(20, 20)) self.tool_button_top.setStyleSheet( 'QToolButton { background-color: transparent;' 'height: 20px; width: 20px; }') self.tool_button_top.setFocusPolicy(Qt.NoFocus) self.horizontalLayout.addWidget(self.tool_button_top) # self.tool_button_back = QToolButton(self.central_widget) # self.tool_button_back.setIcon(QIcon('.\\back.ico')) # self.tool_button_back.setIconSize(QSize(20, 20)) # self.tool_button_back.setStyleSheet('QToolButton { background-color: transparent;' # 'height: 20px; width: 20px; }') # self.tool_button_back.setFocusPolicy(Qt.NoFocus) # self.horizontalLayout.addWidget(self.tool_button_back) self.tool_button_min = QToolButton(self.central_widget) self.tool_button_min.setIcon(QIcon('.\\min.ico')) self.tool_button_min.setIconSize(QSize(20, 20)) self.tool_button_min.setStyleSheet( 'QToolButton { background-color: transparent;' 'height: 20px; width: 20px; }') self.tool_button_min.setFocusPolicy(Qt.NoFocus) self.horizontalLayout.addWidget(self.tool_button_min) self.tool_button_max = QToolButton(self.central_widget) self.tool_button_max.setIcon(QIcon('.\\max.ico')) self.tool_button_max.setIconSize(QSize(20, 20)) self.tool_button_max.setStyleSheet( 'QToolButton { background-color: transparent;' 'height: 20px; width: 20px; }') self.tool_button_max.setFocusPolicy(Qt.NoFocus) self.horizontalLayout.addWidget(self.tool_button_max) self.tool_button_exit = QToolButton(self.central_widget) self.tool_button_exit.setIcon(QIcon('.\\close.ico')) self.tool_button_exit.setIconSize(QSize(20, 20)) self.tool_button_exit.setStyleSheet( 'QToolButton { background-color: transparent;' 'height: 20px; width: 20px; }') self.tool_button_exit.setFocusPolicy(Qt.NoFocus) self.horizontalLayout.addWidget(self.tool_button_exit) self.v_layout_main_window.addWidget(self.widget_title) self.v_layout_tree_view = QVBoxLayout() self.v_layout_tree_view.setContentsMargins(9, 0, 9, 9) self.v_layout_tree_view.setSpacing(6) self.h_layout_filter = QHBoxLayout() self.combo_box_group_name = QComboBox(self.central_widget) self.combo_box_group_name.setMinimumSize(QSize(120, 24)) self.combo_box_group_name.setMaximumSize(QSize(120, 24)) self.h_layout_filter.addWidget(self.combo_box_group_name) self.line_edit_key_filter = QLineEdit(self.central_widget) self.line_edit_key_filter.setMinimumSize(QSize(0, 24)) self.line_edit_key_filter.setMaximumSize(QSize(16777215, 24)) self.h_layout_filter.addWidget(self.line_edit_key_filter) self.combo_box_item_click_response = QComboBox(self.central_widget) self.combo_box_item_click_response.setMinimumSize(QSize(90, 24)) self.combo_box_item_click_response.setMaximumSize(QSize(90, 24)) self.combo_box_item_click_response.addItems(['复制', '复制并粘贴', '编辑']) self.h_layout_filter.addWidget(self.combo_box_item_click_response) self.v_layout_tree_view.addLayout(self.h_layout_filter) self.tree_widget_content_view = QTreeWidget(self.central_widget) self.tree_widget_content_view.header().setSectionResizeMode( QHeaderView.Stretch) self.tree_widget_content_view.setColumnCount(2) self.tree_widget_content_view.setSelectionMode( QAbstractItemView.ExtendedSelection) self.v_layout_tree_view.addWidget(self.tree_widget_content_view) self.v_layout_main_window.addLayout(self.v_layout_tree_view) self.setCentralWidget(self.central_widget) self.translate() self.tree_widget_content_view.itemClicked.connect( self.item_click_response) self.combo_box_group_name.currentIndexChanged.connect( self.group_filter) self.line_edit_key_filter.textChanged.connect(self.search_para) self.tool_button_top.clicked.connect(self.top_window) self.tool_button_max.clicked.connect(self.max_window) self.tool_button_min.clicked.connect(self.min_window) self.tool_button_exit.clicked.connect(self.close_window) self.display_clip_board_database() # self.create_clip_board_database() # 添加内容 def add_clip_content(self): add_content = self.clip_board.text() if add_content: result = None try: result = self.db_cursor.execute( '''SELECT KEY FROM CLIPBOARD WHERE CONTENT = \'%s\' ''' % add_content) except sqlite3.OperationalError as error: QMessageBox.information(self, '提示', str(error)) if result: # 判断结果中是否有内容 result_info = '' for row in result: result_info += row[0] + '\n' if not result_info: group_list = [gp for gp in self.get_database_dict()] dialog = CreateContentDialog(self, group_list, add_content) return_signal = dialog.exec_() if return_signal == QDialog.Accepted: group, key, content = dialog.add_content self.add_clip_content_to_db(group, key, content) self.display_clip_board_database() else: result_info = '内容已存在以下关键字中:\n' + result_info QMessageBox.information(self, '提示', result_info) # 添加内容到数据库 def add_clip_content_to_db(self, group, key, content): self.db_cursor.execute( '''INSERT INTO CLIPBOARD (CONTENT_GROUP,KEY,CONTENT) VALUES (\'%s\', \'%s\', \'%s\')''' % (group, key, content)) self.db_conn.commit() def close_window(self): QMainWindow.close(self) # 重载窗口关闭事件 def closeEvent(self, event: QCloseEvent): if self.tray_icon.isVisible(): origin_status_top = False if self.is_top: origin_status_top = True self.top_window() dialog = ExitDialog() dialog.exec_() if dialog.status == 1: QApplication.quit() elif dialog.status == 2: if origin_status_top: self.top_window() self.hide() event.ignore() else: if origin_status_top: self.top_window() event.ignore() # message = QMessageBox.warning(self, '退出', '''<p>确定要退出吗?''', # QMessageBox.Yes | QMessageBox.No) # if message == QMessageBox.Yes: # event.accept() # self.db_conn.close() # else: # event.ignore() def create_clip_board_database(self): self.db_cursor.execute('''CREATE TABLE CLIPBOARD( CONTENT_GROUP TEXT NOT NULL, KEY TEXT NOT NULL, CONTENT TEXT NOT NULL);''') self.db_conn.commit() self.db_cursor.execute( '''INSERT INTO CLIPBOARD (CONTENT_GROUP,KEY,CONTENT) VALUES (\'TEST_GROUP\', \'TEST_KEY\', \'TEST_CONTENT\')''') self.db_conn.commit() # 删除所选的内容 def delete_contents(self): items = self.tree_widget_content_view.selectedItems() if items: message = QMessageBox.warning(self, '删除', '确定要删除所选组和内容吗?', QMessageBox.Yes | QMessageBox.No) if message == QMessageBox.Yes: for item in items: if item.parent(): self.delete_content_in_database( item.parent().text(0), item.text(0), item.text(1)) else: self.delete_group_in_database(item.text(0)) self.display_clip_board_database() # 删除数据库中的一条内容 def delete_content_in_database(self, group, key, content): self.db_cursor.execute('''DELETE FROM CLIPBOARD WHERE CONTENT_GROUP = \'%s\' AND KEY = \'%s\' AND CONTENT = \'%s\'''' % (group, key, content)) self.db_conn.commit() # 删除数据库中的组 def delete_group_in_database(self, group_name): self.db_cursor.execute('''DELETE FROM CLIPBOARD WHERE CONTENT_GROUP = \'%s\'''' % group_name) self.db_conn.commit() # 把数据库内容显示出来 def display_clip_board_database(self): dict_content = self.get_database_dict() self.tree_widget_content_view.clear() self.combo_box_group_name.clear() if dict_content: grouplist = [gp for gp in dict_content] grouplist.insert(0, '全部组') self.combo_box_group_name.addItems(grouplist) for group_name in dict_content: top_item = QTreeWidgetItem(self.tree_widget_content_view) top_item.setText(0, group_name) for key_name, content in dict_content[group_name]: child_item = QTreeWidgetItem(top_item) child_item.setText(0, key_name) child_item.setText(1, content) if self.tree_widget_content_view.topLevelItemCount(): self.tree_widget_content_view.expandAll() # 获得所有的数据存储在字典中 def get_database_dict(self): dict_content = dict() data_rows = self.db_cursor.execute( '''SELECT CONTENT_GROUP,KEY,CONTENT FROM CLIPBOARD''') for row in data_rows: if row[0] not in dict_content: dict_content[row[0]] = list() dict_content[row[0]].append((row[1], row[2])) return dict_content # 按组过滤 def group_filter(self, index): text = self.combo_box_group_name.itemText(index) count = self.tree_widget_content_view.topLevelItemCount() for i in range(count): item = self.tree_widget_content_view.topLevelItem(i) if text == '全部组': item.setHidden(False) else: if text != item.text(0): item.setHidden(True) else: item.setHidden(False) # 设置热键 def keyPressEvent(self, event: QKeyEvent): if event.key() == Qt.Key_V: if event.modifiers() == Qt.ControlModifier: self.add_clip_content() event.accept() if event.key() == Qt.Key_R: if event.modifiers() == Qt.ControlModifier: if self.tree_widget_content_view.topLevelItemCount(): self.tree_widget_content_view.collapseAll() event.accept() if event.key() == Qt.Key_E: if event.modifiers() == Qt.ControlModifier: if self.tree_widget_content_view.topLevelItemCount(): self.tree_widget_content_view.expandAll() event.accept() if event.key() == Qt.Key_Delete: self.delete_contents() else: event.ignore() # 最大化窗口 def max_window(self): if self.isMaximized(): self.showNormal() self.tool_button_max.setIcon(QIcon('.\\max.ico')) else: self.showMaximized() self.tool_button_max.setIcon(QIcon('.\\normal.ico')) # 最小化窗口 def min_window(self): if not self.isMinimized(): self.showMinimized() # 实现窗口拖动 def mousePressEvent(self, event: QMouseEvent): if event.button() == Qt.LeftButton: self.on_move = True self.mouse_start_point = event.globalPos() self.window_geo = self.frameGeometry() if self.mouse_start_point.x() <= self.window_geo.left() + 9: if self.mouse_start_point.y() <= self.window_geo.top() + 9: self.window_status = 'top_left' self.setCursor(Qt.SizeFDiagCursor) elif self.mouse_start_point.y( ) >= self.window_geo.bottom() - 9: self.window_status = 'bottom_left' self.setCursor(Qt.SizeBDiagCursor) else: self.window_status = 'left' self.setCursor(Qt.SizeHorCursor) elif self.mouse_start_point.x() >= self.window_geo.right() - 9: if self.mouse_start_point.y() <= self.window_geo.top() + 9: self.window_status = 'top_right' self.setCursor(Qt.SizeBDiagCursor) elif self.mouse_start_point.y( ) >= self.window_geo.bottom() - 9: self.window_status = 'bottom_right' self.setCursor(Qt.SizeFDiagCursor) else: self.window_status = 'right' self.setCursor(Qt.SizeHorCursor) elif self.mouse_start_point.y() <= self.window_geo.top() + 9: self.window_status = 'top' self.setCursor(Qt.SizeVerCursor) elif self.mouse_start_point.y() >= self.window_geo.bottom() - 9: self.window_status = 'bottom' self.setCursor(Qt.SizeVerCursor) else: self.window_status = 'move' QMainWindow.mousePressEvent(self, event) def mouseMoveEvent(self, event: QMouseEvent): if event.buttons() and Qt.LeftButton and self.on_move: relative_pos = event.globalPos() - self.mouse_start_point if self.window_status == 'left': width = self.window_geo.right() - self.window_geo.left( ) - relative_pos.x() if width > self.window_width: self.setGeometry(self.window_geo.left() + relative_pos.x(), self.window_geo.top(), width, self.window_geo.height()) if self.window_status == 'right': width = self.window_geo.width() + relative_pos.x() if width > self.window_width: self.setGeometry(self.window_geo.left(), self.window_geo.top(), width, self.window_geo.height()) if self.window_status == 'top': height = self.window_geo.bottom() - self.window_geo.top( ) - relative_pos.y() if height > self.window_height: self.setGeometry(self.window_geo.left(), self.window_geo.top() + relative_pos.y(), self.window_geo.width(), height) if self.window_status == 'bottom': height = self.window_geo.height() + relative_pos.y() if height > self.window_height: self.setGeometry(self.window_geo.left(), self.window_geo.top(), self.window_geo.width(), height) if self.window_status == 'top_left': width = self.window_geo.right() - self.window_geo.left( ) - relative_pos.x() height = self.window_geo.bottom() - self.window_geo.top( ) - relative_pos.y() if width > self.window_width and height > self.window_height: self.setGeometry(self.window_geo.left() + relative_pos.x(), self.window_geo.top() + relative_pos.y(), width, height) if self.window_status == 'top_right': width = self.window_geo.width() + relative_pos.x() height = self.window_geo.bottom() - self.window_geo.top( ) - relative_pos.y() if width > self.window_width and height > self.window_height: self.setGeometry(self.window_geo.left(), self.window_geo.top() + relative_pos.y(), width, height) if self.window_status == 'bottom_right': width = self.window_geo.width() + relative_pos.x() height = self.window_geo.height() + relative_pos.y() if width > self.window_width and height > self.window_height: self.setGeometry(self.window_geo.left(), self.window_geo.top(), width, height) if self.window_status == 'bottom_left': width = self.window_geo.right() - self.window_geo.left( ) - relative_pos.x() height = self.window_geo.height() + relative_pos.y() if width > self.window_width and height > self.window_height: self.setGeometry(self.window_geo.left() + relative_pos.x(), self.window_geo.top(), width, height) if self.window_status == 'move': self.move(self.window_geo.topLeft() + relative_pos) QMainWindow.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event: QMouseEvent): if event.button() == Qt.LeftButton: # 改变移动状态 self.window_status = '' self.on_move = False self.mouse_start_point = None self.window_geo = None self.setCursor(Qt.ArrowCursor) QMainWindow.mouseReleaseEvent(self, event) # 单击托盘时的响应 def restore_win(self, reason): if reason == QSystemTrayIcon.Trigger: if self.isHidden(): self.show() self.activateWindow() # 搜索参数并显示在参数窗口里 def search_para(self, key_name): text = self.combo_box_group_name.currentText() index = self.combo_box_group_name.currentIndex() - 1 pattern = re.compile('.*' + key_name + '.*', re.I) if text == '全部组': count = self.tree_widget_content_view.topLevelItemCount() for i in range(count): num_key_in_show = 0 item = self.tree_widget_content_view.topLevelItem(i) child_count = item.childCount() for child_index in range(child_count): kn = item.child(child_index).text(0) if re.match(pattern, kn): item.child(child_index).setHidden(False) num_key_in_show += 1 else: item.child(child_index).setHidden(True) if num_key_in_show == 0: item.setHidden(True) else: item.setHidden(False) else: num_key_in_show = 0 item = self.tree_widget_content_view.topLevelItem(index) child_count = item.childCount() for child_index in range(child_count): kn = item.child(child_index).text(0) if re.match(pattern, kn): item.child(child_index).setHidden(False) num_key_in_show += 1 else: item.child(child_index).setHidden(True) if num_key_in_show == 0: item.setHidden(True) else: item.setHidden(False) self.tree_widget_content_view.expandAll() # 将选中的内容添加到剪贴板中 def item_click_response(self, item): if self.combo_box_item_click_response.currentText() == '编辑': if item.parent(): group_list = [gp for gp in self.get_database_dict()] old_content = (item.parent().text(0), item.text(0), item.text(1)) dialog = EditContentDialog(self, group_list, old_content) return_signal = dialog.exec_() if return_signal == QDialog.Accepted: if old_content != dialog.add_content: old_group, old_key, old_content = old_content group, key, content = dialog.add_content self.db_cursor.execute( '''UPDATE CLIPBOARD SET CONTENT_GROUP = \'%s\', KEY = \'%s\', CONTENT = \'%s\' WHERE CONTENT_GROUP = \'%s\' AND KEY = \'%s\' AND CONTENT = \'%s\'''' % (group, key, content, old_group, old_key, old_content)) self.db_conn.commit() self.display_clip_board_database() elif self.combo_box_item_click_response.currentText() == '复制': if item.parent(): self.clip_board.setText(item.text(1)) elif self.combo_box_item_click_response.currentText() == '复制并粘贴': if item.parent(): self.clip_board.setText(item.text(1)) focus_win_hwnd = xx = win32gui.GetFocus() hwnd_title = dict() def get_all_window(hwnd, mouse): if win32gui.IsWindow(hwnd) and win32gui.IsWindowEnabled(hwnd) and\ win32gui.IsWindowVisible(hwnd): hwnd_title.update({hwnd: win32gui.GetWindowText(hwnd)}) win32gui.EnumWindows(get_all_window, 1) if hwnd_title: hwnd_list = [h for h, t in hwnd_title.items()] index = hwnd_list.index(focus_win_hwnd) if self.is_top: if index < len(hwnd_list) - 2: focus_win_hwnd = hwnd_list[index + 2] win32gui.SetForegroundWindow(focus_win_hwnd) win32api.keybd_event(17, 0, 0, 0) win32api.keybd_event(86, 0, 0, 0) win32api.keybd_event(86, 0, win32con.KEYEVENTF_KEYUP, 0) win32api.keybd_event(17, 0, win32con.KEYEVENTF_KEYUP, 0) win32api.keybd_event(13, 0, 0, 0) win32api.keybd_event(13, 0, win32con.KEYEVENTF_KEYUP, 0) else: if index < len(hwnd_list) - 1: focus_win_hwnd = hwnd_list[index + 1] win32gui.SetForegroundWindow(focus_win_hwnd) win32api.keybd_event(17, 0, 0, 0) win32api.keybd_event(86, 0, 0, 0) win32api.keybd_event(86, 0, win32con.KEYEVENTF_KEYUP, 0) win32api.keybd_event(17, 0, win32con.KEYEVENTF_KEYUP, 0) win32api.keybd_event(13, 0, 0, 0) win32api.keybd_event(13, 0, win32con.KEYEVENTF_KEYUP, 0) else: pass def top_window(self): if self.is_top: self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint | Qt.MSWindowsOwnDC) self.show() self.is_top = False self.tool_button_top.setIcon(QIcon('.\\top.ico')) else: self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.show() self.is_top = True self.tool_button_top.setIcon(QIcon('.\\un_top.ico')) # 汉化 def translate(self): self.setWindowTitle("MainWindow") self.tool_button_top.setToolTip('置顶') # self.tool_button_back.setToolTip('后台运行') self.tool_button_min.setToolTip('最小化') self.tool_button_max.setToolTip('最大化') self.tool_button_exit.setToolTip('关闭') self.line_edit_key_filter.setPlaceholderText("筛选器") self.tree_widget_content_view.headerItem().setText(0, '关键字') self.tree_widget_content_view.headerItem().setText(1, '内容')
class WithTrayIcon(QDialog): user_closed = False def setupSysTray(self): self._createIcons() self._createActions() self._createTrayIcon() self.trayIcon.activated.connect(self.iconActivated) self.setVPNStatus('off') self.setUpEventListener() self.trayIcon.show() def setVPNStatus(self, status): seticon = self.trayIcon.setIcon settip = self.trayIcon.setToolTip # XXX this is an oversimplification, see #9131 # the simple state for failure is off too, for now. if status == 'off': seticon(self.ICON_OFF) settip('VPN: Off') elif status == 'on': seticon(self.ICON_ON) settip('VPN: On') elif status == 'starting': seticon(self.ICON_WAIT) settip('VPN: Starting') elif status == 'stopping': seticon(self.ICON_WAIT) settip('VPN: Stopping') def setUpEventListener(self): leap_events.register(catalog.VPN_STATUS_CHANGED, self._handle_vpn_event) def _handle_vpn_event(self, *args): status = None if len(args) > 1: status = args[1] self.setVPNStatus(status.lower()) def _createIcons(self): self.ICON_WAIT = QIcon(QPixmap(TRAY_ICONS[0])) self.ICON_ON = QIcon(QPixmap(TRAY_ICONS[1])) self.ICON_OFF = QIcon(QPixmap(TRAY_ICONS[2])) def _createActions(self): self.quitAction = QAction("&Quit", self, triggered=self.closeFromSystray) def iconActivated(self, reason): # can use .Trigger also for single click if reason in (QSystemTrayIcon.DoubleClick, ): self.showNormal() def closeFromSystray(self): self.user_closed = True self.close() def _createTrayIcon(self): self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu) def closeEvent(self, event): if self.trayIcon.isVisible() and not self.user_closed: QMessageBox.information( self, "Bitmask", "Bitmask will minimize to the system tray. " "You can choose 'Quit' from the menu with a " "right click on the icon, and restore the window " "with a double click.") self.hide() if not self.user_closed: event.ignore()
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self): super(QMainWindow, self).__init__() # Set up the user interface from Designer. self.setupUi(self) self.setAccessibleName("Hive Desktop") self.redrawLock = Lock() self.updateLock = Lock() self.optionsDialog = dialogs.Options(self) self.aboutDialog = dialogs.About( self, copyright='holger80', programName='Hive Desktop', version=VERSION, website='https://github.com/holgern/hivedesktop', websiteLabel='Github', comments= '"Welcome to Hive desktop!\n This is the first release for testing qt5.\n Please vote for holger80 as witness, if you like this :).', licenseName='GPL-3.0', # licenseUrl=helpers.joinpath_to_cwd('LICENSE').as_uri(), authors=('holger80', ), # dependencies=[l.strip() for l in requirements.readlines()], ) self.mdrenderer = MDRenderer(str(helpers.joinpath_to_cwd('themes'))) # tmpfile = helpers.mktemp(prefix='hivedesktop', suffix='.html') self.post = {"body": "##test", "authorperm": "@test/test"} self.thread = threads.MDThread(self) # self.webview.url = tmpfile.as_uri() self.feedListWidget.currentRowChanged.connect( self.change_displayed_post, Qt.QueuedConnection) self.timer = QTimer() self.timer.timeout.connect(self.refresh_account_thread) self.timer2 = QTimer() self.timer2.timeout.connect(self.update_account_hist_thread) self.timer3 = QTimer() self.timer3.timeout.connect(self.update_account_feed_thread) self.cache_path = QStandardPaths.writableLocation( QStandardPaths.CacheLocation) self.db_type = "shelve" self.feed = [] self.post = None # Get settings settings = QSettings() # Get checkbox state with speciying type of checkbox: # type=bool is a replacement of toBool() in PyQt5 check_state = settings.value(SETTINGS_TRAY, True, type=bool) hist_info_check_state = settings.value(SETTINGS_HIST_INFO, True, type=bool) account_state = settings.value(SETTINGS_ACCOUNT, "", type=str) # Set state self.accountHistNotificationCheckBox.setChecked(hist_info_check_state) self.autoRefreshCheckBox.setChecked(check_state) if check_state: self.timer.start(5000) self.timer2.start(15000) self.timer3.start(60000) self.accountLineEdit.setText(account_state) # connect the slot to the signal by clicking the checkbox to save the state settings self.autoRefreshCheckBox.clicked.connect(self.save_check_box_settings) self.accountHistNotificationCheckBox.clicked.connect( self.save_check_box_settings) self.accountLineEdit.editingFinished.connect( self.save_account_settings) self.actionAbout.triggered.connect(self.about) self.actionOptions.triggered.connect(self.options) self.threadpool = QThreadPool() 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) menu = QMenu() menu.addAction(self.minimizeAction) menu.addAction(self.maximizeAction) menu.addAction(self.restoreAction) menu.addSeparator() # aboutAction = menu.addAction("about") # aboutAction.triggered.connect(self.about) exitAction = menu.addAction("Exit") exitAction.triggered.connect(self.closeApp) self.tray = QSystemTrayIcon(QIcon(':/icons/icon.ico')) self.tray.setContextMenu(menu) self.tray.setToolTip("Hive Desktop!") self.tray.setObjectName("Hive Desktop") self.setWindowTitle("Hive Desktop") self.tray.show() splash_pix = QPixmap(':/icons/splash.png') splash = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint) splash.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint) splash.setEnabled(False) splash.show() splash.showMessage("<h1><font color='green'>starting...</font></h1>", Qt.AlignTop | Qt.AlignCenter, Qt.black) account = account_state nodelist = NodeList() nodelist.update_nodes() self.stm = Steem(node=nodelist.get_nodes(hive=True)) if account != "": try: self.hist_account = Account(account, steem_instance=self.stm) except: self.hist_account = None else: self.hist_account = None if self.hasFocus is not None: self.init_new_account() # self.button.clicked.connect(lambda: self.text.setText(_get_quote(self.hist_account, self.stm))) self.refreshPushButton.clicked.connect(self.refresh_account_thread) self.refreshPushButton.clicked.connect(self.update_account_hist_thread) self.accountLineEdit.editingFinished.connect(self.update_account_info) splash.deleteLater() self.tray.showMessage("Ready", "Account history loaded!") def triggeredPreview(self): self.authorLabel.setText(self.post["author"]) self.titleLabel.setText(self.post["title"]) self.thread.start() @pyqtSlot(str) def cbMDThread(self, html): self.webview.setHtml(html) def closeEvent(self, event): if self.tray.isVisible(): QMessageBox.information( self, "Hive Desktop", "The program will keep running in the system tray. To " "terminate the program, choose <b>Quit</b> in the " "context menu of the system tray entry.") self.hide() event.ignore() def closeApp(self): self.store_account_hist() sys.exit() def about(self): self.aboutDialog.exec_() def options(self): self.optionsDialog.exec_() # Slot checkbox to save the settings def save_check_box_settings(self): settings = QSettings() settings.setValue(SETTINGS_HIST_INFO, self.accountHistNotificationCheckBox.isChecked()) settings.setValue(SETTINGS_TRAY, self.autoRefreshCheckBox.isChecked()) if self.autoRefreshCheckBox.isChecked(): self.timer.start(5000) self.timer2.start(15000) self.timer3.start(60000) else: self.timer.stop() self.timer2.stop() self.timer3.stop() settings.sync() # Slot checkbox to save the settings def save_account_settings(self): settings = QSettings() settings.setValue(SETTINGS_ACCOUNT, self.accountLineEdit.text()) settings.sync() def update_account_info(self): if self.hist_account is None or self.hist_account[ "name"] != self.accountLineEdit.text(): self.store_account_hist() try: self.hist_account = Account(self.accountLineEdit.text(), steem_instance=self.stm) except: self.hist_account = None self.init_new_account() def init_new_account(self): if self.hist_account is not None: self.db = database.Database(self.db_type, self.cache_path, self.hist_account["name"]) self.refresh_account() self.init_account_hist() self.update_account_hist() self.update_account_feed() self.store_account_hist() def refresh_account_thread(self): worker = Worker(self.refresh_account) self.threadpool.start(worker) def refresh_account(self): if self.hist_account is None: return self.hist_account.refresh() self.accountInfoGroupBox.setTitle( "%s (%.3f)" % (self.hist_account["name"], self.hist_account.rep)) with self.redrawLock: self.votePowerProgressBar.setValue(int(self.hist_account.vp)) if self.hist_account.vp == 100: self.votePowerProgressBar.setFormat("%.2f %%" % (self.hist_account.vp)) else: self.votePowerProgressBar.setFormat( "%.2f %%, full in %s" % (self.hist_account.vp, self.hist_account.get_recharge_time_str())) down_vp = self.hist_account.get_downvoting_power() with self.redrawLock: self.downvotePowerProgressBar.setValue(int(down_vp)) if down_vp == 100: self.downvotePowerProgressBar.setFormat("%.2f %%" % (down_vp)) else: self.downvotePowerProgressBar.setFormat( "%.2f %%, full in %s" % (down_vp, self.hist_account.get_recharge_time( starting_voting_power=down_vp))) self.votePowerLabel.setText("Vote Power, a 100%% vote is %.3f $" % (self.hist_account.get_voting_value_SBD())) self.downvotePowerLabel.setText("DownVote Power") self.STEEMLabel.setText(str(self.hist_account["balance"])) self.SBDLabel.setText(str(self.hist_account["sbd_balance"])) self.SPLabel.setText( "%.3f HP" % self.stm.vests_to_sp(self.hist_account["vesting_shares"])) try: rc_manabar = self.hist_account.get_rc_manabar() with self.redrawLock: self.RCProgressBar.setValue(int(rc_manabar["current_pct"])) self.RCProgressBar.setFormat( "%.2f %%, full in %s" % (rc_manabar["current_pct"], self.hist_account.get_manabar_recharge_time_str( rc_manabar))) rc = self.hist_account.get_rc() estimated_rc = int(rc["max_rc"]) * rc_manabar["current_pct"] / 100 rc_calc = RC(steem_instance=self.stm) self.RCLabel.setText( "RC (%.0f G RC of %.0f G RC)" % (estimated_rc / 10**9, int(rc["max_rc"]) / 10**9)) ret = "--- Approx Costs ---\n" ret += "comment - %.2f G RC - enough RC for %d comments\n" % ( rc_calc.comment() / 10**9, int( estimated_rc / rc_calc.comment())) ret += "vote - %.2f G RC - enough RC for %d votes\n" % ( rc_calc.vote() / 10**9, int(estimated_rc / rc_calc.vote())) ret += "transfer - %.2f G RC - enough RC for %d transfers\n" % ( rc_calc.transfer() / 10**9, int(estimated_rc / rc_calc.transfer())) ret += "custom_json - %.2f G RC - enough RC for %d custom_json\n" % ( rc_calc.custom_json() / 10**9, int(estimated_rc / rc_calc.custom_json())) self.text.setText(ret) except: rc_manabar = None def store_account_hist(self): if self.hist_account is None: return self.db.store_account_hist(self.account_history, self.append_hist_info["trx_ids"]) def init_account_hist(self): if self.hist_account is None: return b = Blockchain(steem_instance=self.stm) latest_block_num = b.get_current_block_num() start_block_num = latest_block_num - (20 * 60 * 24) self.account_history = [] self.account_hist_info = {"start_block": 0, "trx_ids": []} self.append_hist_info = {"start_block": 0, "trx_ids": []} if self.db.has_account_hist(): self.account_history, trx_ids = self.db.load_account_hist() if self.account_history[0]["block"] <= start_block_num: self.append_hist_info["start_block"] = self.account_history[ -1]["block"] self.append_hist_info["trx_ids"] = trx_ids return self.lastUpvotesListWidget.clear() self.lastCurationListWidget.clear() self.lastAuthorListWidget.clear() self.accountHistListWidget.clear() start_block = 0 trx_ids = [] for op in self.hist_account.history(start=start_block_num): if op["block"] < start_block: continue elif op["block"] == start_block: if op["trx_id"] in trx_ids: continue else: trx_ids.append(op["trx_id"]) else: trx_ids = [op["trx_id"]] start_block = op["block"] self.account_history.append(op) self.append_hist_info["start_block"] = start_block self.append_hist_info["trx_ids"] = trx_ids def append_account_hist(self): if self.hist_account is None: return start_block = self.append_hist_info["start_block"] trx_ids = self.append_hist_info["trx_ids"] for op in self.hist_account.history(start=start_block - 20, use_block_num=True): if op["block"] < start_block: continue elif op["block"] == start_block: if op["trx_id"] in trx_ids: continue else: trx_ids.append(op["trx_id"]) else: trx_ids = [op["trx_id"]] start_block = op["block"] # print("Write %d" % op["index"]) self.account_history.append(op) self.append_hist_info["start_block"] = start_block self.append_hist_info["trx_ids"] = trx_ids def update_account_hist_thread(self): worker = Worker(self.update_account_hist) self.threadpool.start(worker) def update_account_feed_thread(self): worker = Worker(self.update_account_feed) self.threadpool.start(worker) @pyqtSlot(int) def change_displayed_post(self, row): if row < 0: return if len(self.feed) == 0: return if len(self.feed) <= row: return #index = self.feedListWidget.currentIndex() #row = index.row() self.post = self.feed[row] with self.updateLock: self.triggeredPreview() def update_account_feed(self): if self.hist_account is None: return updated_feed = self.hist_account.get_account_posts() if len(self.feed) == 0: self.feed = updated_feed else: for post in updated_feed[::-1]: found = False for p in self.feed: if post["authorperm"] == p["authorperm"]: found = True if not found: self.feed.insert(0, post) self.tray.showMessage(post["author"], post["title"]) with self.updateLock: if self.post is None: self.post = self.feed[0] self.triggeredPreview() self.feedListWidget.currentRowChanged.disconnect( self.change_displayed_post) self.feedListWidget.clear() for post in self.feed[::-1]: post_text = "%s - %s" % (post["author"], post["title"]) post_item = QListWidgetItem() post_item.setText(post_text) post_item.setToolTip(post["author"]) self.feedListWidget.insertItem(0, post_item) self.feedListWidget.currentRowChanged.connect( self.change_displayed_post, Qt.QueuedConnection) def update_account_hist(self): if self.hist_account is None: return votes = [] daily_curation = 0 daily_author_SP = 0 daily_author_SBD = 0 daily_author_STEEM = 0 self.append_account_hist() new_op_found = False start_block = self.account_hist_info["start_block"] if start_block == 0: first_call = True else: first_call = False trx_ids = self.account_hist_info["trx_ids"] for op in self.account_history: if op["block"] < start_block: # last_block = op["block"] continue elif op["block"] == start_block: if op["trx_id"] in trx_ids: continue else: trx_ids.append(op["trx_id"]) else: trx_ids = [op["trx_id"]] start_block = op["block"] new_op_found = True op_timedelta = formatTimedelta( addTzInfo(datetime.utcnow()) - formatTimeString(op["timestamp"])) op_local_time = formatTimeString(op["timestamp"]).astimezone( tz.tzlocal()) # print("Read %d" % op["index"]) if op["type"] == "vote": if op["voter"] == self.hist_account["name"]: continue if op["weight"] >= 0: self.lastUpvotesListWidget.insertItem( 0, "%s - %s (%.2f %%) upvote %s" % (op_timedelta, op["voter"], op["weight"] / 100, op["permlink"])) hist_item = "%s - %s - %s (%.2f %%) upvote %s" % ( op_local_time, op["type"], op["voter"], op["weight"] / 100, op["permlink"]) tray_item = "%s - %s (%.2f %%) upvote %s" % ( op["type"], op["voter"], op["weight"] / 100, op["permlink"]) else: hist_item = "%s - %s - %s (%.2f %%) downvote %s" % ( op_local_time, op["type"], op["voter"], op["weight"] / 100, op["permlink"]) tray_item = "%s - %s (%.2f %%) downvote %s" % ( op["type"], op["voter"], op["weight"] / 100, op["permlink"]) self.accountHistListWidget.insertItem(0, hist_item) elif op["type"] == "curation_reward": curation_reward = self.stm.vests_to_sp( Amount(op["reward"], steem_instance=self.stm)) self.lastCurationListWidget.insertItem( 0, "%s - %.3f HP for %s" % (op_timedelta, curation_reward, construct_authorperm(op["comment_author"], op["comment_permlink"]))) hist_item = "%s - %s - %.3f HP for %s" % ( op_local_time, op["type"], curation_reward, construct_authorperm(op["comment_author"], op["comment_permlink"])) tray_item = "%s - %.3f HP for %s" % ( op["type"], curation_reward, construct_authorperm(op["comment_author"], op["comment_permlink"])) self.accountHistListWidget.insertItem(0, hist_item) elif op["type"] == "author_reward": sbd_payout = (Amount(op["sbd_payout"], steem_instance=self.stm)) steem_payout = (Amount(op["steem_payout"], steem_instance=self.stm)) sp_payout = self.stm.vests_to_sp( Amount(op["vesting_payout"], steem_instance=self.stm)) self.lastAuthorListWidget.insertItem( 0, "%s - %s %s %.3f HP for %s" % (op_timedelta, str(sbd_payout), str(steem_payout), sp_payout, op["permlink"])) hist_item = "%s - %s - %s %s %.3f SP for %s" % ( op_local_time, op["type"], str(sbd_payout), str(steem_payout), sp_payout, op["permlink"]) tray_item = "%s - %s %s %.3f SP for %s" % ( op["type"], str(sbd_payout), str(steem_payout), sp_payout, op["permlink"]) self.accountHistListWidget.insertItem(0, hist_item) elif op["type"] == "custom_json": hist_item = "%s - %s - %s" % (op_local_time, op["type"], op["id"]) tray_item = "%s - %s" % (op["type"], op["id"]) self.accountHistListWidget.insertItem(0, hist_item) elif op["type"] == "transfer": hist_item = "%s - %s - %s from %s" % ( op_local_time, op["type"], str(Amount(op["amount"], steem_instance=self.stm)), op["from"]) tray_item = "%s - %s from %s" % ( op["type"], str(Amount(op["amount"], steem_instance=self.stm)), op["from"]) self.accountHistListWidget.insertItem(0, hist_item) elif op["type"] == "comment": comment_type = "post" if op["parent_author"] != "": hist_item = "%s - comment on %s - %s from %s" % ( op_local_time, construct_authorperm( op["parent_author"], op["parent_permlink"]), op["title"], op["author"]) tray_item = "comment from %s: %s on %s" % ( op["author"], op["body"][:100], op["title"]) else: hist_item = "%s - post - %s from %s" % ( op_local_time, op["title"], op["author"]) tray_item = "post from %s: %s" % (op["author"], op["title"]) self.accountHistListWidget.insertItem(0, hist_item) else: hist_item = "%s - %s" % (op_local_time, op["type"]) tray_item = "%s" % (op["type"]) self.accountHistListWidget.insertItem(0, hist_item) if self.accountHistNotificationCheckBox.isChecked( ) and not first_call: self.tray.showMessage(self.hist_account["name"], tray_item) if new_op_found: self.account_hist_info["start_block"] = start_block self.account_hist_info["trx_ids"] = trx_ids for op in self.account_history: if op["type"] == "vote": if op["voter"] == self.hist_account["name"]: continue votes.append(op) elif op["type"] == "curation_reward": curation_reward = self.stm.vests_to_sp( Amount(op["reward"], steem_instance=self.stm)) daily_curation += curation_reward elif op["type"] == "author_reward": sbd_payout = (Amount(op["sbd_payout"], steem_instance=self.stm)) steem_payout = (Amount(op["steem_payout"], steem_instance=self.stm)) sp_payout = self.stm.vests_to_sp( Amount(op["vesting_payout"], steem_instance=self.stm)) daily_author_SP += sp_payout daily_author_STEEM += float(steem_payout) daily_author_SBD += float(sbd_payout) reward_text = "Curation reward (last 24 h): %.3f HP\n" % daily_curation reward_text += "Author reward (last 24 h):\n" reward_text += "%.3f HP - %.3f HIVE - %.3f HBD" % ( daily_author_SP, (daily_author_STEEM), (daily_author_SBD)) self.text2.setText(reward_text)
class MainWindow(QMainWindow): EXIT_CODE_REBOOT = 520 def __init__(self): super().__init__() Config.initialize() self.initUI() def initUI(self): self.setAttribute(Qt.WA_DeleteOnClose) self.spliter = QSplitter(Qt.Vertical) self.spliter.addWidget(TestUnitArea()) self.spliter.addWidget(TestResultArea()) self.spliter.setHandleWidth(1) self.setCentralWidget(self.spliter) tool_menu = QMenu('工具', self.menuBar()) tool_menu.addAction('数据监听', self.onDebugWindow) tool_menu.addAction('单步测试', self.onSingleStep) tool_menu.addAction('记录查询', self.onViewData) tool_menu.addAction('条码打印', self.onPrintBarCode) tool_menu.addAction('异常信息', self.onExceptionWindow) setting_menu = QMenu('选项', self.menuBar()) setting_menu.addAction('参数设置', self.onSetting) # setting_menu.addAction('软件重启', self.onRestart) help_menu = QMenu('帮助', self.menuBar()) help_menu.addAction('关于', self.onAbout) self.menuBar().addMenu(setting_menu) self.menuBar().addMenu(tool_menu) self.menuBar().addMenu(help_menu) QApplication.setWindowIcon(QIcon(Config.LOGO_IMG)) QApplication.instance().aboutToQuit.connect(self.onApplicationQuit) QApplication.setOrganizationName(Config.ORGANIZATION) QApplication.setApplicationName(Config.APP_NAME) QApplication.setApplicationVersion(Config.APP_VERSION) self.restoreQSettings() self.createSystemTray() def onDebugWindow(self): if not ui.DebugDialog.prev_actived: self.debugWin = ui.DebugDialog() self.debugWin.show() else: QApplication.setActiveWindow(ui.DebugDialog.prev_window) ui.DebugDialog.prev_window.showNormal() def onSingleStep(self): if not ui.SingleStepFrame.prev_actived: self.singleWin = ui.SingleStepFrame() self.singleWin.show() else: QApplication.setActiveWindow(ui.SingleStepFrame.prev_window) ui.SingleStepFrame.prev_window.showNormal() def onViewData(self): if not ui.SearchWindow.prev_actived: self.searchWin = ui.SearchWindow() self.searchWin.show() else: QApplication.setActiveWindow(ui.SearchWindow.prev_window) ui.SearchWindow.prev_window.showNormal() def onPrintBarCode(self): if not CodeDialog.prev_actived: self.codeWin = CodeDialog() self.codeWin.show() else: QApplication.setActiveWindow(CodeDialog.prev_window) CodeDialog.prev_window.showNormal() def onExceptionWindow(self): if not ui.ExceptionWindow.prev_actived: self.excptionWin = ui.ExceptionWindow() self.excptionWin.show() else: QApplication.setActiveWindow(ui.ExceptionWindow.prev_window) ui.ExceptionWindow.prev_window.showNormal() def restoreQSettings(self): main_win_geo = Config.QSETTING.value('MainWindow/geometry') main_win_centerwgt_state = Config.QSETTING.value( 'MainWindow/CenterWidget/state') if main_win_geo: self.restoreGeometry(main_win_geo) if main_win_centerwgt_state: self.spliter.restoreState(main_win_centerwgt_state) def onSetting(self): dlg = ui.SettingDialog(self) dlg.move(self.x() + 50, self.y() + 50) dlg.exec() def onRestart(self): QApplication.exit(self.EXIT_CODE_REBOOT) def onAbout(self): dlg = ui.AboutDialog(Config.ABOUT_HTML) dlg.resize(400, 300) dlg.exec() def createSystemTray(self): self.systray = QSystemTrayIcon(self) self.systray.setIcon(QIcon(Config.LOGO_IMG)) self.systray.show() trayMenu = QMenu() trayMenu.addAction('最大化', self.showMaximized) trayMenu.addAction('最小化', self.showMinimized) trayMenu.addAction('显示窗口', self.showNormal) stayOnTop = QAction('总在最前', trayMenu, checkable=True, triggered=self.stayOnTop) trayMenu.addAction(stayOnTop) trayMenu.addSeparator() trayMenu.addAction('退出', QApplication.quit) username = platform.node() ip = socket.gethostbyname(socket.gethostname()) self.systray.setToolTip('用户:{}\nIP:{}'.format(username, ip)) self.systray.activated.connect(self.onSystemTrayActivated) self.systray.setContextMenu(trayMenu) def onSystemTrayActivated(self, reason): if reason in [QSystemTrayIcon.DoubleClick, QSystemTrayIcon.Trigger]: self.showNormal() def stayOnTop(self, checked): self.setWindowFlag(Qt.WindowStaysOnTopHint, self.sender().isChecked()) self.show() def onApplicationQuit(self): Config.QSETTING.setValue('MainWindow/geometry', self.saveGeometry()) Config.QSETTING.setValue('MainWindow/CenterWidget/state', self.spliter.saveState()) Config.finalize() self.systray.deleteLater() def closeEvent(self, event): Config.QSETTING.setValue('MainWindow/geometry', self.saveGeometry()) Config.QSETTING.setValue('MainWindow/CenterWidget/state', self.spliter.saveState()) if self.systray.isVisible(): self.hide() event.ignore()
class MyApp(QMainWindow): def __init__(self): super().__init__() print("ready MainWindow") self.initUI() self.is_pypresence_client_set = False self.p = Help() self.loadFile() #pyqt 종료 이벤트 override 됨 def closeEvent(self, event): if self.is_pypresence_client_set: self.RPC.close() print("RPC setup done, close event done") else: print("RPC setup not done, close event done") print("check line empty:" + str(self.checkEmptyLine())) self.saveFile() self.p.close() def run_pypresence(self, *args): print("run pypresence : " + str(args)) #pypresence 첫 실행 시 #pypresence 주어진 client_id로 연결하고 상태를 업데이트 if not self.is_pypresence_client_set: self.client_id = args[0] #get Discord Developer Portal self.RPC = Presence(self.client_id, pipe=0) self.RPC.connect() self.is_pypresence_client_set = True startTime = datetime.datetime.today().timestamp() self.RPC.update(details=args[1], state=args[2], large_image=args[3], start=startTime) #pypresence 첫 실행이 아닐 시 연결에 변화 없이 내용 업데이트 elif self.is_pypresence_client_set: startTime = datetime.datetime.today().timestamp() self.RPC.update(details=args[1], state=args[2], large_image=args[3], start=startTime) def initUI(self): #Get Foreground Window process name w = win32gui w.GetWindowText(w.GetForegroundWindow()) pid = win32process.GetWindowThreadProcessId(w.GetForegroundWindow()) print(psutil.Process(pid[-1]).name()) #foreground 프로세스 변경 시 자동으로 사용자 상태가 현재 활성화 된 창을 표시할 수 있도록 #SetWinEventHook 를 사용하여 foreground프로세스가 변경될 때 이벤트를 받을 수 있어야야함 #메뉴바 액션 exitAction = QAction('종료', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('프로그램 종료') exitAction.triggered.connect(qApp.quit) aboutAction = QAction('제작자', self) aboutAction.setStatusTip('제작자의 정보 : iro_bound') #exitAction.triggered.connect() #스테이터스 바 #첫번째 호출 시 statusbar 생성, 이후 호출시 상태바 객체 반환 #showMessage(str) 로 상태 메세지 변경 self.statusBar().showMessage('DCGA 준비됨') #메뉴 바 #--self.tray icon-- self.tray = QSystemTrayIcon(QIcon('icon.png'), parent=self) self.tray.setToolTip("check out this app on self.tray icon") self.tray.setVisible(True) menubar = self.menuBar() menubar.setNativeMenuBar(False) #Mac OS sync menu = QMenu('&도움말') filemenu = menu self.tray.setContextMenu(menu) filemenu.addAction(exitAction) filemenu.addAction(aboutAction) menubar.addMenu(menu) #센트럴 위젯 central = QWidget() #central.setStyleSheet("background-color:#333333; border-style:solid; border-width: 1px; border-color: #555555; border-radius: 4px;") self.idLine = QLineEdit() self.idLine.setPlaceholderText("Client ID를 입력") self.contentLine = QLineEdit() self.contentLine.setPlaceholderText("원하는 내용을 입력") self.statusLine = QLineEdit() self.statusLine.setPlaceholderText("원하는 상태를 입력") self.imageLine = QLineEdit() self.imageLine.setPlaceholderText("이미지 이름") self.okButton = QPushButton("적용") self.okButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.doingButton = QPushButton("설정하기") labelID = QLabel("Client 설정 :") labelID.setAlignment(Qt.AlignCenter) label0 = QLabel("~하는 중 :") label0.setAlignment(Qt.AlignCenter) label1 = QLabel("내용 : ") label1.setAlignment(Qt.AlignCenter) label2 = QLabel("상태 : ") label2.setAlignment(Qt.AlignCenter) label3 = QLabel("이미지 : ") label3.setAlignment(Qt.AlignCenter) grid = QGridLayout() grid.setContentsMargins(59, 50, 50, 50) grid.addWidget(labelID, 0, 0) grid.addWidget(self.idLine, 0, 1) grid.addWidget(label0, 1, 0) grid.addWidget(self.doingButton, 1, 1) grid.addWidget(label1, 2, 0) grid.addWidget(self.contentLine, 2, 1) grid.addWidget(label2, 3, 0) grid.addWidget(self.statusLine, 3, 1) grid.addWidget(label3, 4, 0) grid.addWidget(self.imageLine, 4, 1) grid.addWidget(self.okButton, 5, 0, 1, 2) temp = QPushButton("temp") grid.addWidget(temp, 6, 0, 1, 2) temp.clicked.connect(self.temms) #grid.setColumnStretch(0, 2) #grid.setColumnStretch(1, 2) central.setLayout(grid) self.setCentralWidget(central) #윈도우 기본 셋 self.setWindowTitle('Discord Custom GameActivity') self.setWindowIcon(QIcon('icon.png')) self.resize(400, 300) #self.setWindowFlags(Qt.FramelessWindowHint) #window frame hide #self.setAttribute(Qt.WA_TranslucentBackground) #remove border top side grabage self.center() self.show() #self.setStyleSheet("background-color: #333333;") #self -> style css type #이벤트 self.doingButton.clicked.connect(self.onDoingButton) self.okButton.clicked.connect(self.onOkButton) #self.tray.activated(self.self.trayActiviy(self.tray)) #화면 창을 가운데로 정렬 def center(self): qr = self.frameGeometry() #get 창의 위치, 크기 정보를 cp = QDesktopWidget().availableGeometry().center( ) #get 현재 모니터 화면의 가운데 위치 qr.moveCenter(cp) #qr에 담긴 프로그램 창의 중심정보를 화면의 중심으로 이동 self.move(qr.topLeft() ) #현재 창을 qr의 위치로 실제로 이동시킴, 의미 : topLeft => 모니터의 좌상단을 기준으로 #액션 def onDoingButton(self): webbrowser.open("https://discord.com/developers/applications") self.p.show() def onOkButton(self): id = self.idLine.text() content = self.contentLine.text() status = self.statusLine.text() image = self.imageLine.text() print(id, content, status, image) if self.checkEmptyLine(): print("Enter onOkButton event -> checkEmptyLine False Enter here") x = QMessageBox.question(self, '경고', '입력 항목을 다시 확인해주세요', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) else: self.run_pypresence(id, content, status, image) def checkEmptyLine(self): check = True if self.idLine.text() == "" or self.contentLine.text( ) == "" or self.statusLine.text() == "" or self.imageLine.text() == "": check = True else: check = False return check def loadFile(self): loadContent = './config.json' if os.path.isfile(loadContent): with open("config.json", "r") as f: readfile = json.load(f) self.idLine.setText(readfile[0]) self.contentLine.setText(readfile[1]) self.statusLine.setText(readfile[2]) self.imageLine.setText(readfile[3]) print("file load success") print(readfile) else: print("file load fail") def saveFile(self): if self.checkEmptyLine(): print("file save file. check line edit is empty line.") else: writeContent = [ self.idLine.text(), self.contentLine.text(), self.statusLine.text(), self.imageLine.text() ] with open("config.json", "w") as json_file: json.dump(writeContent, json_file) print("file save success") print(writeContent) def temms(self): if self.tray.isVisible(): self.tray.setVisible(False) elif not self.tray.isVisible(): self.tray.setVisible(True) else: print("error")
class MainWindow(QMainWindow): settings_button_clicked = pyqtSignal() status_table_clicked = pyqtSignal(str) export_accounting = pyqtSignal(str, str, str) def __init__(self): # Init the base class QMainWindow.__init__(self) self._settings = load_settings(Config.DEFAULT_SETTINGS) # Init the processed .ui file self._ui = Ui_MainWindow() self._ui.setupUi(self) self.setWindowTitle("TradeSkillMaster Application r{}".format(Config.CURRENT_VERSION)) # connect signals / slots self._ui.addon_status_table.doubleClicked.connect(self._addon_status_table_clicked) self._ui.backup_status_table.doubleClicked.connect(self._backup_status_table_clicked) self._ui.settings_button.clicked.connect(self.settings_button_clicked.emit) self._ui.accounts_dropdown.activated['QString'].connect(self.accounts_dropdown_changed) self._ui.realm_dropdown.activated['QString'].connect(self.realm_dropdown_changed) self._ui.export_button.clicked.connect(self.export_button_clicked) self._ui.help_button.setProperty("url", "http://tradeskillmaster.com/site/getting-help") self._ui.help_button.clicked.connect(self._link_button_clicked) self._ui.premium_button.setProperty("url", "http://tradeskillmaster.com/premium") self._ui.premium_button.clicked.connect(self._link_button_clicked) self._ui.logo_button.setProperty("url", "http://tradeskillmaster.com") self._ui.logo_button.clicked.connect(self._link_button_clicked) self._ui.twitter_button.setProperty("url", "http://twitter.com/TSMAddon") self._ui.twitter_button.clicked.connect(self._link_button_clicked) # Apply the stylesheet file = QFile(":/resources/main_window.css") file.open(QIODevice.ReadOnly) data = str(file.readAll(), encoding="ascii") self.setStyleSheet(data) # set properties which are necessary for tweaking the style self._ui.help_button.setProperty("id", "premiumButton") self._ui.premium_button.setProperty("id", "premiumButton") self._ui.header_text.setProperty("id", "headerText") # stylesheet tweaks for things which don't work when put into the .css for some unknown reason self._ui.accounting_tab.setStyleSheet("QCheckBox:disabled { color : #666; } QCheckBox { color : white; }"); self._sync_status_table_model = TableModel(self, ['Region/Realm', 'AuctionDB', 'Great Deals', 'Last Updated']) self._ui.sync_status_table.setModel(self._sync_status_table_model) self._addon_status_table_model = TableModel(self, ['Name', 'Version', 'Status']) self._ui.addon_status_table.setModel(self._addon_status_table_model) self._backup_status_table_model = TableModel(self, ['System ID', 'Account', 'Timestamp', 'Sync Status']) self._ui.backup_status_table.setModel(self._backup_status_table_model) self._accounting_info = {} self._accounting_current_account = "" self._accounting_current_realm = "" if Config.IS_WINDOWS: # create the system tray icon / menu self._tray_icon = QSystemTrayIcon(QIcon(":/resources/logo.png"), self) self._tray_icon.setToolTip("TradeSkillMaster Application r{}".format(Config.CURRENT_VERSION)) self._tray_icon.activated.connect(self._icon_activated) tray_icon_menu = QMenu(self) restore_action = QAction("Restore", tray_icon_menu) restore_action.triggered.connect(self._restore_from_tray) tray_icon_menu.addAction(restore_action) tray_icon_menu.addSeparator() quit_action = QAction("Quit", tray_icon_menu) quit_action.triggered.connect(self.close) tray_icon_menu.addAction(quit_action) self._tray_icon.setContextMenu(tray_icon_menu) self._tray_icon.hide() def __del__(self): if Config.IS_WINDOWS: self._tray_icon.hide() def changeEvent(self, event): if not Config.IS_WINDOWS: return if event.type() == QEvent.WindowStateChange: if self.isMinimized() and self._settings.minimize_to_tray: logging.getLogger().info("Minimizing to the system tray") self._tray_icon.show() self.hide() event.ignore() def closeEvent(self, event): if self._settings.confirm_exit: msg_box = QMessageBox() msg_box.setWindowIcon(QIcon(":/resources/logo.png")) msg_box.setWindowModality(Qt.ApplicationModal) msg_box.setIcon(QMessageBox.Information) msg_box.setText("Are you sure you want to exit?") msg_box.setStandardButtons(QMessageBox.Cancel | QMessageBox.Yes) if msg_box.exec_() != QMessageBox.Yes: event.ignore() return event.accept() def set_visible(self, visible): self.setVisible(visible) if self._settings.start_minimized and Config.IS_WINDOWS: self.showMinimized() self.setWindowState(Qt.WindowMinimized) if self._settings.minimize_to_tray: logging.getLogger().info("Minimizing to the system tray") self._tray_icon.show() self.hide() else: logging.getLogger().info("Minimizing") def _restore_from_tray(self): if not Config.IS_WINDOWS: return logging.getLogger().info("Restoring from the system tray") self.show() self.setWindowState(Qt.WindowActive) self._tray_icon.hide() def _icon_activated(self, reason): if not Config.IS_WINDOWS: return if reason == QSystemTrayIcon.Trigger or reason == QSystemTrayIcon.DoubleClick: self._restore_from_tray() def _link_button_clicked(self): QDesktopServices.openUrl(QUrl(self.sender().property("url"))) def _addon_status_table_clicked(self, index): key = self._addon_status_table_model.get_click_key(index) if key: self.status_table_clicked.emit(key) def _backup_status_table_clicked(self, index): key = self._backup_status_table_model.get_click_key(index) if key: self.status_table_clicked.emit(key) def set_sync_status_data(self, data): self._sync_status_table_model.set_info(data) self._ui.sync_status_table.resizeColumnsToContents() self._ui.sync_status_table.sortByColumn(0, Qt.AscendingOrder) def set_addon_status_data(self, data): self._addon_status_table_model.set_info(data) self._ui.addon_status_table.resizeColumnsToContents() self._ui.addon_status_table.sortByColumn(0, Qt.AscendingOrder) def set_backup_status_data(self, data): system_text = "The system ID is unique to the computer you are running the desktop app from. " \ "<a href=\"http://tradeskillmaster.com/user/backups\" style=\"color: #EC7800\">Premium users " \ "can sync backups</a> to the cloud and across multiple computers. Otherwise, only backups from " \ "the local system (<font style=\"color: cyan\">{}</font>) will be listed below.<br><br>To " \ "restore a backup, double-click on the row in the table below.".format(Config.SYSTEM_ID) self._ui.backup_system_text.setText(system_text) self._backup_status_table_model.set_info(data) self._ui.backup_status_table.resizeColumnsToContents() self._ui.backup_status_table.sortByColumn(2, Qt.DescendingOrder) def show_notification(self, message, critical): if not Config.IS_WINDOWS: return icon = QSystemTrayIcon.Critical if critical else QSystemTrayIcon.NoIcon if self._tray_icon.isVisible(): self._tray_icon.showMessage("TradeSkillMaster Desktop Application", message, icon) else: # The tray icon needs to be visible to show the message, but we can immediately hide it afterwards # This is the behavior on Windows 10 at least...need to confirm on other operating systems self._tray_icon.show() self._tray_icon.showMessage("TradeSkillMaster Desktop Application", message, icon) self._tray_icon.hide() def _update_dropdown(self, dropdown, items, selected_item): items = [""] + items selected_index = 0 dropdown.clear() for index, item in enumerate(items): if item == selected_item: selected_index = index dropdown.addItem(item) dropdown.setCurrentIndex(selected_index) def _update_accounting_tab(self): # update the accounts dropdown accounts = [x for x in self._accounting_info if self._accounting_info[x]] self._update_dropdown(self._ui.accounts_dropdown, accounts, self._accounting_current_account) # update the realm dropdown self._ui.realm_dropdown.setEnabled(self._accounting_current_account != "") if self._accounting_current_account != "": self._update_dropdown(self._ui.realm_dropdown, self._accounting_info[self._accounting_current_account], self._accounting_current_realm) # update the export button self._ui.export_button.setEnabled(self._accounting_current_realm != "") def set_accounting_accounts(self, info): self._accounting_info = info self._update_accounting_tab() def accounts_dropdown_changed(self, account): self._accounting_current_account = account self._accounting_current_realm = "" self._update_accounting_tab() def realm_dropdown_changed(self, realm): assert(self._accounting_current_account) self._accounting_current_realm = realm self._update_accounting_tab() def export_button_clicked(self): self._ui.export_button.setEnabled(False) self._ui.export_button.setText("Exporting...") def do_export(): if self._ui.sales_checkbox.checkState(): self.export_accounting.emit(self._accounting_current_account, self._accounting_current_realm, "sales") if self._ui.purchases_checkbox.checkState(): self.export_accounting.emit(self._accounting_current_account, self._accounting_current_realm, "purchases") if self._ui.income_checkbox.checkState(): self.export_accounting.emit(self._accounting_current_account, self._accounting_current_realm, "income") if self._ui.expenses_checkbox.checkState(): self.export_accounting.emit(self._accounting_current_account, self._accounting_current_realm, "expenses") if self._ui.expired_checkbox.checkState(): self.export_accounting.emit(self._accounting_current_account, self._accounting_current_realm, "expired") if self._ui.canceled_checkbox.checkState(): self.export_accounting.emit(self._accounting_current_account, self._accounting_current_realm, "canceled") self._ui.export_button.setEnabled(True) self._ui.export_button.setText("Export to CSV") # show a popup saying we've exported everything msg_box = QMessageBox() msg_box.setWindowIcon(QIcon(":/resources/logo.png")) msg_box.setWindowModality(Qt.ApplicationModal) msg_box.setIcon(QMessageBox.Information) msg_box.setText("The TSM_Accounting data has been successfully exported to your desktop.") msg_box.setStandardButtons(QMessageBox.Ok) msg_box.exec_() # slight delay so the button gets disabled QTimer.singleShot(1, do_export)
class Dialog(QDialog): def __init__(self): super().__init__() # Создание парсера self._dotaParser = parser.DotaParser() # Инициализация GUI self.initUi() self.initTray() self.initTimer() # Первоначальный парсинг self.startParser() self.fillDialog() def initUi(self): self._ongoingTitle = QLabel("<h3>Ongoing matches</h3>") self._ongoingLabel = QLabel("") self._upcomingTitle = QLabel("<h3>Upcoming matches</h3>") self._upcomingLabel = QLabel("") self._quitButton = QPushButton("Quit") self._layout = QVBoxLayout() self._layout.addWidget(self._ongoingTitle) self._layout.addWidget(self._ongoingLabel) self._layout.addWidget(self._upcomingTitle) self._layout.addWidget(self._upcomingLabel) self._layout.addWidget(self._quitButton) self.setLayout(self._layout) self._quitButton.clicked.connect(qApp.quit) self.setWindowTitle("Dota-2-Widget") def initTray(self): self._tray = QSystemTrayIcon() self._tray.setToolTip("dota-2-widget") self._tray.setIcon(QIcon("./dota2.gif")) self._tray.activated.connect(self.showOrHideDialog) self._tray.show() def initTimer(self): self._timer = QTimer() self._timer.timeout.connect(self.startParser) self._timer.timeout.connect(self.fillDialog) self._timer.start(5000) def startParser(self): self._dotaParser.startParser() #print("startParser") def fillDialog(self): # Получение результатов ongoingMatches = self._dotaParser.getOngoingMatches() upcomingMatches = self._dotaParser.getUpcomingMatches() # Запись результатов resultString = "" for i in range(0, len(ongoingMatches)): resultString += ongoingMatches[i] + '\n' self._ongoingLabel.setText(resultString) resultString = "" for i in range(0, len(upcomingMatches)): resultString += upcomingMatches[i] + '\n' if (i > 10): break self._upcomingLabel.setText(resultString) #print("fillDialog") def closeEvent(self, ce): if (self._tray.isVisible()): self.hide() def showOrHideDialog(self, result): if (result == QSystemTrayIcon.Trigger): if (not self.isVisible()): self.show() else: self.hide()
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self): super(QMainWindow, self).__init__() # Set up the user interface from Designer. self.setupUi(self) self.setAccessibleName("Hive Desktop") self.redrawLock = Lock() self.updateLock = Lock() self.optionsDialog = dialogs.Options(self) self.aboutDialog = dialogs.About(self, copyright='holger80', programName='Hive Desktop', version=VERSION, website='https://github.com/holgern/hivedesktop', websiteLabel='Github', comments='"Welcome to Hive desktop!\n This is the first release for testing qt5.\n Please vote for holger80 as witness, if you like this :).', licenseName='GPL-3.0', # licenseUrl=helpers.joinpath_to_cwd('LICENSE').as_uri(), authors=('holger80',), # dependencies=[l.strip() for l in requirements.readlines()], ) self.mdrenderer = MDRenderer(str(helpers.joinpath_to_cwd('themes'))) # tmpfile = helpers.mktemp(prefix='hivedesktop', suffix='.html') self.post = {"body": "##test", "authorperm": "@test/test"} self.thread = threads.MDThread(self) # self.webview.url = tmpfile.as_uri() self.feedListWidget.currentRowChanged.connect(self.change_displayed_post, Qt.QueuedConnection) self.timer = QTimer() self.timer.timeout.connect(self.refresh_account_thread) self.timer2 = QTimer() self.timer2.timeout.connect(self.update_account_hist_thread) self.timer3 = QTimer() self.timer3.timeout.connect(self.update_account_feed_thread) self.cache_path = QStandardPaths.writableLocation(QStandardPaths.CacheLocation) self.db_type = "shelve" self.db_type = "sqlite" self.feed = [] self.post = None # Get settings settings = QSettings() # Get checkbox state with speciying type of checkbox: # type=bool is a replacement of toBool() in PyQt5 check_state = settings.value(SETTINGS_TRAY, True, type=bool) hist_info_check_state = settings.value(SETTINGS_HIST_INFO, True, type=bool) account_state = settings.value(SETTINGS_ACCOUNT, "", type=str) self.resize(settings.value(SETTINGS_SIZE, QSize(1053, 800))) self.move(settings.value(SETTINGS_POS, QPoint(50, 50))) #self.accountHistTableWidget.setColumnCount(5) #self.accountHistTableWidget.setHorizontalHeaderLabels(["type", "1", "2", "3", "timestamp"]) self.update_account_refreshtime = 5000 # Set state self.accountHistNotificationCheckBox.setChecked(hist_info_check_state) self.autoRefreshCheckBox.setChecked(check_state) if check_state: self.timer.start(self.update_account_refreshtime) self.timer2.start(15000) self.timer3.start(60000) self.accountLineEdit.setText(account_state) # connect the slot to the signal by clicking the checkbox to save the state settings self.autoRefreshCheckBox.clicked.connect(self.save_check_box_settings) self.accountHistNotificationCheckBox.clicked.connect(self.save_check_box_settings) self.accountLineEdit.editingFinished.connect(self.save_account_settings) self.actionAbout.triggered.connect(self.about) self.actionOptions.triggered.connect(self.options) self.threadpool = QThreadPool() 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) menu = QMenu() menu.addAction(self.minimizeAction) menu.addAction(self.maximizeAction) menu.addAction(self.restoreAction) menu.addSeparator() # aboutAction = menu.addAction("about") # aboutAction.triggered.connect(self.about) exitAction = menu.addAction("Exit") exitAction.triggered.connect(self.closeApp) self.tray = QSystemTrayIcon(QIcon(':/icons/icon.ico')) self.tray.setContextMenu(menu) self.tray.activated.connect(self.trayAction) self.tray.setToolTip("Hive Desktop!") self.tray.setObjectName("Hive Desktop") self.setWindowTitle("Hive Desktop") self.tray.show() splash_pix = QPixmap(':/icons/splash.png') splash = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint) splash.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint) splash.setEnabled(False) #splash.show() #splash.showMessage("<h1><font color='green'>starting...</font></h1>", Qt.AlignTop | Qt.AlignCenter, Qt.black) account = account_state nodelist = NodeList() nodelist.update_nodes() self.stm = Steem(node=nodelist.get_nodes(hive=True)) set_shared_blockchain_instance(self.stm) if account != "": try: self.hist_account = Account(account, steem_instance=self.stm) except: self.hist_account = None else: self.hist_account = None self.refreshPushButton.clicked.connect(self.refresh_account) self.refreshPushButton.clicked.connect(self.update_account_hist_thread) self.accountLineEdit.editingFinished.connect(self.update_account_info) if self.hasFocus is not None: self.init_new_account() self.init_new_blocks() splash.deleteLater() def triggeredPreview(self): self.authorLabel.setText(self.post["author"]) self.titleLabel.setText(self.post["title"]) self.auhorpermLineEdit.setText(construct_authorperm(self.post["author"], self.post["permlink"])) self.thread.start() @pyqtSlot(str) def cbMDThread(self, html): self.webview.setHtml(html) @pyqtSlot(QSystemTrayIcon.ActivationReason) def trayAction(self, reason): if reason == QSystemTrayIcon.Trigger: if self.isHidden(): self.showNormal() else: self.hide() def closeEvent(self, event): if self.tray.isVisible(): #QMessageBox.information(self, "Hive Desktop", # "The program will keep running in the system tray. To " # "terminate the program, choose <b>Quit</b> in the " # "context menu of the system tray entry.") self.tray.showMessage("Hive Desktop", "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() def hide(self): self.update_account_refreshtime = 60000 self.timer.start(self.update_account_refreshtime) QMainWindow.hide(self) def showNormal(self): self.update_account_refreshtime = 5000 self.timer.start(self.update_account_refreshtime) QMainWindow.showNormal(self) def showMaximized(self): self.update_account_refreshtime = 5000 self.timer.start(self.update_account_refreshtime) QMainWindow.showMaximized(self) def closeApp(self): settings = QSettings() settings.setValue(SETTINGS_SIZE, self.size()) settings.setValue(SETTINGS_POS, self.pos()) settings.sync() sys.exit() def about(self): self.aboutDialog.exec_() def options(self): self.optionsDialog.exec_() # Slot checkbox to save the settings def save_check_box_settings(self): settings = QSettings() settings.setValue(SETTINGS_HIST_INFO, self.accountHistNotificationCheckBox.isChecked()) settings.setValue(SETTINGS_TRAY, self.autoRefreshCheckBox.isChecked()) if self.autoRefreshCheckBox.isChecked(): self.timer.start(self.update_account_refreshtime) self.timer2.start(15000) self.timer3.start(60000) else: self.timer.stop() self.timer2.stop() self.timer3.stop() settings.sync() # Slot checkbox to save the settings def save_account_settings(self): settings = QSettings() settings.setValue(SETTINGS_ACCOUNT, self.accountLineEdit.text()) settings.sync() def update_account_info(self): if self.hist_account is None or self.hist_account["name"] != self.accountLineEdit.text(): try: self.hist_account = Account(self.accountLineEdit.text(), steem_instance=self.stm) except: self.hist_account = None self.init_new_account() def init_new_account(self): self.refresh_account() self.update_account_feed() if self.hist_account is not None: self.db = database.Database(self.db_type, self.cache_path, self.hist_account["name"]) self.loadingProgressBar.setMaximum(self.db.get_account().virtual_op_count()) self.iah_thread = threads.IAHThread(self, self.db) self.timer.stop() self.timer2.stop() self.refreshPushButton.setEnabled(False) self.accountLineEdit.setEnabled(False) self.iah_thread.start() else: self.init_account_hist() self.update_account_hist() def init_new_blocks(self): blockchain = Blockchain() self.block_db = database_blocks.DatabaseBlocks(self.db_type, self.cache_path) self.loadingBlocksProgressBar.setMaximum(self.block_db.block_history) self.blocks_thread = threads.BlocksThread(self, self.block_db, blockchain) self.blocks_thread.start() @pyqtSlot(str) def cIAHThread(self, dummy): self.init_account_hist() self.update_account_hist() self.refreshPushButton.setEnabled(True) self.accountLineEdit.setEnabled(True) self.tray.showMessage("Ready", "Account history loaded!") self.timer.start(self.update_account_refreshtime) self.timer2.start(15000) @pyqtSlot(str) def cBlocksThread(self, dummy): self.init_blocks() self.update_blocks() #self.timer.start(self.update_account_refreshtime) #self.timer2.start(15000) def refresh_account_thread(self): worker = Worker(self.refresh_account) self.threadpool.start(worker) def refresh_account(self): if self.hist_account is None: return self.hist_account.refresh() self.accountInfoGroupBox.setTitle("%s (%.3f)" % (self.hist_account["name"], self.hist_account.rep)) with self.redrawLock: self.votePowerProgressBar.setValue(int(self.hist_account.vp)) if self.hist_account.vp == 100: self.votePowerProgressBar.setFormat("%.2f %%" % (self.hist_account.vp)) else: self.votePowerProgressBar.setFormat("%.2f %%, full in %s" % (self.hist_account.vp, self.hist_account.get_recharge_time_str())) down_vp = self.hist_account.get_downvoting_power() with self.redrawLock: self.downvotePowerProgressBar.setValue(int(down_vp)) if down_vp == 100: self.downvotePowerProgressBar.setFormat("%.2f %%" % (down_vp)) else: self.downvotePowerProgressBar.setFormat("%.2f %%, full in %s" % (down_vp, self.hist_account.get_recharge_time(starting_voting_power=down_vp))) self.votePowerLabel.setText("Vote Power, a 100%% vote is %.3f $" % (self.hist_account.get_voting_value_SBD())) self.downvotePowerLabel.setText("DownVote Power") self.STEEMLabel.setText(str(self.hist_account["balance"])) self.SBDLabel.setText(str(self.hist_account["sbd_balance"])) self.SPLabel.setText("%.3f HP" % self.stm.vests_to_sp(self.hist_account["vesting_shares"])) try: rc_manabar = self.hist_account.get_rc_manabar() with self.redrawLock: self.RCProgressBar.setValue(int(rc_manabar["current_pct"])) self.RCProgressBar.setFormat("%.2f %%, full in %s" % (rc_manabar["current_pct"], self.hist_account.get_manabar_recharge_time_str(rc_manabar))) rc = self.hist_account.get_rc() estimated_rc = int(rc["max_rc"]) * rc_manabar["current_pct"] / 100 rc_calc = RC(steem_instance=self.stm) self.RCLabel.setText("RC (%.0f G RC of %.0f G RC)" % (estimated_rc / 10**9, int(rc["max_rc"]) / 10**9)) ret = "--- Approx Costs ---\n" ret += "comment - %.2f G RC - enough RC for %d comments\n" % (rc_calc.comment() / 10**9, int(estimated_rc / rc_calc.comment())) ret += "vote - %.2f G RC - enough RC for %d votes\n" % (rc_calc.vote() / 10**9, int(estimated_rc / rc_calc.vote())) ret += "transfer - %.2f G RC - enough RC for %d transfers\n" % (rc_calc.transfer() / 10**9, int(estimated_rc / rc_calc.transfer())) ret += "custom_json - %.2f G RC - enough RC for %d custom_json\n" % (rc_calc.custom_json() / 10**9, int(estimated_rc / rc_calc.custom_json())) self.text.setText(ret) except: rc_manabar = None @pyqtSlot(int) def set_loading_progress(self, val): with self.redrawLock: self.loadingProgressBar.setValue(val) @pyqtSlot(int) def set_loading_blocks_progress(self, val): with self.redrawLock: self.loadingBlocksProgressBar.setValue(val) def init_blocks(self): self.blockCountLabel.setText("%d entries" % self.block_db.get_block_count()) def init_account_hist(self): if self.hist_account is None: return self.account_hist_info = {"start_block": 0} self.append_hist_info = {"start_block": 0} #if not self.db.has_account_hist(): # ops = self.db.get_acc_hist() # self.db.store_account_hist(ops) #else: # start_op = self.db.get_last_op() # ops = self.db.read_missing_account_history_data(start_op) # self.db.store_account_hist(ops) self.accountHistoryLabel.setText("%d entries" % self.db.get_count()) self.accountHistTableWidget.clear() def append_blocks(self): start_op = self.block_db.get_last_op() data = self.block_db.read_missing_block_data(start_op) # print("finished") if len(data) > 0: # print(op["timestamp"]) self.block_db.append_blocks(data) self.blockCountLabel.setText("%d entries" % self.block_db.get_block_count()) def append_account_hist(self): if self.hist_account is None: return # print("loading db") start_op = self.db.get_last_op() data = self.db.read_missing_account_history_data(start_op) # print("finished") if len(data) > 0: # print(op["timestamp"]) self.db.append_account_hist(data) self.accountHistoryLabel.setText("%d entries" % self.db.get_count()) def update_account_hist_thread(self): worker = Worker(self.update_account_hist) self.threadpool.start(worker) def update_account_feed_thread(self): worker = Worker(self.update_account_feed) self.threadpool.start(worker) @pyqtSlot(int) def change_displayed_post(self, row): if row < 0: return if len(self.feed) == 0: return if len(self.feed) <= row: return #index = self.feedListWidget.currentIndex() #row = index.row() self.post = self.feed[row] with self.updateLock: self.triggeredPreview() replies = Comment(self.post).get_all_replies() is_shown = [0] * len(replies) max_depth = 0 for i in range(len(replies)): if replies[i].depth > max_depth: max_depth = replies[i].depth self.commentsTreeWidget.clear() def create_item(reply): item = QTreeWidgetItem() item.setText(0, reply["author"]) item.setText(1, reply["body"]) item.setToolTip(1, reply["body"]) return item for i in range(len(replies)): if is_shown[i] == 1: continue reply = replies[i] if reply.depth == 1: is_shown[i] = 1 item = create_item(reply) for j in range(len(replies)): rr = replies[j] if is_shown[j] == 1: continue if rr.depth == 2: is_shown[j] = 1 if rr.parent_author == reply["author"] and rr.parent_permlink == reply["permlink"]: rr_item = create_item(rr) if max_depth >= 3: for k in range(len(replies)): rrr = replies[k] if is_shown[k] == 1: continue if rrr.depth == 3: is_shown[k] = 1 if rrr.parent_author == rr["author"] and rrr.parent_permlink == rr["permlink"]: rrr_item = create_item(rrr) if max_depth >= 4: for l in range(len(replies)): rrrr = replies[l] if is_shown[l] == 1: continue if rrrr.depth == 4: is_shown[l] = 1 if rrrr.parent_author == rrr["author"] and rrrr.parent_permlink == rrr["permlink"]: rrrr_item = create_item(rrrr) rrr_item.addChild(rrrr_item) rr_item.addChild(rrr_item) item.addChild(rr_item) self.commentsTreeWidget.addTopLevelItem(item) def update_account_feed(self): if self.hist_account is None: return try: updated_feed = self.hist_account.get_account_posts() except: print("could not update feed") return if len(self.feed) == 0: self.feed = updated_feed else: for post in updated_feed[::-1]: found = False for p in self.feed: if post["authorperm"] == p["authorperm"]: found = True if not found: self.feed.insert(0, post) if (addTzInfo(datetime.utcnow()) - post["created"]).total_seconds() / 60 < 5: self.tray.showMessage(post["author"], post["title"]) with self.updateLock: if self.post is None: self.post = self.feed[0] self.triggeredPreview() self.feedListWidget.currentRowChanged.disconnect(self.change_displayed_post) self.feedListWidget.clear() for post in self.feed[::-1]: post_text = "%s - %s" % (post["author"], post["title"]) post_item = QListWidgetItem() post_item.setText(post_text) post_item.setToolTip(post["author"]) self.feedListWidget.insertItem(0, post_item) self.feedListWidget.currentRowChanged.connect(self.change_displayed_post, Qt.QueuedConnection) def update_blocks(self): return def update_account_hist(self): if self.hist_account is None: return votes = [] daily_curation = 0 daily_author_SP = 0 daily_author_SBD = 0 daily_author_STEEM = 0 self.append_account_hist() new_op_found = False start_block = self.account_hist_info["start_block"] if start_block == 0: first_call = True b = Blockchain() start_block = b.get_current_block_num() - 20 * 60 * 24 * 7 self.account_hist_info["start_block"] = start_block else: first_call = False ops = self.db.load_account_hist(start_block) for op in ops: if op["block"] < start_block: # last_block = op["block"] continue start_block = op["block"] new_op_found = True tray_item = None op_timedelta = formatTimedelta(addTzInfo(datetime.utcnow()) - addTzInfo(op["timestamp"])) op_local_time = addTzInfo(op["timestamp"]).astimezone(tz.tzlocal()) # print("Read %d" % op["index"]) self.accountHistTableWidget.insertRow(0) self.accountHistTableWidget.setItem(0, 4, QTableWidgetItem(str(op_local_time))) if op["type"] == "vote": if op["voter"] == self.hist_account["name"]: self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem("Vote")) self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem(op["author"])) elif op["weight"] >= 0: self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem("Vote Post")) self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem(op["voter"])) tray_item = "%s - %s (%.2f %%) vote %s" % (op["type"], op["voter"], op["weight"] / 100, op["permlink"]) else: self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem("Dowvote Post")) self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem(op["voter"])) # hist_item.setToolTip(0, op["permlink"]) tray_item = "%s - %s (%.2f %%) downvote %s" % (op["type"], op["voter"], op["weight"] / 100, op["permlink"]) self.accountHistTableWidget.setItem(0, 2, QTableWidgetItem("%.2f %%" % (op["weight"] / 100))) elif op["type"] == "curation_reward": self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"])) curation_reward = self.stm.vests_to_sp(Amount(op["reward"], steem_instance=self.stm)) self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem("%.3f HP" % curation_reward)) self.accountHistTableWidget.setItem(0, 2, QTableWidgetItem(construct_authorperm(op["comment_author"], op["comment_permlink"]))) hist_item = "%s - %s - %.3f HP for %s" % (op_local_time, op["type"], curation_reward, construct_authorperm(op["comment_author"], op["comment_permlink"])) tray_item = "%s - %.3f HP for %s" % (op["type"], curation_reward, construct_authorperm(op["comment_author"], op["comment_permlink"])) elif op["type"] == "author_reward": self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"])) sbd_payout = (Amount(op["sbd_payout"], steem_instance=self.stm)) steem_payout = (Amount(op["steem_payout"], steem_instance=self.stm)) sp_payout = self.stm.vests_to_sp(Amount(op["vesting_payout"], steem_instance=self.stm)) hist_item = "%s - %s - %s %s %.3f SP for %s" % (op_local_time, op["type"], str(sbd_payout), str(steem_payout), sp_payout, op["permlink"]) tray_item = "%s - %s %s %.3f SP for %s" % (op["type"], str(sbd_payout), str(steem_payout), sp_payout, op["permlink"]) elif op["type"] == "custom_json": self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"])) self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem(op["json_id"])) json_data = QTableWidgetItem(op["json"]) json_data.setToolTip(op["json"]) self.accountHistTableWidget.setItem(0, 2, json_data) hist_item = "%s - %s - %s" % (op_local_time, op["type"], op["id"]) tray_item = "%s - %s" % (op["type"], op["json_id"]) elif op["type"] == "transfer": self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"])) hist_item = "%s - %s - %s from %s" % (op_local_time, op["type"], str(Amount(op["amount"], steem_instance=self.stm)), op["from"]) tray_item = "%s - %s from %s" % (op["type"], str(Amount(op["amount"], steem_instance=self.stm)), op["from"]) elif op["type"] == "comment": if op["parent_author"] != "": comment_type = "comment" hist_item = "%s - comment on %s - %s from %s" % (op_local_time, construct_authorperm(op["parent_author"], op["parent_permlink"]), op["title"], op["author"]) tray_item = "comment from %s: %s on %s" % (op["author"], op["body"][:100], op["title"]) self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"])) self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem(op["author"])) body = QTableWidgetItem(op["body"]) body.setToolTip(op["body"]) self.accountHistTableWidget.setItem(0, 2, body) else: comment_type = "post" hist_item = "%s - post - %s from %s" % (op_local_time, op["title"], op["author"]) tray_item = "post from %s: %s" % (op["author"], op["title"]) self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(comment_type)) elif op["type"] == "producer_reward": self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"])) self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem(" %.3f HP" % float(self.stm.vests_to_sp(Amount(op["vesting_shares"]))))) hist_item = "%s - %s" % (op_local_time, op["type"]) tray_item = "%s - %.3f HP" % (op["type"], float(self.stm.vests_to_sp(Amount(op["vesting_shares"])))) else: self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"])) hist_item = "%s - %s" % (op_local_time, op["type"]) tray_item = "%s" % (op["type"]) if self.accountHistNotificationCheckBox.isChecked() and not first_call and tray_item is not None: self.tray.showMessage(self.hist_account["name"], tray_item) if new_op_found: self.account_hist_info["start_block"] = start_block for op in ops: if op["type"] == "vote": if op["voter"] == self.hist_account["name"]: continue votes.append(op) elif op["type"] == "curation_reward": curation_reward = self.stm.vests_to_sp(Amount(op["reward"], steem_instance=self.stm)) daily_curation += curation_reward elif op["type"] == "author_reward": sbd_payout = (Amount(op["sbd_payout"], steem_instance=self.stm)) steem_payout = (Amount(op["steem_payout"], steem_instance=self.stm)) sp_payout = self.stm.vests_to_sp(Amount(op["vesting_payout"], steem_instance=self.stm)) daily_author_SP += sp_payout daily_author_STEEM += float(steem_payout) daily_author_SBD += float(sbd_payout) reward_text = "Curation reward (last 24 h): %.3f HP\n" % daily_curation reward_text += "Author reward (last 24 h):\n" reward_text += "%.3f HP - %.3f HIVE - %.3f HBD" % (daily_author_SP, (daily_author_STEEM), (daily_author_SBD)) self.text2.setText(reward_text)