class MyWidget2(QMainWindow, Ui_MainWindow1): def __init__(self): super().__init__() self.setupUi(self) self.init_ui() self.dialog = MyWidget2() def init_ui(self): self.easy_btn.clicked.connect(self.openGame) self.show() def init_pygame(self): self.game = Game() self.timer = QTimer() self.timer.timeout.connect(self.pygame_loop) self.timer.start(10) def pygame_loop(self): if self.game.loop(self): self.timer.stop() self.timer.disconnect() pygame.quit() def openGame(self): self.init_pygame() self.dialog.close()
class proxyTryconnect(QObject): reconnectproxy = pyqtSignal() def __init__(self): super(proxyTryconnect, self).__init__() self.trytimes = 1 self.keeptrytimes = False self.resetTime = False self.resetTimeCount = 0 self.periodicCheck = QTimer() def periodicCheckProxyStatus(self): self.resetTimeCount += 1 if self.resetTimeCount <= self.resetTime and self.trytimes <= 0: self.reconnectproxy.emit() self.reset() elif self.resetTimeCount >= self.resetTime and self.trytimes > 0: self.resetTimeCount = 0 def reset(self): self.resetTimeCount = 0 self.trytimes = copy.deepcopy(self.keeptrytimes) def setresetTime(self, seconds, trytimes): if not isinstance(seconds, int): seconds = 60 if not isinstance(trytimes, int): trytimes = 3 if seconds == 0: seconds = 1 if trytimes == 0: self.trytimes = 1 else: self.trytimes = copy.deepcopy(trytimes) self.keeptrytimes = copy.deepcopy(self.trytimes) self.resetTime = seconds * self.keeptrytimes self.periodicCheck.timeout.connect(self.periodicCheckProxyStatus) self.periodicCheck.start(1000) def stopperiodicCheckProxyStatus(self): self.periodicCheck.stop() self.periodicCheck.disconnect(self.periodicCheckProxyStatus) def trytimesDecrease(self): self.trytimes -= 1
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 MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.setupTrayicon() self.setupVariables() self.setupUi() self.setupConnections() self.show() def setupVariables(self): settings = QSettings() self.workEndTime = QTime( int(settings.value(workHoursKey, 0)), int(settings.value(workMinutesKey, 25)), int(settings.value(workSecondsKey, 0)), ) self.restEndTime = QTime( int(settings.value(restHoursKey, 0)), int(settings.value(restMinutesKey, 5)), int(settings.value(restSecondsKey, 0)), ) self.timeFormat = "hh:mm:ss" self.time = QTime(0, 0, 0, 0) self.workTime = QTime(0, 0, 0, 0) self.restTime = QTime(0, 0, 0, 0) self.totalTime = QTime(0, 0, 0, 0) self.currentMode = Mode.work self.maxRepetitions = -1 self.currentRepetitions = 0 def setupConnections(self): """ Create button connections """ self.startButton.clicked.connect(self.startTimer) self.startButton.clicked.connect( lambda: self.startButton.setDisabled(True)) self.startButton.clicked.connect( lambda: self.pauseButton.setDisabled(False)) self.startButton.clicked.connect( lambda: self.resetButton.setDisabled(False)) self.pauseButton.clicked.connect(self.pauseTimer) self.pauseButton.clicked.connect( lambda: self.startButton.setDisabled(False)) self.pauseButton.clicked.connect( lambda: self.pauseButton.setDisabled(True)) self.pauseButton.clicked.connect( lambda: self.resetButton.setDisabled(False)) self.pauseButton.clicked.connect( lambda: self.startButton.setText("continue")) self.resetButton.clicked.connect(self.resetTimer) self.resetButton.clicked.connect( lambda: self.startButton.setDisabled(False)) self.resetButton.clicked.connect( lambda: self.pauseButton.setDisabled(True)) self.resetButton.clicked.connect( lambda: self.resetButton.setDisabled(True)) self.resetButton.clicked.connect( lambda: self.startButton.setText("start")) self.acceptTaskButton.pressed.connect(self.insertTask) self.deleteTaskButton.pressed.connect(self.deleteTask) """ Create spinbox connections """ self.workHoursSpinBox.valueChanged.connect(self.updateWorkEndTime) self.workMinutesSpinBox.valueChanged.connect(self.updateWorkEndTime) self.workSecondsSpinBox.valueChanged.connect(self.updateWorkEndTime) self.restHoursSpinBox.valueChanged.connect(self.updateRestEndTime) self.restMinutesSpinBox.valueChanged.connect(self.updateRestEndTime) self.restSecondsSpinBox.valueChanged.connect(self.updateRestEndTime) self.repetitionsSpinBox.valueChanged.connect(self.updateMaxRepetitions) """ Create combobox connections """ self.modeComboBox.currentTextChanged.connect(self.updateCurrentMode) """ Create tablewidget connections """ self.tasksTableWidget.cellDoubleClicked.connect( self.markTaskAsFinished) def setupUi(self): self.size_policy = sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) """ Create tabwidget """ self.tabWidget = QTabWidget() """ Create tab widgets """ timerWidget = self.setupTimerTab() tasksWidget = self.setupTasksTab() statisticsWidget = self.setupStatisticsTab() """ add tab widgets to tabwidget""" self.timerTab = self.tabWidget.addTab(timerWidget, makeIcon("timer"), "Timer") self.tasksTab = self.tabWidget.addTab(tasksWidget, makeIcon("tasks"), "Tasks") self.statisticsTab = self.tabWidget.addTab(statisticsWidget, makeIcon("statistics"), "Statistics") """ Set mainwindows central widget """ self.setCentralWidget(self.tabWidget) def setupTimerTab(self): settings = QSettings() self.timerContainer = QWidget(self) self.timerContainerLayout = QVBoxLayout(self.timerContainer) self.timerContainer.setLayout(self.timerContainerLayout) """ Create work groupbox""" self.workGroupBox = QGroupBox("Work") self.workGroupBoxLayout = QHBoxLayout(self.workGroupBox) self.workGroupBox.setLayout(self.workGroupBoxLayout) self.workHoursSpinBox = QSpinBox( minimum=0, maximum=24, value=int(settings.value(workHoursKey, 0)), suffix="h", sizePolicy=self.size_policy, ) self.workMinutesSpinBox = QSpinBox( minimum=0, maximum=60, value=int(settings.value(workMinutesKey, 25)), suffix="m", sizePolicy=self.size_policy, ) self.workSecondsSpinBox = QSpinBox( minimum=0, maximum=60, value=int(settings.value(workSecondsKey, 0)), suffix="s", sizePolicy=self.size_policy, ) """ Create rest groupbox""" self.restGroupBox = QGroupBox("Rest") self.restGroupBoxLayout = QHBoxLayout(self.restGroupBox) self.restGroupBox.setLayout(self.restGroupBoxLayout) self.restHoursSpinBox = QSpinBox( minimum=0, maximum=24, value=int(settings.value(restHoursKey, 0)), suffix="h", sizePolicy=self.size_policy, ) self.restMinutesSpinBox = QSpinBox( minimum=0, maximum=60, value=int(settings.value(restMinutesKey, 5)), suffix="m", sizePolicy=self.size_policy, ) self.restSecondsSpinBox = QSpinBox( minimum=0, maximum=60, value=int(settings.value(restSecondsKey, 0)), suffix="s", sizePolicy=self.size_policy, ) self.restGroupBoxLayout.addWidget(self.restHoursSpinBox) self.restGroupBoxLayout.addWidget(self.restMinutesSpinBox) self.restGroupBoxLayout.addWidget(self.restSecondsSpinBox) """ Create other groupbox""" self.otherGroupBox = QGroupBox("Other") self.otherGroupBoxLayout = QHBoxLayout(self.otherGroupBox) self.otherGroupBox.setLayout(self.otherGroupBoxLayout) self.repetitionsLabel = QLabel("Repetitions") self.repetitionsSpinBox = QSpinBox( minimum=0, maximum=10000, value=0, sizePolicy=self.size_policy, specialValueText="∞", ) self.modeLabel = QLabel("Mode") self.modeComboBox = QComboBox(sizePolicy=self.size_policy) self.modeComboBox.addItems(["work", "rest"]) self.otherGroupBoxLayout.addWidget(self.repetitionsLabel) self.otherGroupBoxLayout.addWidget(self.repetitionsSpinBox) self.otherGroupBoxLayout.addWidget(self.modeLabel) self.otherGroupBoxLayout.addWidget(self.modeComboBox) """ Create timer groupbox""" self.lcdDisplayGroupBox = QGroupBox("Time") self.lcdDisplayGroupBoxLayout = QHBoxLayout(self.lcdDisplayGroupBox) self.lcdDisplayGroupBox.setLayout(self.lcdDisplayGroupBoxLayout) self.timeDisplay = QLCDNumber(8, sizePolicy=self.size_policy) self.timeDisplay.setFixedHeight(100) self.timeDisplay.display("00:00:00") self.lcdDisplayGroupBoxLayout.addWidget(self.timeDisplay) """ Create pause, start and reset buttons""" self.buttonContainer = QWidget() self.buttonContainerLayout = QHBoxLayout(self.buttonContainer) self.buttonContainer.setLayout(self.buttonContainerLayout) self.startButton = self.makeButton("start", disabled=False) self.resetButton = self.makeButton("reset") self.pauseButton = self.makeButton("pause") """ Add widgets to container """ self.workGroupBoxLayout.addWidget(self.workHoursSpinBox) self.workGroupBoxLayout.addWidget(self.workMinutesSpinBox) self.workGroupBoxLayout.addWidget(self.workSecondsSpinBox) self.timerContainerLayout.addWidget(self.workGroupBox) self.timerContainerLayout.addWidget(self.restGroupBox) self.timerContainerLayout.addWidget(self.otherGroupBox) self.timerContainerLayout.addWidget(self.lcdDisplayGroupBox) self.buttonContainerLayout.addWidget(self.pauseButton) self.buttonContainerLayout.addWidget(self.startButton) self.buttonContainerLayout.addWidget(self.resetButton) self.timerContainerLayout.addWidget(self.buttonContainer) return self.timerContainer def setupTasksTab(self): settings = QSettings() """ Create vertical tasks container """ self.tasksWidget = QWidget(self.tabWidget) self.tasksWidgetLayout = QVBoxLayout(self.tasksWidget) self.tasksWidget.setLayout(self.tasksWidgetLayout) """ Create horizontal input container """ self.inputContainer = QWidget() self.inputContainer.setFixedHeight(50) self.inputContainerLayout = QHBoxLayout(self.inputContainer) self.inputContainerLayout.setContentsMargins(0, 0, 0, 0) self.inputContainer.setLayout(self.inputContainerLayout) """ Create text edit """ self.taskTextEdit = QTextEdit( placeholderText="Describe your task briefly.", undoRedoEnabled=True) """ Create vertical buttons container """ self.inputButtonContainer = QWidget() self.inputButtonContainerLayout = QVBoxLayout( self.inputButtonContainer) self.inputButtonContainerLayout.setContentsMargins(0, 0, 0, 0) self.inputButtonContainer.setLayout(self.inputButtonContainerLayout) """ Create buttons """ self.acceptTaskButton = QToolButton(icon=makeIcon("check")) self.deleteTaskButton = QToolButton(icon=makeIcon("trash")) """ Create tasks tablewidget """ self.tasksTableWidget = QTableWidget(0, 1) self.tasksTableWidget.setHorizontalHeaderLabels(["Tasks"]) self.tasksTableWidget.horizontalHeader().setStretchLastSection(True) self.tasksTableWidget.verticalHeader().setVisible(False) self.tasksTableWidget.setWordWrap(True) self.tasksTableWidget.setTextElideMode(Qt.ElideNone) self.tasksTableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tasksTableWidget.setSelectionMode( QAbstractItemView.SingleSelection) self.insertTasks(*settings.value(tasksKey, [])) """ Add widgets to container widgets """ self.inputButtonContainerLayout.addWidget(self.acceptTaskButton) self.inputButtonContainerLayout.addWidget(self.deleteTaskButton) self.inputContainerLayout.addWidget(self.taskTextEdit) self.inputContainerLayout.addWidget(self.inputButtonContainer) self.tasksWidgetLayout.addWidget(self.inputContainer) self.tasksWidgetLayout.addWidget(self.tasksTableWidget) return self.tasksWidget def setupStatisticsTab(self): """ Create statistics container """ self.statisticsContainer = QWidget() self.statisticsContainerLayout = QVBoxLayout(self.statisticsContainer) self.statisticsContainer.setLayout(self.statisticsContainerLayout) """ Create work time groupbox """ self.statisticsWorkTimeGroupBox = QGroupBox("Work Time") self.statisticsWorkTimeGroupBoxLayout = QHBoxLayout() self.statisticsWorkTimeGroupBox.setLayout( self.statisticsWorkTimeGroupBoxLayout) self.statisticsWorkTimeDisplay = QLCDNumber(8) self.statisticsWorkTimeDisplay.display("00:00:00") self.statisticsWorkTimeGroupBoxLayout.addWidget( self.statisticsWorkTimeDisplay) """ Create rest time groupbox """ self.statisticsRestTimeGroupBox = QGroupBox("Rest Time") self.statisticsRestTimeGroupBoxLayout = QHBoxLayout() self.statisticsRestTimeGroupBox.setLayout( self.statisticsRestTimeGroupBoxLayout) self.statisticsRestTimeDisplay = QLCDNumber(8) self.statisticsRestTimeDisplay.display("00:00:00") self.statisticsRestTimeGroupBoxLayout.addWidget( self.statisticsRestTimeDisplay) """ Create total time groupbox """ self.statisticsTotalTimeGroupBox = QGroupBox("Total Time") self.statisticsTotalTimeGroupBoxLayout = QHBoxLayout() self.statisticsTotalTimeGroupBox.setLayout( self.statisticsTotalTimeGroupBoxLayout) self.statisticsTotalTimeDisplay = QLCDNumber(8) self.statisticsTotalTimeDisplay.display("00:00:00") self.statisticsTotalTimeGroupBoxLayout.addWidget( self.statisticsTotalTimeDisplay) """ Add widgets to container """ self.statisticsContainerLayout.addWidget( self.statisticsTotalTimeGroupBox) self.statisticsContainerLayout.addWidget( self.statisticsWorkTimeGroupBox) self.statisticsContainerLayout.addWidget( self.statisticsRestTimeGroupBox) return self.statisticsContainer def setupTrayicon(self): self.trayIcon = QSystemTrayIcon(makeIcon("tomato")) self.trayIcon.setContextMenu(QMenu()) self.quitAction = self.trayIcon.contextMenu().addAction( makeIcon("exit"), "Quit", self.exit) self.quitAction.triggered.connect(self.exit) self.trayIcon.activated.connect(self.onActivate) self.trayIcon.show() def leaveEvent(self, event): super(MainWindow, self).leaveEvent(event) self.tasksTableWidget.clearSelection() def closeEvent(self, event): super(MainWindow, self).closeEvent(event) settings = QSettings() settings.setValue(workHoursKey, self.workHoursSpinBox.value()) settings.setValue( workMinutesKey, self.workMinutesSpinBox.value(), ) settings.setValue( workSecondsKey, self.workSecondsSpinBox.value(), ) settings.setValue(restHoursKey, self.restHoursSpinBox.value()) settings.setValue( restMinutesKey, self.restMinutesSpinBox.value(), ) settings.setValue( restSecondsKey, self.restSecondsSpinBox.value(), ) tasks = [] for i in range(self.tasksTableWidget.rowCount()): item = self.tasksTableWidget.item(i, 0) if not item.font().strikeOut(): tasks.append(item.text()) settings.setValue(tasksKey, tasks) def startTimer(self): try: if not self.timer.isActive(): self.createTimer() except: self.createTimer() def createTimer(self): self.timer = QTimer() self.timer.timeout.connect(self.updateTime) self.timer.timeout.connect(self.maybeChangeMode) self.timer.setInterval(1000) self.timer.setSingleShot(False) self.timer.start() def pauseTimer(self): try: self.timer.stop() self.timer.disconnect() except: pass def resetTimer(self): try: self.pauseTimer() self.time = QTime(0, 0, 0, 0) self.displayTime() except: pass def maybeStartTimer(self): if self.currentRepetitions != self.maxRepetitions: self.startTimer() started = True else: self.currentRepetitions = 0 started = False return started def updateWorkEndTime(self): self.workEndTime = QTime( self.workHoursSpinBox.value(), self.workMinutesSpinBox.value(), self.workSecondsSpinBox.value(), ) def updateRestEndTime(self): self.restEndTime = QTime( self.restHoursSpinBox.value(), self.restMinutesSpinBox.value(), self.restSecondsSpinBox.value(), ) def updateCurrentMode(self, mode: str): self.currentMode = Mode.work if mode == "work" else Mode.rest def updateTime(self): self.time = self.time.addSecs(1) self.totalTime = self.totalTime.addSecs(1) if self.modeComboBox.currentText() == "work": self.workTime = self.workTime.addSecs(1) else: self.restTime = self.restTime.addSecs(1) self.displayTime() def updateMaxRepetitions(self, value): if value == 0: self.currentRepetitions = 0 self.maxRepetitions = -1 else: self.maxRepetitions = 2 * value def maybeChangeMode(self): if self.currentMode is Mode.work and self.time >= self.workEndTime: self.resetTimer() self.modeComboBox.setCurrentIndex(1) self.incrementCurrentRepetitions() started = self.maybeStartTimer() self.showWindowMessage( Status.workFinished if started else Status.repetitionsReached) elif self.currentMode is Mode.rest and self.time >= self.restEndTime: self.resetTimer() self.modeComboBox.setCurrentIndex(0) self.incrementCurrentRepetitions() started = self.maybeStartTimer() self.showWindowMessage( Status.restFinished if started else Status.repetitionsReached) def incrementCurrentRepetitions(self): if self.maxRepetitions > 0: self.currentRepetitions += 1 def insertTask(self): task = self.taskTextEdit.toPlainText() self.insertTasks(task) def insertTasks(self, *tasks): for task in tasks: if task: rowCount = self.tasksTableWidget.rowCount() self.tasksTableWidget.setRowCount(rowCount + 1) self.tasksTableWidget.setItem(rowCount, 0, QTableWidgetItem(task)) self.tasksTableWidget.resizeRowsToContents() self.taskTextEdit.clear() def deleteTask(self): selectedIndexes = self.tasksTableWidget.selectedIndexes() if selectedIndexes: self.tasksTableWidget.removeRow(selectedIndexes[0].row()) def markTaskAsFinished(self, row, col): item = self.tasksTableWidget.item(row, col) font = self.tasksTableWidget.item(row, col).font() font.setStrikeOut(False if item.font().strikeOut() else True) item.setFont(font) def displayTime(self): self.timeDisplay.display(self.time.toString(self.timeFormat)) self.statisticsRestTimeDisplay.display( self.restTime.toString(self.timeFormat)) self.statisticsWorkTimeDisplay.display( self.workTime.toString(self.timeFormat)) self.statisticsTotalTimeDisplay.display( self.totalTime.toString(self.timeFormat)) def showWindowMessage(self, status): if status is Status.workFinished: self.trayIcon.showMessage("Break", choice(work_finished_phrases), makeIcon("tomato")) elif status is Status.restFinished: self.trayIcon.showMessage("Work", choice(rest_finished_phrases), makeIcon("tomato")) else: self.trayIcon.showMessage("Finished", choice(pomodoro_finished_phrases), makeIcon("tomato")) self.resetButton.click() def makeButton(self, text, iconName=None, disabled=True): button = QPushButton(text, sizePolicy=self.size_policy) if iconName: button.setIcon(makeIcon(iconName)) button.setDisabled(disabled) return button def exit(self): self.close() app = QApplication.instance() if app: app.quit() def onActivate(self, reason): if reason == QSystemTrayIcon.Trigger: self.show()
class MainWindow(QWidget): screenWidth = None # 窗口的宽度 screenHeight = None # 窗口的高度 pixmap = None # 临时存储图片路径 background = None # 开始时的背景图片 timer = None # 定时器 # 按钮 closeButton = None # 关闭窗口按钮 minimizeButton = None # 最小化窗口按钮 startButton = None # 开始游戏按钮 muteButton = None # 静音按钮 cancelMuteButton = None # 取消静音按钮 nextMusicButton = None # 下一首音乐按钮 previousMusicButton = None # 上一首音乐按钮 buttonSize = 30 + 5 def __init__(self, parent=None): super(MainWindow, self).__init__(parent) # self.parent = parent self.player = Player(self) self.game = Tetris(self) self.screenWidth = 900 self.screenHeight = 800 self.initWindow() self.initUI() self.initButton() def initWindow(self): """初始化窗口""" self.resize(self.screenWidth, self.screenHeight) # 窗口大小 self.setWindowTitle('Tetris') self.setWindowFlag(Qt.FramelessWindowHint) # 无边框 self.setWindowState(Qt.WindowActive) # 活动窗口 self.setWindowIcon(QIcon('./icons/captain_America.ico')) # 设置图标 def initUI(self): """绘制背景图片""" # 第一张背景图片 self.background = QLabel(self) self.pixmap = QPixmap('./icons/background.png') self.background.setPixmap(self.pixmap) def initButton(self): """初始化按钮属性,创建按钮""" # 读取qss文件 with open('./QSS/mainWindow.qss', 'r', encoding='utf-8') as fp: self.setStyleSheet(fp.read()) fp.close() # 右上角的关闭按钮 self.closeButton = QPushButton(self) self.closeButton.setObjectName('closeButton') self.closeButton.setShortcut('ESC') # 按钮热键esc self.closeButton.setToolTip('关闭') # 悬停在按钮上的提示->关闭 self.closeButton.move(self.screenWidth - self.buttonSize, 5) # 按钮的位置 self.closeButton.clicked.connect(self.close) # 右上角的最小化按钮 self.minimizeButton = QPushButton(self) self.minimizeButton.setObjectName('minimizeButton') self.minimizeButton.setToolTip('最小化') # 悬停在按钮上的提示->最小化 self.minimizeButton.move(self.screenWidth - 2 * self.buttonSize, 5) # 按钮的位置 self.minimizeButton.clicked.connect(self.showMinimized) # 开始游戏的按钮 self.startButton = QPushButton(self) self.startButton.setObjectName('startButton') self.startButton.move(self.screenWidth // 2 - 100, self.screenHeight // 2 - 50) # 按钮的位置 self.startButton.clicked.connect( lambda: { self.startButton.hide(), self.background.hide(), self.game.start(), self.setTimer(), }) # 静音按钮 self.muteButton = QPushButton(self) self.muteButton.setObjectName('muteButton') self.muteButton.move(self.screenWidth - 3 * self.buttonSize, 5) # 按钮的位置 self.muteButton.hide() # 默认隐藏 self.muteButton.clicked.connect( lambda: { self.player.cancelMute(), self.cancelMuteButton.show(), self.muteButton.hide(), }) # 取消静音按钮 self.cancelMuteButton = QPushButton(self) self.cancelMuteButton.setObjectName('cancelMuteButton') self.cancelMuteButton.move(self.screenWidth - 3 * self.buttonSize, 5) # 按钮的位置 self.cancelMuteButton.clicked.connect( lambda: { self.player.mute(), self.cancelMuteButton.hide(), self.muteButton.show(), }) # 下一首音乐按钮 self.nextMusicButton = QPushButton(self) self.nextMusicButton.setObjectName('nextMusicButton') self.nextMusicButton.setToolTip('下一首') # 悬停在按钮上的提示->下一首 self.nextMusicButton.move(self.screenWidth - 4 * self.buttonSize, 5) # 按钮的位置 self.nextMusicButton.clicked.connect(self.player.nextMusic) # 上一首音乐按钮 self.previousMusicButton = QPushButton(self) self.previousMusicButton.setObjectName('previousMusicButton') self.previousMusicButton.setToolTip('上一首') # 悬停在按钮上的提示->上一首 self.previousMusicButton.move(self.screenWidth - 5 * self.buttonSize, 5) # 按钮的位置 self.previousMusicButton.clicked.connect(self.player.previousMusic) def setTimer(self): """设置Timer, 每隔500ms检测游戏是否结束""" self.timer = QTimer() interval = 500 self.timer.start(interval) self.timer.timeout.connect(self.gameOver) def gameOver(self): """判断游戏是否结束""" if self.game.isGameOver is True: self.background.show() self.startButton.show() self.timer.disconnect() self.game.isGameStart = False self.game.restartButton.hide() self.game.gameOverImage.hide()
class Tetris(QWidget): screenWidth = None # 窗口宽度 screenHeight = None # 窗口高度 isGameStart = None # 游戏是否开始 isGameOver = None # 游戏是否结束 isPause = None # 游戏是否暂停 pauseButton = None # 暂停游戏按钮 resumeButton = None # 继续游戏按钮 restartButton = None # 重新开始游戏按钮 gameOverImage = None # 游戏结束时显示的图片 blockSize = None # 一个方块的大小(像素px) allRows = None # 所有的行 allColumns = None # 所有的列 allBlock = None # 二维数组, 记录方块 currentRow = None # 当前行 currentColumn = None # 当前列 dropTimer = None # 方块下降的定时器 updateTimer = None # 屏幕更新的定时器 removeBlockTimer = None # 消除方块的定时器 dropInterval = None # 方块下降定时器的时间间隔 updateInterval = None # 屏幕更新定时器的时间间隔 removeBlockInterval = None # 消除方块定时器的时间间隔 blocks = None # 枚举所有的方块 blockDict = None # 存储方块属性的字典 nextBlockDict = None # 存储下一个方块属性的字典 block = None # 当前的方块 shape = None # 当前方块的类型 index = None # 当前方块类型的下标 score = None # 得分情况 pixmap = None # 临时存储图片路径 paint = None # 画笔 font = None # 字体 def __init__(self, parent=None): super(Tetris, self).__init__(parent) self.screenWidth = 900 self.screenHeight = 800 self.setFocusPolicy(Qt.StrongFocus) # 设置焦点, 监听键盘 self.resize(self.screenWidth, self.screenHeight) self.initButton() self.initImage() def initButton(self): """初始化重新开始游戏的按钮""" # 暂停游戏按钮 self.pauseButton = QPushButton(self) self.pauseButton.setObjectName('pauseButton') self.pauseButton.setShortcut('P') self.pauseButton.setToolTip('暂停') # 悬停在按钮上的提示->暂停 self.pauseButton.move(self.screenWidth - 210, 5) # 按钮的位置 self.pauseButton.hide() # 默认隐藏 self.pauseButton.clicked.connect(lambda: { self.pause(), self.pauseButton.hide(), self.resumeButton.show(), }) # 继续游戏按钮 self.resumeButton = QPushButton(self) self.resumeButton.setObjectName('resumeButton') self.resumeButton.setToolTip('继续') # 悬停在按钮上的提示->继续 self.resumeButton.move(self.screenWidth - 210, 5) # 按钮的位置 self.resumeButton.hide() # 默认隐藏 self.resumeButton.clicked.connect(lambda: { self.resume(), self.resumeButton.hide(), self.pauseButton.show(), }) # 重新开始游戏按钮 self.restartButton = QPushButton(self) self.restartButton.setObjectName('restartButton') self.restartButton.move(self.screenWidth // 2 - 200, self.screenHeight // 2 - 50) self.restartButton.hide() # 默认隐藏 self.restartButton.clicked.connect(self.gameOver) def initImage(self): """初始化游戏结束的图片""" self.gameOverImage = QLabel(self) self.gameOverImage.setPixmap(QPixmap('./icons/game_over.png')) self.gameOverImage.move(self.screenWidth // 24, self.screenHeight // 4) self.gameOverImage.hide() # 默认隐藏 def initSetting(self): """初始化方块的一些初始值""" self.blocks = { 'L Shape': [[[0, 0], [0, -1], [0, -2], [1, -2]], [[-1, -1], [0, -1], [1, -1], [-1, -2]], [[-1, 0], [0, 0], [0, -1], [0, -2]], [[-1, -1], [0, -1], [1, -1], [1, 0]]], 'J Shape': [[[0, 0], [0, -1], [0, -2], [-1, -2]], [[-1, 0], [-1, -1], [0, -1], [1, -1]], [[0, 0], [1, 0], [0, -1], [0, -2]], [[-1, -1], [0, -1], [1, -1], [1, -2]]], 'Z Shape': [[[-1, 0], [0, 0], [0, -1], [1, -1]], [[0, 0], [0, -1], [-1, -1], [-1, -2]], [[-1, 0], [0, 0], [0, -1], [1, -1]], [[0, 0], [0, -1], [-1, -1], [-1, -2]]], 'S Shape': [[[-1, 0], [-1, -1], [0, -1], [0, -2]], [[0, 0], [1, 0], [-1, -1], [0, -1]], [[-1, 0], [-1, -1], [0, -1], [0, -2]], [[0, 0], [1, 0], [-1, -1], [0, -1]]], 'O Shape': [[[-1, 0], [0, 0], [-1, -1], [0, -1]], [[-1, 0], [0, 0], [-1, -1], [0, -1]], [[-1, 0], [0, 0], [-1, -1], [0, -1]], [[-1, 0], [0, 0], [-1, -1], [0, -1]]], 'I Shape': [[[0, 0], [0, -1], [0, -2], [0, -3]], [[-2, -1], [-1, -1], [0, -1], [1, -1]], [[0, 0], [0, -1], [0, -2], [0, -3]], [[-2, -1], [-1, -1], [0, -1], [1, -1]]], 'T Shape': [[[-1, -1], [0, -1], [1, -1], [0, -2]], [[0, 0], [-1, -1], [0, -1], [0, -2]], [[0, 0], [-1, -1], [0, -1], [1, -1]], [[0, 0], [0, -1], [1, -1], [0, -2]]] } self.score = 0 self.blockSize = 40 # 方块的大小 self.allRows = 20 # 总共20行 self.allColumns = 15 # 总共15列 self.currentRow = self.allRows + 4 # +4行是用来放置待出现的方块的 self.currentColumn = self.allColumns // 2 self.allBlock = [[0 for row in range(self.allColumns)] for column in range(self.allRows + 5)] self.allBlock[0] = [1 for column in range(self.allColumns)] # 用来判断方块是否到底 # print(self.allBlock) def initFont(self): """初始化字体""" # 使用本地字体 fontID = QFontDatabase.addApplicationFont('./Font/Consolas Italic.ttf') self.font = QFont() self.font.setFamily(QFontDatabase.applicationFontFamilies(fontID)[0]) self.font.setItalic(True) # 斜体 self.font.setBold(True) # 粗体 self.font.setPixelSize(40) # 字体大小 def initTimer(self): """初始化定时器""" # 方块下降的定时器 self.dropTimer = QTimer(self) self.dropInterval = 500 # 每0.5秒下降一格 self.dropTimer.start(self.dropInterval) self.dropTimer.timeout.connect(self.blockDrop) # paintEvent更新的定时器 self.updateTimer = QTimer(self) self.updateInterval = 10 self.updateTimer.start(self.updateInterval) self.updateTimer.timeout.connect(self.update) # 消除方块的定时器 self.removeBlockTimer = QTimer(self) self.removeBlockInterval = 150 self.removeBlockTimer.start(self.removeBlockInterval) self.removeBlockTimer.timeout.connect(self.removeBlock) def getBlock(self): """获取方块""" shape = random.choice(list(self.blocks.keys())) # 选择随机方块的类型 index = random.randint(0, 3) # if shape == 'L Shape' and index == 3: # pass block = self.blocks[shape][index] blockDict = { 'shape': shape, 'index': index, 'block': block, } return blockDict def getCurrentBlock(self): """获取目前的方块""" self.blockDict = self.nextBlockDict self.shape = self.blockDict['shape'] self.index = self.blockDict['index'] self.block = self.blockDict['block'] self.nextBlockDict = self.getBlock() def blockDrop(self): """每运行一次, 方块下降一格, 通过QTimer每隔一定时间运行一次""" for position1 in self.block: x = position1[0] + self.currentColumn # x->column y = position1[1] + self.currentRow # y->row # print(x, y) if self.allBlock[y - 1][x] == 1: for position2 in self.block: self.allBlock[position2[1] + self.currentRow][position2[0] + self.currentColumn] = 1 break else: # 下落方块没有接触到其他方块或者没有到底, 继续下降 self.currentRow -= 1 return # 判断游戏结束 if 1 in self.allBlock[self.allRows]: self.pause() self.update() self.removeBlockTimer.disconnect() self.updateTimer.disconnect() self.pauseButton.hide() self.gameOverImage.show() self.restartButton.show() return # 方块下落完成, 获取下一个方块 self.getCurrentBlock() self.currentRow = self.allRows + 4 self.currentColumn = self.allColumns // 2 def removeBlock(self): """消除方块""" # 叠满一行时消除方块, 从上往下消除 for row in range(self.allRows, 0, -1): if 0 not in self.allBlock[row]: # 消除方块时触发音效, 消除一行触发一次 player = QMediaPlayer(self) player.setMedia( QMediaContent( QUrl.fromLocalFile('./AudioFrequency/dingdong.mp3'))) player.play() self.allBlock.pop(row) # 即删即增 self.allBlock.append([0 for column in range(self.allColumns)]) self.score += 1 break def blockMove(self, movePosition): """左右移动方块movePosition>0 代表向右移动一格 <0 代表向左移动一格""" for position in self.block: x = position[0] + self.currentColumn + movePosition y = position[1] + self.currentRow if x < 0 or x > self.allColumns - 1 or y > self.allRows: # 说明方块左右移动出边界了 return elif self.allBlock[y][x] == 1: # 说明方块左右移动碰到方块了 return else: self.currentColumn += movePosition def rotate(self): """顺时针旋转方块""" for position in self.blocks[self.shape][(self.index + 1) % 4]: x = position[0] + self.currentColumn y = position[1] + self.currentRow # print(x, y) if x < 0 or x > self.allColumns - 1 or y > self.allRows: # 说明方块旋转时候出边界了 return elif self.allBlock[y][x] == 1: # 说明方块旋转时候碰到方块了 return else: self.index += 1 # print(self.blocks[self.shape][self.index % 4]) self.block = self.blocks[self.shape][self.index % 4] def start(self): """开始游戏""" self.isGameStart = True self.isGameOver = False self.isPause = False self.pauseButton.show() self.initSetting() self.initFont() self.initTimer() self.nextBlockDict = self.getBlock() self.getCurrentBlock() def pause(self): """游戏暂停""" self.isPause = True self.dropTimer.disconnect() def resume(self): """游戏继续""" self.isPause = False self.dropTimer.start(self.dropInterval) self.dropTimer.timeout.connect(self.blockDrop) def gameOver(self): """游戏结束""" self.isGameOver = True def paintEvent(self, event): """重写paintEvent, 使用QTimer, 每10ms调用一次""" self.paint = QPainter(self) self.paint.begin(self) # 开始重绘 if self.isGameStart is True: penColor = QColor(255, 255, 255) # 白色 # backgroundColor = QColor(255, 192, 203) # 粉色 self.paint.setPen(QPen(penColor, 2, Qt.SolidLine, Qt.RoundCap)) # 白色, self.pixmap = QPixmap('./icons/game_background.png') self.paint.drawPixmap( QRect(0, 0, self.screenWidth, self.screenHeight), self.pixmap) # 背景图片 self.paint.drawLine(self.screenWidth - 300, 0, self.screenWidth - 300, self.screenHeight) # 分割线 # 绘制正在下落的方块 for position in self.block: x = position[0] + self.currentColumn y = position[1] + self.currentRow self.paint.drawPixmap( QRect(x * self.blockSize, (self.allRows - y) * self.blockSize, self.blockSize, self.blockSize), QPixmap('./icons/block.png')) # 绘制静态方块 for row in range(1, self.allRows + 1): for column in range(self.allColumns): if self.allBlock[row][column] == 1: self.paint.drawPixmap( QRect(column * self.blockSize, (self.allRows - row) * self.blockSize, self.blockSize, self.blockSize), QPixmap('./icons/fill_block.png')) # 绘制下一个出现的方块 for position in self.nextBlockDict['block']: x = position[0] + 18.5 # 18.5是740px/40px(方块大小) y = position[1] + 12.5 # 7.5是500px/40px(方块大小) 从下往上 self.paint.drawPixmap( QRect(int(x * self.blockSize), int((self.allRows - y) * self.blockSize), self.blockSize, self.blockSize), QPixmap('./icons/block.png')) # 绘制得分情况 self.paint.setFont(self.font) self.paint.drawText(self.screenWidth - 250, 150, 'Score: %d' % self.score) self.paint.end() # 结束重绘 def keyPressEvent(self, event): """重写keyPressEvent""" if self.isGameOver is False and self.isPause is False: if event.key() == Qt.Key_A: self.blockMove(-1) elif event.key() == Qt.Key_D: self.blockMove(1) if event.key() == Qt.Key_W: self.rotate() if event.key() == Qt.Key_S: # 加速下降, 加速一个方格 self.blockDrop()
class PokerTable(QMainWindow): exit_table = pyqtSignal() def __init__(self, api_obj: api.API, connect_ssl, host, port, table_id, bb, socks_ip, socks_port, socks5, parent_window, cards, spectator: bool, currency_unit: str): """ :type connect_ssl: bool :type socks5: bool :type cards: dict """ super(PokerTable, self).__init__() self.spectator = spectator self.currency_unit = currency_unit self.parent_window = parent_window self.api = api_obj self.socks5 = socks5 self.socks_port = socks_port self.socks_ip = socks_ip self.connect_ssl = connect_ssl self.bb = bb self.table_id = table_id self.port = port self.host = host self.cards = cards self.thread_pool = [ ] # used to store reference to all newly created threads self.close_overrider = False # decides whether to show messsage box or not global CARDS CARDS = self.cards self.ui = table_window.Ui_MainWindow() self.ui.setupUi(self) self.curr_progress_bar = self.ui.progressBar_pl_1 # signals self.ui.lineEdit_chat.returnPressed.connect(self.prepare_message_send) # [!] call, fold and raise request self.ui.pushButton_call.clicked.connect(partial(self.CRF, 'call')) self.ui.pushButton_fold.clicked.connect(partial(self.CRF, "fold")) self.ui.pushButton_raise.clicked.connect(partial(self.CRF, "raise")) self.ui.pushButton_quit.clicked.connect(self.prepare_quit_table) # change in input amt will be retained in the next turn self.ui.spinBox.valueChanged.connect(self.change_input_amt) # [!] set size policy for all other widgets self.set_size_retain_policy() # [!] HIDE buttons at start self.hide_action_buttons() # [!] Hide all Dealer buttons for label in self.findChildren(QLabel): label: QLabel if label.objectName().endswith('dealer'): label.setVisible(False) self.hide_show() # prepare sound file for sound at myturn=true self.sound_is_muted = False self.ui.pushButton_sound.setIcon(QIcon(UNMUTE_ICON)) if MULTIMEDIA_PACKAGE: self.ui.pushButton_sound.clicked.connect(self.mute_sound) else: # if multimedia package not found self.mute_sound() # set button icon to mute self.ui.pushButton_sound.setDisabled(True) # set button disabled # [!] initialize thread self.request_thread = QtCore.QThread() self.thread_pool.append(self.request_thread) if not self.spectator: self.ui.statusbar.clearMessage() self.ui.statusbar.showMessage("Joining table...", 20 * 60 * 1000) # [!] Sets plain text for all labels accepting API data self.set_plain_text_format() # [!] table update timer self.start_time_loop() def hide_action_buttons(self): self.ui.pushButton_raise.setVisible(False) self.ui.pushButton_fold.setVisible(False) self.ui.pushButton_call.setVisible(False) self.ui.spinBox.setVisible(False) self.ui.horizontalSlider_amt.setVisible(False) def mute_sound(self): try: if self.sound_is_muted: self.ui.pushButton_sound.setIcon(QIcon(UNMUTE_ICON)) self.sound_is_muted = False else: self.ui.pushButton_sound.setIcon(QIcon(MUTE_ICON)) self.sound_is_muted = True except (FileNotFoundError, FileExistsError): self.ui.pushButton_sound.setIcon(QIcon(MUTE_ICON)) self.sound_is_muted = True def progress_bar_timer(self, progressbar: QProgressBar): self.m_2_countdown = QTimer() self.m_2_countdown.timeout.connect( partial(self.update_progress_bar_value, progressbar)) self.m_2_countdown.start(1000) def update_progress_bar_value(self, progressbar: QProgressBar, set_value=None): if set_value is not None: self.progress_curr_time = set_value # after each 15 sec this function called from API results and the # current timer countdown gets corrected if self.progress_curr_time > 0: self.progress_curr_time -= 1 # update after each 1 sec and decrease the time count from 2 mins to 0 sec gradually else: self.progress_curr_time = 0 self.m_2_countdown.stop() progressbar.setValue(self.progress_curr_time) self.update() def set_plain_text_format(self): # format for name labels regex_label_name = QRegularExpression("label_pl_[1-6]_name") for label_name in self.findChildren(QLabel, regex_label_name, Qt.FindChildrenRecursively): label_name: QLabel label_name.setTextFormat(Qt.PlainText) # format for stack of each player regex_label_btc = QRegularExpression("label_pl_[1-6]_btc") for label_btc in self.findChildren(QLabel, regex_label_btc, Qt.FindChildrenRecursively): label_name: QLabel label_btc.setTextFormat(Qt.PlainText) # format for bet amount of each player regex_label_bet = QRegularExpression("label_pl_[1-6]_bet_amt") for label_bet in self.findChildren(QLabel, regex_label_bet, Qt.FindChildrenRecursively): label_name: QLabel label_bet.setTextFormat(Qt.PlainText) # for pot and result label self.ui.label_result.setTextFormat(Qt.PlainText) self.ui.label_pot.setTextFormat(Qt.PlainText) def set_size_retain_policy(self): """ retain size for all widgets that may need to be hidden :return: None """ widget_types = [ QLabel, QProgressBar, QFrame, QPushButton, QSpinBox, QSlider, QSpacerItem ] for widget in widget_types: for parent_widgets in self.findChildren( widget, options=Qt.FindChildrenRecursively): size_policy = parent_widgets.sizePolicy() size_policy.setRetainSizeWhenHidden(True) parent_widgets.setSizePolicy(size_policy) def prepare_quit_table(self): params = ('GET /json/table/{table_id}/quit', None, { 'table_id': self.table_id }) self.request_ = RequestThread.RequestThread(params, self.api) self.request_.moveToThread(self.request_thread) self.request_.resp.connect(self.quit_table) self.request_.error_signal.connect( partial(self.user_retry, 'quit_table')) self.request_.complete.connect(self.close_request_thread) self.request_thread.started.connect(self.request_.run) self.request_thread.start() show_request_status(self) def quit_table(self, res_list: list): self.ui.statusbar.clearMessage() res = res_list[0] if res is not None and res == {}: self.close_timers() self.close_all_threads() self.close_overrider = True self.close() self.exit_table.emit( ) # signal to close this window and show the join list else: show_message(ERROR_POPUP_TITLE, ERROR_MSG, 'error') # hide/display specific widgets for player def hide_show(self, show=False, pos=None): for i in range(1, 7): pl_frame = self.findChildren(QFrame, f"frame_pl_{i}")[0] try: if show and pos == i: pl_frame.setVisible(True) elif (not show and not pos) or (not show and pos == i): pl_frame.setVisible(False) if i in [ 1, 4 ]: # hide/show progress bar for player 1 and player 4, rest of the progress bars get # hidden with the frame_pl_{n} progress_bar = self.findChild(QProgressBar, f'progressBar_pl_{i}') clock_icon = self.findChild(QLabel, f"clock_label_{i}", Qt.FindChildrenRecursively) if show and pos == i: progress_bar.setVisible(True) clock_icon.setVisible(True) elif (not show and not pos) or (not show and pos == i): progress_bar.setVisible(False) clock_icon.setVisible(False) frame_map = { 2: 16, 3: 19, 5: 17, 6: 15 } # hides all frames for players [2,3,5,6] if i in frame_map: frame = self.findChild(QFrame, f"frame_{frame_map[i]}") if show and pos == i: frame.setVisible(True) elif not show and not pos: frame.setVisible(False) elif not show and pos == i: frame.setVisible(False) except (AttributeError, KeyError): if show_message(ERROR_POPUP_TITLE, ERROR_MSG, 'warning'): self.hide_show(show, pos) if show is False and pos is None: self.ui.frame_table_top.setVisible(False) def update_player_view(self, players): frame = self.findChild(QFrame, f'frame_pl_{players[0]}', options=Qt.FindChildrenRecursively) frame.setVisible(False) self.hide_show(pos=int(players[0])) def update_progressbar(self, players, mode='hide'): for player_index, player in enumerate(players, 1): progress_bar = self.findChild(QProgressBar, f"progressBar_pl_{player}", Qt.FindChildrenRecursively) clock_icon = self.findChild(QLabel, f"clock_label_{player}", Qt.FindChildrenRecursively) if mode == 'show': progress_bar.setVisible(True) clock_icon.setVisible(True) else: progress_bar.setHidden(True) clock_icon.setHidden(True) def update_player_cards(self, players, mode='hide'): label_card_1 = self.findChild(QLabel, f"label_p{players[0]}c{1}", options=Qt.FindChildrenRecursively) label_card_2 = self.findChild(QLabel, f"label_p{players[0]}c{2}", options=Qt.FindChildrenRecursively) avatar = self.findChild(QLabel, f"pl_{players[0]}_avatar") visibility = True if mode == 'show' else False label_card_1.setVisible(visibility) label_card_2.setVisible(visibility) avatar.setVisible(not visibility) def close_request_thread(self): try: self.request_thread.quit() self.request_thread.disconnect() except Exception as e: pass def start_time_loop(self): """ calls: get /json/table/:id :return: """ self.s_15_timer = QTimer() self.s_15_timer.timeout.connect(self.update_time) self.curr_time = 0 self.timer_pause = False self.prepare_update() self.s_15_timer.start(1000) def update_time(self): """ gets called after each 1 second :return: """ if not self.timer_pause: self.curr_time += 1 if self.curr_time == TABLE_REFRESH_TIME: self.curr_time = 0 self.prepare_update() self.update() def update_players(self, players): def player_details(): if player.get('name'): label_name = self.findChild(QLabel, f"label_pl_{position}_name", options=Qt.FindChildrenRecursively) label_name.setText(str(player['name'])) if 'stack' in player: label_stack = self.findChild( QLabel, f"label_pl_{position}_btc", options=Qt.FindChildrenRecursively) label_stack.setText(f"{player['stack']}{self.currency_unit}") def player_cards(): if player.get('card1') and player.get("card2"): # [!] card 1 self.update_player_cards([position], mode='show') label_card_1 = self.findChild( QLabel, f"label_p{position}c{1}", options=Qt.FindChildrenRecursively) card_1_name = player['card1'] if card_1_name and card_1_name in CARDS: label_card_1.setPixmap(CARDS[card_1_name]) else: # if some invalid card name is received then card image is hidden label_card_1.setVisible(False) # [!] card 2 label_card_2 = self.findChild( QLabel, f"label_p{position}c{2}", options=Qt.FindChildrenRecursively) card_2_name = player['card2'] if card_2_name and card_2_name in CARDS: label_card_2.setPixmap(CARDS[card_2_name]) else: # if some invalid card name is received then card image is hidden label_card_2.setVisible(False) else: self.update_player_cards([position], mode='hide') def players_turn(): progress_bar = self.findChild(QProgressBar, f"progressBar_pl_{position}", options=Qt.FindChildrenRecursively) progress_bar: QProgressBar if player.get('timeleft'): self.update_progressbar( [position], mode='show') # show progress bar + clock icon progress_bar.setMaximum( PROGRESS_BAR_MAX ) # since using time left parameter, so setting it to calculate in percentage if player.get("timeleft") and str( player.get("timeleft")).isdigit(): value = int( int(player['timeleft']) / 100 * PROGRESS_BAR_MAX) try: if self.m_2_countdown.isActive( ) and self.curr_progress_bar != progress_bar: self.m_2_countdown.stop() self.m_2_countdown.disconnect() self.curr_progress_bar = progress_bar self.progress_bar_timer(progress_bar) self.progress_curr_time = value else: self.m_2_countdown.stop() self.m_2_countdown.timeout.disconnect() self.progress_bar_timer( progress_bar) # after stop activation self.curr_progress_bar = progress_bar self.progress_curr_time = value except AttributeError: self.progress_bar_timer( progress_bar) # first activation self.progress_curr_time = value else: progress_bar.setVisible(False) self.update_progressbar([position], 'hide') def player_is_dealer(): label_dealer = self.findChild(QLabel, f"label_pl_{position}_dealer", options=Qt.FindChildrenRecursively) if player.get('button'): # [!] need to display or hide the "Dealer" sign if player.get('button') is True: label_dealer.setVisible(True) else: label_dealer.setVisible(False) else: label_dealer.setVisible(False) def players_bet(): label_bet = self.findChild(QLabel, f"label_pl_{position}_bet") label_bet_amt = self.findChild(QLabel, f"label_pl_{position}_bet_amt") if player.get('bet') and str(player.get('bet')).isdigit( ): # shows bet amt when bet is not null and # don't show if bet isn't greater than 0, and if it's an integer label_bet.setVisible(True) label_bet_amt.setVisible(True) label_bet_amt.setText(str(player.get('bet'))) else: label_bet.setVisible(False) label_bet_amt.setVisible(False) for player_index in range(1, 7): if str(player_index) in players: player = players[str(player_index)] else: self.update_player_view([player_index]) continue player: Dict position = player['position'] self.hide_show(show=True, pos=int( position)) # displays the frame and the players attributes # player view initializations player_details() player_is_dealer() players_bet() player_cards() players_turn() def update_action_buttons(self, table_data: Dict): try: self.ui.pushButton_fold.setVisible(True) # display fold button mybet = int(table_data['mybet']) max_bet = int(table_data['maxbet']) mystack = int(table_data['mystack']) bb = int(self.bb) # table.bb from /json/tables if mybet >= max_bet: self.ui.pushButton_call.setVisible(True) self.ui.pushButton_call.setText("CHECK") if mybet < max_bet: self.ui.pushButton_call.setVisible(True) self.ui.pushButton_call.setText("CALL") if mystack >= ( max_bet - mybet + bb): # display raise button with slider and input number self.ui.horizontalSlider_amt.setVisible(True) # show slider self.ui.spinBox.setVisible(True) # show input number self.ui.pushButton_raise.setVisible(True) # show raise button # set minimum if (2 * max_bet) > bb: self.ui.spinBox.setMinimum(2 * max_bet) self.ui.horizontalSlider_amt.setMinimum(2 * max_bet) else: self.ui.spinBox.setMinimum(bb) self.ui.horizontalSlider_amt.setMinimum(bb) # set maximum self.ui.spinBox.setMaximum(mystack + mybet) self.ui.horizontalSlider_amt.setMaximum(mystack + mybet) self.ui.spinBox.setValue(self.ui.spinBox.minimum()) if not self.sound_is_muted and MULTIMEDIA_PACKAGE: beep_sound() # play the beep sound except Exception as e: show_message(ERROR_POPUP_TITLE, ERROR_MSG, 'error') def change_input_amt(self, value): """ called when value in input amt box is changed """ self.last_input_amt = value def update_board(self, table_data: Dict): if 'board' in table_data: # [!] data:{board:[...]} --> Table top 5 cards list # [!] Show/hide 5 card images board = table_data['board'] self.ui.frame_table_top.setVisible( True ) # this will display 5 card images with result and pot label, unavailable data will # set corresponding label to hide (For example: if there's no result, label_result will be hidden) for card_index, card in enumerate(board): label = self.findChild(QLabel, f"label_c{card_index + 1}", options=Qt.FindChildrenRecursively) if card and card in CARDS: label.setVisible(True) label.setPixmap( CARDS[card] ) # CARDS[card] returns QPixmap object, loaded at the start from Image files else: label.setVisible(False) else: self.ui.frame_table_top.setVisible(False) if table_data.get('result') and table_data.get('result'): # [!] shows the result self.ui.label_result.setVisible(True) result = str(table_data.get('result')) self.ui.label_result.setText(result) else: self.ui.label_result.setVisible(False) if 'pot' in table_data and str(table_data.get('pot')).isdigit(): self.ui.label_pot.setVisible(True) self.ui.label_pot.setText( f"{table_data['pot']}{self.currency_unit}") else: self.ui.label_pot.setVisible(False) def update_messages(self, res: Dict): chat_widget.delete_message(self, clear_all=True) messages = res.get("messages") if len(messages) > 5: chat_widget.delete_message(self, clear_all=True) del messages[0] for message in messages: name = message['name'] content = message['message'] timer = message['timer'] if timer and str(timer).isdigit(): chat_widget.add_message(self, name, timer, content) def user_retry(self, call_name): response = show_error_message() loop = QtCore.QEventLoop() QtCore.QTimer.singleShot( 1500, loop.quit) # wait 1.5 seconds before making the next requests loop.exec_() if call_name != 'update_table': self.close_request_thread() if call_name == 'update_table': self.close_table_thread() if response: self.prepare_update() else: self.timer_pause = False elif call_name == 'message' and response: self.prepare_message_send() elif call_name == 'quit_table' and response: self.prepare_quit_table() elif call_name == 'call' and response: self.prepare_call_action() elif call_name == 'check' and response: self.prepare_check_action() elif call_name == 'fold' and response: self.prepare_fold_action() elif call_name == 'raise' and response: self.prepare_raise_action() def prepare_update(self): if not self.spectator: self.ui.statusbar.showMessage("Joining table...", 20 * 60 * 1000) self.timer_pause = True # set timer pause, so that countdown pauses during table update params = tuple( ['GET /json/table/{table_id}', None, { 'table_id': self.table_id }]) if not hasattr(self, 'table_thread'): self.table_thread = QtCore.QThread() self.thread_pool.append(self.table_thread) self.request_ = RequestThread.RequestThread(params, self.api) self.request_.moveToThread(self.table_thread) self.request_.resp.connect(self.update_table) self.request_.error_signal.connect( partial(self.user_retry, 'update_table')) self.request_.complete.connect(self.close_table_thread) self.table_thread.started.connect(self.request_.run) self.table_thread.start() show_request_status(self) def close_table_thread(self): try: self.table_thread.quit() self.table_thread.disconnect() except Exception as e: pass def update_table(self, res_list: list): # updates table view and messages """ This function will basically refresh the table and update the message box :return: """ self.ui.statusbar.clearMessage() self.timer_pause = False res = res_list[0] if res is not None and 'error' not in res and type(res) == dict: if 'data' in res: res: Dict table_data = res['data'] self.update_board(table_data) if table_data.get( 'players') and table_data.get('players') != 'null': players = table_data.get('players') players: Dict[Dict] self.update_players(players) if table_data.get( 'myturn' ) and 'mybet' in table_data and 'maxbet' in table_data and 'mystack' in table_data: self.ui.statusbar.clearMessage( ) # clear "joining table..." status when mystack > 0 self.update_action_buttons(table_data) else: if not self.spectator: self.ui.statusbar.showMessage("Joining table...", 20 * 60 * 1000) self.spectator = False if table_data.get('mystack') is not None: self.spectator = True self.ui.statusbar.clearMessage() self.ui.pushButton_call.setVisible( False) # hide call button self.ui.horizontalSlider_amt.setVisible( False) # hide slider self.ui.spinBox.setVisible(False) # hide input number self.ui.pushButton_raise.setVisible( False) # hide raise button self.ui.pushButton_fold.setVisible( False) # hide fold button if res.get('messages'): self.update_messages(res) def prepare_call_action(self): self.request_ = RequestThread.RequestThread( params=('POST /json/table/{table_id}/call', None, { 'table_id': self.table_id }), api_obj=self.api) self.request_.resp.connect(self.do_call_action) self.request_.error_signal.connect(partial(self.user_retry, 'call')) self.request_.complete.connect(self.close_request_thread) self.request_.moveToThread(self.request_thread) self.request_thread.started.connect(self.request_.run) self.request_thread.start() show_request_status(self) def do_call_action(self, res_list: list): self.ui.statusbar.clearMessage() res = res_list[0] if (res and "error" in res) or (res is None): show_message(ERROR_POPUP_TITLE, ERROR_MSG, 'error') elif res and res.get('table'): self.prepare_update() return 0 def prepare_check_action(self): self.request_ = RequestThread.RequestThread( params=('POST /json/table/{table_id}/check', None, { 'table_id': self.table_id }), api_obj=self.api) self.request_.resp.connect(self.do_check_action) self.request_.error_signal.connect(partial(self.user_retry, 'check')) self.request_.complete.connect(self.close_request_thread) self.request_.moveToThread(self.request_thread) self.request_thread.started.connect(self.request_.run) self.request_thread.start() show_request_status(self) def do_check_action(self, res_list: list): self.ui.statusbar.clearMessage() res = res_list[0] if res is None or 'error' in res: show_message(ERROR_POPUP_TITLE, ERROR_MSG, 'error') elif res and res.get('table'): self.prepare_update() return 0 def prepare_fold_action(self): self.request_ = RequestThread.RequestThread( ('POST /json/table/{table_id}/fold', None, { 'table_id': self.table_id }), api_obj=self.api) self.request_.resp.connect(self.do_fold_action) self.request_.error_signal.connect(partial(self.user_retry, 'fold')) self.request_.complete.connect(self.close_request_thread) self.request_.moveToThread(self.request_thread) self.request_thread.started.connect(self.request_.run) self.request_thread.start() show_request_status(self) def do_fold_action(self, res_list: list): self.ui.statusbar.clearMessage() res = res_list[0] if res and res.get('table'): # res['table'] will be table_id here self.prepare_update() return 0 elif res is None: show_message(ERROR_POPUP_TITLE, ERROR_MSG, 'error') def prepare_raise_action(self): amt_raise = self.ui.horizontalSlider_amt.value() body_bytes = f"amount={amt_raise}".encode('iso-8859-1') self.request_ = RequestThread.RequestThread( ('POST /json/table/{table_id}/raise', body_bytes, { 'table_id': self.table_id }), api_obj=self.api) self.request_.resp.connect(self.do_raise_action) self.request_.error_signal.connect(partial(self.user_retry, 'raise')) self.request_.complete.connect(self.close_request_thread) self.request_.moveToThread(self.request_thread) self.request_thread.started.connect(self.request_.run) self.request_thread.start() show_request_status(self) def do_raise_action(self, res_list: list): self.ui.statusbar.clearMessage() res = res_list[0] if res and res.get('table'): self.prepare_update() return 0 else: show_message(ERROR_POPUP_TITLE, ERROR_MSG, 'error') def CRF(self, request): """ Handles the CALL, RAISE AND FOLD button action :param request: alias for button action performed :return: """ self.hide_action_buttons( ) # hide all action button, if any one is pressed if request == 'call' and self.ui.pushButton_call.text().lower( ) == 'check': request = 'check' if request == 'call': self.prepare_call_action() elif request == 'check': self.prepare_check_action() elif request == 'fold': self.prepare_fold_action() elif request == 'raise': self.prepare_raise_action() def prepare_message_send(self): content = self.ui.lineEdit_chat.text() if content and content.replace(' ', ''): content = content.strip() if len(content ) < 50: # length of each message is less than 50 (49 chars) # post message content body_bytes = f"message={content}".encode('iso-8859-1') if not hasattr(self, 'message_thread'): self.message_thread = QtCore.QThread() self.thread_pool.append(self.message_thread) self.request_ = RequestThread.RequestThread( ('POST /json/table/{table_id}/message', body_bytes, { 'table_id': self.table_id }), self.api) self.request_.resp.connect(self.send_message) self.request_.error_signal.connect( partial(self.user_retry, 'message')) self.request_.complete.connect(self.close_message_thread) self.request_.moveToThread(self.message_thread) self.message_thread.started.connect(self.request_.run) self.message_thread.start() show_request_status(self) self.ui.lineEdit_chat.clear( ) # clear line after message send request posted def send_message(self, res_list): self.ui.statusbar.clearMessage() res = res_list[0] if res is None: show_message(ERROR_POPUP_TITLE, ERROR_MSG, 'error') elif res is not None and 'table' in res: pass else: show_message(ERROR_POPUP_TITLE, ERROR_MSG, 'error') self.prepare_update() # update table after sending message self.update() def close_message_thread(self): try: self.message_thread.quit() self.message_thread.disconnect() except Exception as e: pass def close_all_threads(self): for thread in self.thread_pool: try: thread.quit() thread.disconnect() except Exception as e: pass def close_timers(self): try: self.s_15_timer.stop() if hasattr(self, 'm_2_countdown'): self.m_2_countdown.stop() except Exception as e: pass def closeEvent(self, event: 'QtCore.QEvent') -> None: if event.spontaneous(): message = QMessageBox( QMessageBox.Question, "Close Confirmation - Torpoker", "Are you sure you want to close this window?", QMessageBox.Yes | QMessageBox.Cancel) res = message.exec_() if res == QMessageBox.Yes: self.s_15_timer.stop() self.close_timers() self.close_all_threads() self.exit_table.emit( ) # emit signal to go back to the tables list window self.close() else: event.ignore() else: self.close_timers() self.close_all_threads() self.s_15_timer.stop() self.close()
class OfflineWindow(QMainWindow, Ui_offline): def __init__(self, parent=None): super(OfflineWindow, self).__init__(parent) self.setupUi(self) self.initUI() self.initEvent() self.timer = QTimer(self) self.isPlay = False self.frameIndex = 0 self.output_path = '/home/mmap/work/VSA_Client/sequences/zjz/' def initUI(self): self.setStyleSheet("QGroupBox#gboxMain{border-width:0px;}") self.setProperty("Form", True) self.setWindowFlags(Qt.Widget) self.setWindowTitle("本地视频文件处理") self.widget_alg.move(0, 0) self.widge_title.move(0, 40) self.widget_main.move(0, 70) self.play_show.setText("") self.play_show.setFrameShape(QFrame.Box) self.play_show.setStyleSheet( "border-width: 1px;border-style: solid;border-color: rgb(128,128,128);" ) self.play_show2.setText("") self.play_show2.setFrameShape(QFrame.Box) self.play_show2.setStyleSheet( "border-width: 1px;border-style: solid;border-color: rgb(128,128,128);" ) # str_bg = ["背景建模", "KNN", "MOG2"] # self.addCboxItem(self.cbox_bg, str_bg) # str_od = ["行人检测", "YOLOV3_tiny", "YOLOV3"] # self.addCboxItem(self.cbox_od, str_od) # str_sot = ["单目标跟踪", "SiamFC", "SiamRPN", "SiamRPN-CIR"] # self.addCboxItem(self.cbox_sot, str_sot) # str_mot = ["多目标跟踪", "DeepSort"] # self.addCboxItem(self.cbox_mot, str_mot) def initEvent(self): self.btn_back2online.clicked.connect( self.back2onlineHandle) #点击了"返回在线处理"按钮,则执行back2onlineHandle函数 self.btn_open.clicked.connect(self.open_query_video) self.btn_play.clicked.connect(self.label_click) def addCboxItem(self, target, items): for i in range(len(items)): target.addItem(items[i]) def back2onlineHandle(self): pass # self.myMain=MyMainWindow(self.widget_show) # self.myMain.setAttribute(Qt.WA_DeleteOnClose) # self.myMain.show() # self.myOffline.move(0, 0) # self.myOffline = OfflineWindow(self.widget_show) # self.myOffline.setAttribute(Qt.WA_DeleteOnClose) # self.widget_alg.hide() # 这一行去掉了还能用,搞清楚它是干嘛的 # self.widget_main.hide() # 这一行去掉了还能用,搞清楚它是干嘛的 # self.myOffline.show() # self.myOffline.move(0, 0) # 这一行去掉了还能用,搞清楚它是干嘛的 def open_query_video(self): self.query_video, fileType = QFileDialog.getOpenFileName( self, 'Open a query video', './source', '*.mp4;;*.avi;;All Files(*)') self.vdo = cv2.VideoCapture(self.query_video) self.isPlay = True self.btn_play.setText("暂停") self.timer.timeout.connect(self.play) self.timer.start(50) def play(self): ret, frame = self.vdo.read() if ret: frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) frame = cv2.resize(frame, (850, 480)) height, width, channel = frame.shape bytesPerLine = 3 * width qimg = QtGui.QImage(frame.data, width, height, bytesPerLine, QtGui.QImage.Format_RGB888) qimg = QtGui.QPixmap.fromImage(qimg) self.play_show.setPixmap(qimg) self.play_show.setScaledContents(True) self.frameIndex += 1 else: self.timer.disconnect() def playFrame(self): frame_index = '%04d' % self.frameIndex frame_path = self.output_path + frame_index + '.jpg' frame = cv2.imread(frame_path) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) frame = cv2.resize(frame, (850, 480)) height, width, channel = frame.shape bytesPerLine = 3 * width qimg = QtGui.QImage(frame.data, width, height, bytesPerLine, QtGui.QImage.Format_RGB888) qimg = QtGui.QPixmap.fromImage(qimg) self.play_show2.setPixmap(qimg) self.play_show2.setScaledContents(True) self.frameIndex += 1 def label_click(self): if self.isPlay == True: self.btn_play.setText("播放") self.timer.disconnect() self.isPlay = False else: self.btn_play.setText("暂停") self.isPlay = True self.play_show.hide() self.play_show2.show() self.timer.timeout.connect(self.playFrame) self.timer.start()
class SeeingMonitor(QMainWindow, Ui_MainWindow): def __init__(self): super(SeeingMonitor, self).__init__() self.setupUi(self) self.Camera = None self.THRESH = None self.threshold_auto = False self.frame = None self.draw_only_frame = None self.video_source = VideoSource.NONE self.export_video = False self.select_noiseArea = False self.coordinates_noiseArea = [] self.lineedit_path.setText(QDir.currentPath()) self.lineedit_filename.setText("seeing.csv") self.save_filename = None self._updateFileSave() self.pause_pressed = False self.datetimeedit_start.setMinimumDateTime(QDateTime.currentDateTime()) self.datetimeedit_end.setMinimumDateTime(QDateTime.currentDateTime()) if platform.system() == 'Linux': self.button_start.setEnabled(False) self.button_settings.setEnabled(False) self.button_start.clicked.connect(self.startLiveCamera) self.button_settings.clicked.connect(self.showSettings) self.button_simulation.clicked.connect(self.startSimulation) self.button_import.clicked.connect(self.importVideo) self.button_export.clicked.connect(self.exportVideo) self.button_noise.clicked.connect(self.selectNoiseArea) self.lineedit_path.textEdited.connect(self._updateFileSave) self.lineedit_filename.textEdited.connect(self._updateFileSave) self.slider_threshold.valueChanged.connect(self._updateThreshold) self.checkbox_thresh.stateChanged.connect(self._updateThresholdState) # Update the Tilt value self.spinbox_b.valueChanged.connect(self._updateFormulaZTilt) self.spinbox_d.valueChanged.connect(self._updateFormulaZTilt) # Update the constants in the FWHM seeing formula self.spinbox_d.valueChanged.connect(self._updateFormulaConstants) self.spinbox_lambda.valueChanged.connect(self._updateFormulaConstants) # Timer for acquiring images at regular intervals self.acquisition_timer = QTimer(parent=self.centralwidget) self.timer_interval = None self._updateThreshold() self._updateFormulaZTilt() self._updateFormulaConstants() # Storing the Delta X and Y in an array to calculate the Standard Deviation self.arr_delta_x = deque(maxlen=100) self.arr_delta_y = deque(maxlen=100) self.plot_length = 1000 self.fwhm_lat = 0 self.fwhm_tra = 0 self.max_lat = 1 self.min_lat = 0 self.max_tra = 1 self.min_tra = 0 self.series_lat = QLineSeries() self.series_lat.setName("Lateral") self.series_tra = QLineSeries() self.series_tra.setName("Transversal") self.chart = QChart() self.chart.addSeries(self.series_lat) self.chart.addSeries(self.series_tra) # self.chart.createDefaultAxes() self.axis_horizontal = QDateTimeAxis() self.axis_horizontal.setMin(QDateTime.currentDateTime().addSecs(-60 * 1)) self.axis_horizontal.setMax(QDateTime.currentDateTime().addSecs(0)) self.axis_horizontal.setFormat("HH:mm:ss.zzz") self.axis_horizontal.setLabelsFont( QFont(QFont.defaultFamily(self.font()), pointSize=5)) self.axis_horizontal.setLabelsAngle(-20) self.chart.addAxis(self.axis_horizontal, Qt.AlignBottom) self.axis_vertical_lat = QValueAxis() self.axis_vertical_lat.setRange(self.max_lat, self.min_lat) self.chart.addAxis(self.axis_vertical_lat, Qt.AlignLeft) self.axis_vertical_tra = QValueAxis() self.axis_vertical_tra.setRange(self.max_tra, self.min_tra) self.chart.addAxis(self.axis_vertical_tra, Qt.AlignRight) self.series_lat.attachAxis(self.axis_horizontal) self.series_lat.attachAxis(self.axis_vertical_lat) self.series_tra.attachAxis(self.axis_horizontal) self.series_tra.attachAxis(self.axis_vertical_tra) self.chart.setTitle("Full Width at Half Maximum") self.chart.legend().setVisible(True) self.chart.legend().setAlignment(Qt.AlignBottom) self.chartView = QChartView(self.chart, parent=self.graphicsView) self.chartView.resize(640, 250) self.chartView.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.chartView.setRenderHint(QPainter.Antialiasing) def closeEvent(self, event): try: self.Camera.StopLive() except AttributeError: pass try: self.cap.release() except AttributeError: pass try: self.video_writer.release() except AttributeError: pass event.accept() def _callbackFunction(self, hGrabber, pBuffer, framenumber, pData): """ This is an example callback function for image processig with opencv. The image data in pBuffer is converted into a cv Matrix and with cv.mean() the average brightness of the image is measuered. :param: hGrabber: This is the real pointer to the grabber object. :param: pBuffer : Pointer to the first pixel's first byte :param: framenumber : Number of the frame since the stream started :param: pData : Pointer to additional user data structure """ if pData.buffer_size > 0: image = C.cast(pBuffer, C.POINTER(C.c_ubyte * pData.buffer_size)) cvMat = np.ndarray(buffer=image.contents, dtype=np.uint8, shape=(pData.height, pData.width, pData.iBitsPerPixel)) frame = np.uint8(cvMat) self.frame = cv2.resize(frame, (640, 480)) self.draw_only_frame = self.frame.copy() self._monitor() def _startLiveCamera(self): # Create a function pointer Callbackfunc = IC.TIS_GrabberDLL.FRAMEREADYCALLBACK( self._callbackFunction) ImageDescription = CallbackUserData() # Create the camera object self.Camera = IC.TIS_CAM() self.Camera.ShowDeviceSelectionDialog() if self.Camera.IsDevValid() != 1: print("[Error Camera Selection] Couldn't open camera device !") # QMessageBox.warning(self, "Error Camera Selection", "Couldn't open camera device !") # raise Exception("Unable to open camera device !") return # Now pass the function pointer and our user data to the library self.Camera.SetFrameReadyCallback(Callbackfunc, ImageDescription) # Handle each incoming frame automatically self.Camera.SetContinuousMode(0) print('Starting live stream ...') self.Camera.StartLive( 0 ) ####### PAUSE LIVE STREAM WHEN PAUSE CLICKED ??? ############################################## # self.Camera.StartLive(1) Imageformat = self.Camera.GetImageDescription()[:3] ImageDescription.width = Imageformat[0] ImageDescription.height = Imageformat[1] ImageDescription.iBitsPerPixel = Imageformat[2] // 8 ImageDescription.buffer_size = ImageDescription.width * ImageDescription.height * ImageDescription.iBitsPerPixel while self.video_source == VideoSource.CAMERA: pass # self.timer_interval = 20 # try: # self.acquisition_timer.disconnect() # except TypeError: # pass # self.acquisition_timer.timeout.connect(self._updateLiveCamera) # self.acquisition_timer.start(self.timer_interval) def startLiveCamera(self): try: self.acquisition_timer.disconnect() except TypeError: pass self.video_source = VideoSource.CAMERA self.button_export.setEnabled(True) self._setPauseButton() # Disable other functionalities # self.button_simulation.setEnabled(False) t = threading.Thread(target=self._startLiveCamera, args=(), daemon=True) t.start() def showSettings(self): if not self.Camera.IsDevValid(): QMessageBox.warning( self, "Camera Selection Error", "Please select a camera first by clicking on the button <strong>Start</strong>" ) return try: self.Camera.ShowPropertyDialog() except Exception as e: logging.error(traceback.format_exc()) QMessageBox.warning(self, "Property Dialog Error", traceback.format_exc()) # def _updateLiveCamera(self): # # Capturing a frame # self.Camera.SnapImage() # frame = self.Camera.GetImage() # frame = np.uint8(frame) # self.frame = cv2.resize(frame, (640, 480)) # self.draw_only_frame = self.frame.copy() # self._monitor() # self.displayParameters() def displayParameters(self): parameters_text = "" ExposureTime = [0] self.Camera.GetPropertyAbsoluteValue("Exposure", "Value", ExposureTime) parameters_text = parameters_text + str(ExposureTime[0]) + "\n" GainValue = [0] self.Camera.GetPropertyAbsoluteValue("Gain", "Value", GainValue) parameters_text = parameters_text + str(GainValue[0]) + "\n" self.parameters_label.setText(parameters_text) self.parameters_label.adjustSize() def startSimulation(self): if self.Camera != None and self.Camera.IsDevValid() == 1: self.Camera.StopLive() self.video_source = VideoSource.SIMULATION self.button_export.setEnabled(True) self._setPauseButton() # Disable other functionalities # self.button_start.setEnabled(False) self.button_settings.setEnabled(False) # Generating fake images of DIMM star (One single star that is split by the DIMM) self.starsGenerator = FakeStars() self.timer_interval = 100 try: self.acquisition_timer.disconnect() except TypeError: pass self.acquisition_timer.timeout.connect(self._updateSimulation) self.acquisition_timer.start(self.timer_interval) def _updateSimulation(self): frame = self.starsGenerator.generate() self.frame = cv2.resize(frame, (640, 480)) self.draw_only_frame = self.frame.copy() self._monitor() ################################################################################################################################################################ def _writeCSV(self, headerOnly=False): if headerOnly: with open(self.save_filename, "w") as csvFile: fieldnames = ["timestamp", "lateral", "transversal", "star"] writer = csv.DictWriter(csvFile, fieldnames=fieldnames) writer.writeheader() csvFile.close() else: with open(self.save_filename, "a") as csvFile: writer = csv.writer(csvFile) writer.writerow([ self.current.toTime_t(), self.fwhm_lat, self.fwhm_tra, self.lineedit_star.text() ]) # csvFile.write(",".join([str(self.current) , str(self.fwhm_lat), str(self.fwhm_tra), self.lineedit_star.text()])) # csvFile.write("\n") # csvFile.close() def selectNoiseArea(self): self.label_info.setText("Please select on the video a noise area.") self.button_noise.setText("Selecting ...") self.coordinates_noiseArea = [] self.select_noiseArea = True def _set_noiseArea(self, x1, y1, x2, y2): if len(self.coordinates_noiseArea) == 0: self.coordinates_noiseArea.append([x1, y1]) self.coordinates_noiseArea.append([x2, y2]) elif len(self.coordinates_noiseArea) == 2: self.coordinates_noiseArea[0] = [x1, y1] self.coordinates_noiseArea[1] = [x2, y2] def _draw_noiseArea(self): if len(self.coordinates_noiseArea) >= 2: cv2.rectangle(self.draw_only_frame, (self.coordinates_noiseArea[0][0], self.coordinates_noiseArea[0][1]), (self.coordinates_noiseArea[1][0], self.coordinates_noiseArea[1][1]), (0, 255, 0), 1) def _updateFileSave(self): self.save_filename = join(self.lineedit_path.text(), self.lineedit_filename.text()) self._writeCSV(headerOnly=True) def _updateThreshold(self): if self.threshold_auto: if self.coordinates_noiseArea.__len__() >= 2: noise_area = self.frame[self.coordinates_noiseArea[0][1]:self. coordinates_noiseArea[1][1], self.coordinates_noiseArea[0][0]:self. coordinates_noiseArea[1][0]] try: self.THRESH = noise_area.max() + 20 # self.THRESH = int(round(noise_area.mean())) except ValueError: return self.slider_threshold.setValue(self.THRESH) self.checkbox_thresh.setText("Threshold ({}, auto)".format( self.THRESH)) else: self.THRESH = self.slider_threshold.value() self.checkbox_thresh.setText("Threshold ({})".format(self.THRESH)) def _updateThresholdState(self, state): if state == 0: self.threshold_auto = False self.slider_threshold.setEnabled(True) else: if self.coordinates_noiseArea.__len__() < 2: QMessageBox.information(self, "Select Noise Area", "Please select the noise area") self.selectNoiseArea() self.threshold_auto = True self.slider_threshold.setEnabled(False) def _updateFormulaZTilt(self): self.spinbox_d.setStyleSheet("QSpinBox { background-color: blue; }") try: b = float(self.spinbox_b.value()) / float(self.spinbox_d.value()) except ZeroDivisionError: QMessageBox.warning(self, "Zero Division Error", "D (Apertures Diameter cannot be Zero") return self.K_lat = 0.364 * (1 - 0.532 * np.power(b, -1 / 3) - 0.024 * np.power(b, -7 / 3)) self.K_tra = 0.364 * (1 - 0.798 * np.power(b, -1 / 3) - 0.018 * np.power(b, -7 / 3)) def _updateFormulaConstants(self): # Calculate value to make process faster self.A = 0.98 * np.power( float(self.spinbox_d.value()) / float(self.spinbox_lambda.value()), 0.2) def _calcSeeing(self): std_x = np.std(self.arr_delta_x) std_y = np.std(self.arr_delta_y) # Seeing self.current = QDateTime.currentDateTime() self.fwhm_lat = self.A * np.power(std_x / self.K_lat, 0.6) self.fwhm_tra = self.A * np.power(std_y / self.K_tra, 0.6) threading.Thread(target=self._plotSeeing, args=(), daemon=True).start() threading.Thread(target=self._writeCSV, args=(), daemon=True).start() self.label_info.setText("lat: " + str(self.fwhm_lat) + " | lon: " + str(self.fwhm_tra)) def _calcSeeing_arcsec(self): std_x = np.std(self.arr_delta_x) std_y = np.std(self.arr_delta_y) # Seeing self.current = QDateTime.currentDateTime() self.fwhm_lat = self.A * np.power( std_x / self.K_lat, 0.6) * 205.0 * self.spinbox_pwidth.value( ) / self.spinbox_focal.value() self.fwhm_tra = self.A * np.power( std_y / self.K_tra, 0.6) * 205.0 * self.spinbox_pheight.value( ) / self.spinbox_focal.value() threading.Thread(target=self._plotSeeing, args=(), daemon=True).start() threading.Thread(target=self._writeCSV, args=(), daemon=True).start() self.label_info.setText("lat: " + str(self.fwhm_lat) + " | lon: " + str(self.fwhm_tra)) def _monitor(self): tic = time.time() gray = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY) self._updateThreshold() _, thresholded = cv2.threshold(gray, self.THRESH, 255, cv2.THRESH_TOZERO) # _, contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) contours = contours[:2] # if contours.__len__() > 2: # QMessageBox.warning(self, "Thresholding error", "More than 2 projections were found. " + \ # "Please increase threshold manually or select a better noise area.") cv2.drawContours(self.draw_only_frame, contours, -1, (0, 255, 0), 2) self._draw_noiseArea() try: moments_star_1 = cv2.moments(contours[0]) moments_star_2 = cv2.moments(contours[1]) except IndexError: print("Only {} were found ! (Must be at least 2)".format( len(contours))) else: try: cX_star1 = int(moments_star_1["m10"] / moments_star_1["m00"]) cY_star1 = int(moments_star_1["m01"] / moments_star_1["m00"]) cX_star2 = int(moments_star_2["m10"] / moments_star_2["m00"]) cY_star2 = int(moments_star_2["m01"] / moments_star_2["m00"]) except ZeroDivisionError: return if self.enable_seeing.isChecked(): delta_x = abs(cX_star2 - cX_star1) delta_y = abs(cY_star2 - cY_star1) self.arr_delta_x.append(delta_x) self.arr_delta_y.append(delta_y) # self._calcSeeing() self._calcSeeing_arcsec() cv2.drawMarker(self.draw_only_frame, (cX_star1, cY_star1), color=(0, 0, 255), markerSize=30, thickness=1) cv2.drawMarker(self.draw_only_frame, (cX_star2, cY_star2), color=(0, 0, 255), markerSize=30, thickness=1) finally: self._displayImage() threading.Thread(target=self._writeVideoFile, args=(), daemon=True).start() toc = time.time() elapsed = toc - tic try: print("FPS max = {}".format(int(1.0 / elapsed))) except ZeroDivisionError: pass def _displayImage(self): qImage = array2qimage(self.draw_only_frame) self.stars_capture.setPixmap(QPixmap(qImage)) def _plotSeeing(self): self.axis_horizontal.setMin(QDateTime.currentDateTime().addSecs(-60 * 1)) self.axis_horizontal.setMax(QDateTime.currentDateTime().addSecs(0)) if self.series_lat.count() > self.plot_length - 1: self.series_lat.removePoints( 0, self.series_lat.count() - self.plot_length - 1) if self.series_tra.count() > self.plot_length - 1: self.series_tra.removePoints( 0, self.series_tra.count() - self.plot_length - 1) if self.fwhm_lat > self.max_lat: self.max_lat = self.fwhm_lat self.axis_vertical_lat.setMax(self.max_lat + 10) if self.fwhm_lat < self.min_lat: self.min_lat = self.fwhm_lat self.axis_vertical_lat.setMax(self.min_lat - 10) if self.fwhm_tra > self.max_tra: self.max_tra = self.fwhm_tra self.axis_vertical_tra.setMax(self.max_tra + 10) if self.fwhm_tra < self.min_tra: self.min_tra = self.fwhm_tra self.axis_vertical_tra.setMax(self.min_tra - 10) # print(self.fwhm_lat, self.fwhm_tra) self.series_lat.append(self.current.toMSecsSinceEpoch(), self.fwhm_lat) self.series_tra.append(self.current.toMSecsSinceEpoch(), self.fwhm_tra) def importVideo(self): self.video_source = VideoSource.VIDEO self.button_export.setEnabled(True) self._setPauseButton() options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog filename, _ = QFileDialog.getOpenFileName( self, "Import from Video File", QDir.currentPath(), "Video Files (*.avi *.mp4 *.mpeg *.flv *.3gp *.mov);;All Files (*)", options=options) if filename: if self.Camera != None and self.Camera.IsDevValid() == 1: self.Camera.StopLive() self.cap = cv2.VideoCapture(filename) # print("CAP_PROP_POS_MSEC :", self.cap.get(cv2.CAP_PROP_POS_MSEC)) # print("CAP_PROP_POS_FRAMES :", self.cap.get(cv2.CAP_PROP_POS_FRAMES)) # print("CAP_PROP_POS_AVI_RATIO :", self.cap.get(cv2.CAP_PROP_POS_AVI_RATIO)) # print("CAP_PROP_FRAME_WIDTH :", self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # print("CAP_PROP_FRAME_HEIGHT :", self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # print("CAP_PROP_FPS :", self.cap.get(cv2.CAP_PROP_FPS)) # print("CAP_PROP_FOURCC :", self.cap.get(cv2.CAP_PROP_FOURCC)) # print("CAP_PROP_FRAME_COUNT :", self.cap.get(cv2.CAP_PROP_FRAME_COUNT)) # print("CAP_PROP_FORMAT :", self.cap.get(cv2.CAP_PROP_FORMAT)) # print("CAP_PROP_MODE :", self.cap.get(cv2.CAP_PROP_MODE)) # print("CAP_PROP_BRIGHTNESS :", self.cap.get(cv2.CAP_PROP_BRIGHTNESS)) # print("CAP_PROP_CONTRAST :", self.cap.get(cv2.CAP_PROP_CONTRAST)) # print("CAP_PROP_SATURATION :", self.cap.get(cv2.CAP_PROP_SATURATION)) # print("CAP_PROP_HUE :", self.cap.get(cv2.CAP_PROP_HUE)) # print("CAP_PROP_GAIN :", self.cap.get(cv2.CAP_PROP_GAIN)) # print("CAP_PROP_EXPOSURE :", self.cap.get(cv2.CAP_PROP_EXPOSURE)) # print("CAP_PROP_CONVERT_RGB :", self.cap.get(cv2.CAP_PROP_CONVERT_RGB)) # print("CAP_PROP_WHITE_APERTURE :", self.cap.get(cv2.CAP_PROP_APERTURE)) # print("CAP_PROP_RECTIFICATION :", self.cap.get(cv2.CAP_PROP_RECTIFICATION)) # print("CAP_PROP_ISO_SPEED :", self.cap.get(cv2.CAP_PROP_ISO_SPEED)) # print("CAP_PROP_BUFFERSIZE :", self.cap.get(cv2.CAP_PROP_BUFFERSIZE)) if self.cap.isOpened() == False: QMessageBox.warning(self, "Import from Video", "Cannot load file '{}'.".format(filename)) return self.timer_interval = round(1000.0 / self.cap.get(cv2.CAP_PROP_FPS)) try: self.acquisition_timer.disconnect() except TypeError: pass self.acquisition_timer.timeout.connect(self._grabVideoFrame) self.acquisition_timer.start(self.timer_interval) def _grabVideoFrame(self): ret, frame = self.cap.read() if ret == True: self.frame = cv2.resize(frame, (640, 480)) self.draw_only_frame = self.frame.copy() self._monitor() else: try: self.acquisition_timer.disconnect() except TypeError: pass QMessageBox.information(self, "Import from Video", "Video complete !") self.cap.release() def exportVideo(self): # if not self.enable_seeing.isChecked(): # answer = QMessageBox.question(self, # "Export to Video File", # "Seeing Monitoring is not activated. Continue ?", # QMessageBox.Yes|QMessageBox.No, # QMessageBox.No) # if answer == QMessageBox.No: # return options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog filename, _ = QFileDialog.getSaveFileName( self, "Export to Video File", QDir.currentPath(), "All Files (*);;Video Files (*.avi *.mp4 *.mpeg *.flv *.3gp *.mov)", options=options) if filename: if splitext(filename)[1] != ".avi": filename = splitext(filename)[0] + ".avi" QMessageBox.information( self, "Export to Video File", "Only '.avi' extension is supported. Video will be saved as '{}'" .format(filename)) print(round(1000.0 / float(self.timer_interval))) self.video_writer = cv2.VideoWriter( filename, cv2.VideoWriter_fourcc(*'MJPG'), round(1000.0 / float(self.timer_interval)), ( 640, 480 ) ################################################################################# ) self.export_video = True def _writeVideoFile(self): current = QDateTime.currentDateTime() if self.export_video and current >= self.datetimeedit_start.dateTime() and \ current < self.datetimeedit_end.dateTime(): # self.video_writer.write(self.frame) self.video_writer.write(self.draw_only_frame) def _setPauseButton(self): self.button_pause.setEnabled(True) self.button_pause.setText("⏸ Pause") self.button_pause.clicked.connect(self._pause) def _pause(self): self.pause_pressed = True # IC_SuspendLive IC_StopLive ################################################################################## self.button_pause.setText("▶ Resume") self.button_pause.clicked.connect(self._resume) if self.video_source == VideoSource.CAMERA: self.Camera.StopLive() else: self.acquisition_timer.stop() def _resume(self): self.pause_pressed = False self._setPauseButton() if self.video_source == VideoSource.CAMERA: self.Camera.StartLive(0) else: try: self.acquisition_timer.disconnect() except TypeError: pass self.acquisition_timer.start(self.timer_interval) if self.video_source == VideoSource.CAMERA: pass elif self.video_source == VideoSource.SIMULATION: self.acquisition_timer.timeout.connect(self._updateSimulation) elif self.video_source == VideoSource.VIDEO: self.acquisition_timer.timeout.connect(self._grabVideoFrame)
class Player(QMainWindow, Ui_MainWindow): play_signal = pyqtSignal(object) IDLE = 0 READY = 1 PLAY = 2 def __init__(self): super(Player, self).__init__() # init UI self.setupUi(self) self.media_player = QMediaPlayer(self) self.video_widget = QVideoWidget(self) self.media_player.setVideoOutput(self.video_widget) self.centralwidget.layout().insertWidget(0, self.video_widget) self.playBtn.clicked.connect(self.play_media) self.pauseBtn.clicked.connect(self.pause_media) self.stopBtn.clicked.connect(self.stop_media) self.progressSlider.originMouseMoveEvent = self.progressSlider.mouseMoveEvent self.progressSlider.mouseMoveEvent = self.progressSlider_mouse_move self.progressSlider.sliderReleased.connect(self.reposition_media) self.progressSlider.setDisabled(True) self.urlLineEdit.setText('rtsp://127.0.0.1:57501/1') # init component self.timer = QTimer(self) # init sockets self.client_rtsp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.client_rtsp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.client_rtp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.client_rtp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.client_rtcp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.client_rtcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # init thread self.client_rtp_thread = None self.play_event = None # init client parameters self.seq = 0 self.client_rtp_port = None self.client_rtcp_port = None self.client_session_id = None self.url = None self.status = self.IDLE self.media_duration = 0 self.current_time = 0 self.init_end_time_label() self.set_play_time() # init cache file self.client_root = os.path.split(os.path.abspath(__file__))[0] self.cache_filename = os.path.join(self.client_root, 'Cache/tmp.ts') self.file = None def setup_play(self, url): url_tup = util.parse_url(url) if not url_tup: QMessageBox.warning(self, 'Warning', 'Invalid URL.') return -1 ip = url_tup[0] port = int(url_tup[1]) path = url_tup[2] try: self.client_rtsp_socket.connect((ip, port)) except Exception as e: QMessageBox.warning(self, 'Warning', 'Error: connect to media server failed.') return -1 # send OPTIONS request_dict = {'CSeq': str(self.seq)} request = rtsp.generate_request('OPTIONS', url, request_dict) self.client_rtsp_socket.send(request.encode()) response = self.client_rtsp_socket.recv(1024).decode() if rtsp.get_status_code(response) != 200: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response code.') return -1 response_dict = rtsp.get_response_dict(response) if int(response_dict.get('CSeq')) != self.seq: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response SN.') return -1 self.seq += 1 # send DESCRIBE request_dict = {'CSeq': str(self.seq), 'Accept': 'application/sdp'} request = rtsp.generate_request('DESCRIBE', url, request_dict) self.client_rtsp_socket.send(request.encode()) response = self.client_rtsp_socket.recv(1024).decode() if rtsp.get_status_code(response) != 200: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response code.') return -1 response_dict = rtsp.get_response_dict(response) if int(response_dict.get('CSeq')) != self.seq: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response SN.') return -1 self.client_rtp_port = util.match_rtp_port(response) if not self.client_rtp_port: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: can not specify RTP port.') return -1 self.client_rtcp_port = self.client_rtp_port+1 self.seq += 1 # setup RTP and RTCP socket self.client_rtp_socket.bind(('127.0.0.1', self.client_rtp_port)) self.client_rtcp_socket.bind(('127.0.0.1', self.client_rtcp_port)) self.status = self.READY # send SETUP request_dict = {'CSeq': str(self.seq), 'Transport': 'RTP/AVP;unicast;client_port=%d-%d' % (self.client_rtp_port, self.client_rtcp_port)} request = rtsp.generate_request('SETUP', url, request_dict) self.client_rtsp_socket.send(request.encode()) response = self.client_rtsp_socket.recv(1024).decode() if rtsp.get_status_code(response) != 200: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response code.') return -1 response_dict = rtsp.get_response_dict(response) if int(response_dict.get('CSeq')) != self.seq: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response SN.') return -1 self.client_session_id = int(response_dict.get('Session')) self.seq += 1 self.status = self.READY # send PLAY request_dict = {'CSeq': str(self.seq), 'Session': self.client_session_id, 'Range': 'npt=0.000-'} request = rtsp.generate_request('PLAY', url, request_dict) self.client_rtsp_socket.send(request.encode()) response = self.client_rtsp_socket.recv(1024).decode() if rtsp.get_status_code(response) != 200: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response code.') return -1 response_dict = rtsp.get_response_dict(response) if int(response_dict.get('CSeq')) != self.seq: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response SN.') return -1 self.seq += 1 self.current_time, self.media_duration = util.match_media_time(response) return 0 def recv_stream(self, cache_filename): cur_seq = 0 self.file = open(cache_filename, 'wb') while True: if self.status == self.READY: self.play_event.wait() if self.status == self.IDLE: break try: data = self.client_rtp_socket.recv(rtp.TS_RTP_PACKET_SIZE) except: continue seq = rtp_packet.get_seq(data) if seq and seq < cur_seq: continue cur_seq = seq payload = rtp_packet.get_payload(data) self.file.write(payload) if self.file: self.file.close() def start_play(self): self.init_progress_slider() self.init_end_time_label() self.timer.stop() self.timer.disconnect() self.timer.timeout.connect(self.update_play_time) self.timer.start(1000) self.progressSlider.setDisabled(False) self.media_player.setMedia(QMediaContent(QUrl.fromLocalFile(self.cache_filename))) self.media_player.play() def progressSlider_mouse_move(self, event): self.progressSlider.originMouseMoveEvent(event) self.current_time = self.progressSlider.value() self.set_play_time() def init_progress_slider(self): self.progressSlider.setMinimum(0) self.progressSlider.setMaximum(self.media_duration) def init_end_time_label(self): self.endTimeLabel.setText('%d:%02d' % (self.media_duration // 60, self.media_duration % 60)) def set_play_time(self): if self.current_time >= self.media_duration: self.stop_media() self.progressSlider.setValue(self.current_time) current_time = self.current_time self.curTimeLabel.setText('%d:%02d' % (current_time // 60, current_time % 60)) def update_play_time(self): self.current_time += 1 self.set_play_time() def closeEvent(self, event): if self.status != self.IDLE: self.stop_media() def play_media(self): if self.status == self.IDLE: # setup and play url = self.urlLineEdit.text() res = self.setup_play(url) if res != -1: # remove cache file if os.path.exists(self.cache_filename): os.remove(self.cache_filename) self.url = url self.client_rtp_thread = threading.Thread(target=self.recv_stream, args=(self.cache_filename,)) self.status = self.PLAY self.play_event = threading.Event() self.client_rtp_thread.start() self.timer.timeout.connect(self.start_play) self.timer.start(3000) elif self.status == self.READY: # send PLAY request_dict = {'CSeq': str(self.seq), 'Session': self.client_session_id, 'Range': 'npt=now-'} request = rtsp.generate_request('PLAY', self.url, request_dict) self.client_rtsp_socket.send(request.encode()) response = self.client_rtsp_socket.recv(1024).decode() if rtsp.get_status_code(response) != 200: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response code.') return response_dict = rtsp.get_response_dict(response) if int(response_dict.get('CSeq')) != self.seq: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response SN.') return self.seq += 1 # resume self.status = self.PLAY self.play_event.set() self.media_player.play() self.timer.start(1000) else: return def pause_media(self): if self.status != self.PLAY: return self.media_player.pause() self.timer.stop() self.play_event.clear() self.status = self.READY # send PAUSE request_dict = {'CSeq': str(self.seq), 'Session': self.client_session_id} request = rtsp.generate_request('PAUSE', self.url, request_dict) self.client_rtsp_socket.send(request.encode()) response = self.client_rtsp_socket.recv(1024).decode() if rtsp.get_status_code(response) != 200: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response code.') return -1 response_dict = rtsp.get_response_dict(response) if int(response_dict.get('CSeq')) != self.seq: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response SN.') return -1 self.seq += 1 def stop_media(self): if self.status == self.IDLE: return request_dict = {'CSeq': str(self.seq), 'Session': self.client_session_id} request = rtsp.generate_request('TEARDOWN', self.url, request_dict) self.client_rtsp_socket.send(request.encode()) response = self.client_rtsp_socket.recv(1024).decode() if rtsp.get_status_code(response) != 200: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response code.') return response_dict = rtsp.get_response_dict(response) if int(response_dict.get('CSeq')) != self.seq: # self.close_rtsp_connection() self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response SN.') return self.seq += 1 self.destroy_connection() def reposition_media(self): if self.status == self.IDLE: return self.pause_media() self.media_player.stop() self.media_player.setMedia(QMediaContent()) self.timer.stop() self.timer.disconnect() self.play_event.clear() self.status = self.READY self.current_time = self.progressSlider.value() self.set_play_time() # reset socket self.client_rtp_socket.shutdown(socket.SHUT_RDWR) self.client_rtp_socket.close() self.client_rtp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.client_rtp_socket.bind(('127.0.0.1', self.client_rtp_port)) # send PLAY request_dict = {'CSeq': str(self.seq), 'Session': self.client_session_id, 'Range': 'npt=%.3f-%.3f' % (self.current_time, self.media_duration)} request = rtsp.generate_request('PLAY', self.url, request_dict) self.client_rtsp_socket.send(request.encode()) response = self.client_rtsp_socket.recv(1024).decode() if rtsp.get_status_code(response) != 200: self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response code.') return response_dict = rtsp.get_response_dict(response) if int(response_dict.get('CSeq')) != self.seq: self.destroy_connection() QMessageBox.warning(self, 'Warning', 'Error: unexpected server response SN.') return self.seq += 1 self.current_time, self.media_duration = util.match_media_time(response) # reset cache file self.file.close() os.remove(self.cache_filename) self.file = open(self.cache_filename, 'wb') # resume self.status = self.PLAY self.play_event.set() self.timer.timeout.connect(self.start_play) self.timer.start(3000) def destroy_connection(self): # reset status self.status = self.IDLE # reset player self.media_player.stop() self.media_player.setMedia(QMediaContent()) # reset RTSP socket self.client_rtsp_socket.shutdown(socket.SHUT_RDWR) self.client_rtsp_socket.close() self.client_rtsp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.client_rtsp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # reset RTP socket self.client_rtp_socket.shutdown(socket.SHUT_RDWR) self.client_rtp_socket.close() self.client_rtp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.client_rtp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # reset RTCP socket self.client_rtcp_socket.shutdown(socket.SHUT_RDWR) self.client_rtcp_socket.close() self.client_rtcp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.client_rtcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # reset thread self.client_rtp_thread = None self.play_event = None # reset timer self.timer.stop() self.timer.disconnect() # reset client parameters self.seq = 0 self.client_rtp_port = None self.client_rtcp_port = None self.client_session_id = None self.url = None self.media_duration = 0 self.current_time = 0 self.init_end_time_label() self.set_play_time() # reset UI self.progressSlider.setDisabled(True) # reset cache file self.file = None
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() settings = QSettings() self.setup_trayicon() self.setup_ui() self.update_work_end_time() self.update_rest_end_time() self.setup_connections() self.timeFormat = "hh:mm:ss" self.time = QTime(0, 0, 0, 0) self.workTime = QTime(0, 0, 0, 0) self.restTime = QTime(0, 0, 0, 0) self.totalTime = QTime(0, 0, 0, 0) self.currentMode = Mode.work self.maxRepetitions = -1 self.currentRepetitions = 0 self.show() def leaveEvent(self, event): super(MainWindow, self).leaveEvent(event) self.tasksTableWidget.clearSelection() def closeEvent(self, event): super(MainWindow, self).closeEvent(event) settings = QSettings() settings.setValue("timer/work/hours", self.workHoursSpinBox.value()) settings.setValue("timer/work/minutes", self.workMinutesSpinBox.value()) settings.setValue("timer/work/seconds", self.workSecondsSpinBox.value()) settings.setValue("timer/rest/hours", self.restHoursSpinBox.value()) settings.setValue("timer/rest/minutes", self.restMinutesSpinBox.value()) settings.setValue("timer/rest/seconds", self.restSecondsSpinBox.value()) tasks = [] for i in range(self.tasksTableWidget.rowCount()): item = self.tasksTableWidget.item(i, 0) if not item.font().strikeOut(): tasks.append(item.text()) settings.setValue("tasks/tasks", tasks) def start_timer(self): try: if not self.timer.isActive(): self.create_timer() except: self.create_timer() def create_timer(self): self.timer = QTimer() self.timer.timeout.connect(self.update_time) self.timer.timeout.connect(self.maybe_change_mode) self.timer.setInterval(1000) self.timer.setSingleShot(False) self.timer.start() def pause_timer(self): try: self.timer.stop() self.timer.disconnect() except: pass def reset_timer(self): try: self.pause_timer() self.time = QTime(0, 0, 0, 0) self.display_time() except: pass def maybe_start_timer(self): if self.currentRepetitions != self.maxRepetitions: self.start_timer() started = True else: self.currentRepetitions = 0 started = False return started def update_work_end_time(self): self.workEndTime = QTime(self.workHoursSpinBox.value(), self.workMinutesSpinBox.value(), self.workSecondsSpinBox.value()) def update_rest_end_time(self): self.restEndTime = QTime(self.restHoursSpinBox.value(), self.restMinutesSpinBox.value(), self.restSecondsSpinBox.value()) def update_current_mode(self, mode: str): self.currentMode = Mode.work if mode == "work" else Mode.rest def update_time(self): self.time = self.time.addSecs(1) self.totalTime = self.totalTime.addSecs(1) if self.modeComboBox.currentText() == "work": self.workTime = self.workTime.addSecs(1) else: self.restTime = self.restTime.addSecs(1) self.display_time() def update_max_repetitions(self, value): if value == 0: self.currentRepetitions = 0 self.maxRepetitions = -1 else: self.maxRepetitions = 2 * value def maybe_change_mode(self): if self.currentMode is Mode.work and self.time >= self.workEndTime: self.reset_timer() self.modeComboBox.setCurrentIndex(1) self.increment_current_repetitions() started = self.maybe_start_timer() self.show_window_message( Status.workFinished if started else Status.repetitionsReached) elif self.currentMode is Mode.rest and self.time >= self.restEndTime: self.reset_timer() self.modeComboBox.setCurrentIndex(0) self.increment_current_repetitions() started = self.maybe_start_timer() self.show_window_message( Status.restFinished if started else Status.repetitionsReached) def increment_current_repetitions(self): if self.maxRepetitions > 0: self.currentRepetitions += 1 def insert_task(self): task = self.taskTextEdit.toPlainText() self.insert_tasks(task) def insert_tasks(self, *tasks): for task in tasks: if task: rowCount = self.tasksTableWidget.rowCount() self.tasksTableWidget.setRowCount(rowCount + 1) self.tasksTableWidget.setItem(rowCount, 0, QTableWidgetItem(task)) self.tasksTableWidget.resizeRowsToContents() self.taskTextEdit.clear() def delete_task(self): selectedIndexes = self.tasksTableWidget.selectedIndexes() if selectedIndexes: self.tasksTableWidget.removeRow(selectedIndexes[0].row()) def mark_task_as_finished(self, row, col): item = self.tasksTableWidget.item(row, col) font = self.tasksTableWidget.item(row, col).font() font.setStrikeOut(False if item.font().strikeOut() else True) item.setFont(font) def display_time(self): self.timeDisplay.display(self.time.toString(self.timeFormat)) self.statisticsRestTimeDisplay.display( self.restTime.toString(self.timeFormat)) self.statisticsWorkTimeDisplay.display( self.workTime.toString(self.timeFormat)) self.statisticsTotalTimeDisplay.display( self.totalTime.toString(self.timeFormat)) def show_window_message(self, status): if status is Status.workFinished: self.trayIcon.showMessage("Break", choice(work_finished_phrases), QIcon("icons/tomato.png")) elif status is Status.restFinished: self.trayIcon.showMessage("Work", choice(rest_finished_phrases), QIcon("icons/tomato.png")) else: self.trayIcon.showMessage("Finished", choice(pomodoro_finished_phrases), QIcon("icons/tomato.png")) self.resetButton.click() def setup_connections(self): self.playButton.clicked.connect(self.start_timer) self.playButton.clicked.connect( lambda: self.playButton.setDisabled(True)) self.playButton.clicked.connect( lambda: self.pauseButton.setDisabled(False)) self.playButton.clicked.connect( lambda: self.resetButton.setDisabled(False)) self.pauseButton.clicked.connect(self.pause_timer) self.pauseButton.clicked.connect( lambda: self.playButton.setDisabled(False)) self.pauseButton.clicked.connect( lambda: self.pauseButton.setDisabled(True)) self.pauseButton.clicked.connect( lambda: self.resetButton.setDisabled(False)) self.resetButton.clicked.connect(self.reset_timer) self.resetButton.clicked.connect( lambda: self.playButton.setDisabled(False)) self.resetButton.clicked.connect( lambda: self.pauseButton.setDisabled(True)) self.resetButton.clicked.connect( lambda: self.resetButton.setDisabled(True)) self.workHoursSpinBox.valueChanged.connect(self.update_work_end_time) self.workMinutesSpinBox.valueChanged.connect(self.update_work_end_time) self.workSecondsSpinBox.valueChanged.connect(self.update_work_end_time) self.restHoursSpinBox.valueChanged.connect(self.update_rest_end_time) self.restMinutesSpinBox.valueChanged.connect(self.update_rest_end_time) self.restSecondsSpinBox.valueChanged.connect(self.update_rest_end_time) self.modeComboBox.currentTextChanged.connect(self.update_current_mode) self.repetitionsSpinBox.valueChanged.connect( self.update_max_repetitions) self.acceptTaskButton.pressed.connect(self.insert_task) self.deleteTaskButton.pressed.connect(self.delete_task) self.tasksTableWidget.cellDoubleClicked.connect( self.mark_task_as_finished) def setup_ui(self): settings = QSettings() self.size_policy = sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) #TABWIDGET self.tabWidget = QTabWidget() self.pomodoroWidget = QWidget(self) self.pomodoroWidgetLayout = QVBoxLayout(self.pomodoroWidget) self.pomodoroWidget.setLayout(self.pomodoroWidgetLayout) # work self.workGroupBox = QGroupBox("Work") self.workGroupBoxLayout = QHBoxLayout(self.workGroupBox) self.workGroupBox.setLayout(self.workGroupBoxLayout) self.workHoursSpinBox = QSpinBox(minimum=0, maximum=24, value=settings.value( "timer/work/hours", 0), suffix="h", sizePolicy=self.size_policy) self.workMinutesSpinBox = QSpinBox(minimum=0, maximum=60, value=settings.value( "timer/work/minutes", 25), suffix="m", sizePolicy=self.size_policy) self.workSecondsSpinBox = QSpinBox(minimum=0, maximum=60, value=settings.value( "timer/work/seconds", 0), suffix="s", sizePolicy=self.size_policy) self.workGroupBoxLayout.addWidget(self.workHoursSpinBox) self.workGroupBoxLayout.addWidget(self.workMinutesSpinBox) self.workGroupBoxLayout.addWidget(self.workSecondsSpinBox) # rest self.restGroupBox = QGroupBox("Rest") self.restGroupBoxLayout = QHBoxLayout(self.restGroupBox) self.restGroupBox.setLayout(self.restGroupBoxLayout) self.restHoursSpinBox = QSpinBox(minimum=0, maximum=24, value=settings.value( "timer/rest/hours", 0), suffix="h", sizePolicy=self.size_policy) self.restMinutesSpinBox = QSpinBox(minimum=0, maximum=60, value=settings.value( "timer/rest/minutes", 5), suffix="m", sizePolicy=self.size_policy) self.restSecondsSpinBox = QSpinBox(minimum=0, maximum=60, value=settings.value( "timer/rest/seconds", 0), suffix="s", sizePolicy=self.size_policy) self.restGroupBoxLayout.addWidget(self.restHoursSpinBox) self.restGroupBoxLayout.addWidget(self.restMinutesSpinBox) self.restGroupBoxLayout.addWidget(self.restSecondsSpinBox) #OTHER self.otherGroupBox = QGroupBox("Other") self.otherGroupBoxLayout = QHBoxLayout(self.otherGroupBox) self.otherGroupBox.setLayout(self.otherGroupBoxLayout) self.repetitionsLabel = QLabel("Repetitions", sizePolicy=self.size_policy) self.repetitionsSpinBox = QSpinBox(minimum=0, maximum=10000, value=0, sizePolicy=self.size_policy, specialValueText="∞") self.modeLabel = QLabel("Mode", sizePolicy=self.size_policy) self.modeComboBox = QComboBox() self.modeComboBox.addItems(["work", "rest"]) self.otherGroupBoxLayout.addWidget(self.repetitionsLabel) self.otherGroupBoxLayout.addWidget(self.repetitionsSpinBox) self.otherGroupBoxLayout.addWidget(self.modeLabel) self.otherGroupBoxLayout.addWidget(self.modeComboBox) #LCDDISPLAY self.lcdDisplayGroupBox = QGroupBox("Time") self.lcdDisplayGroupBoxLayout = QHBoxLayout(self.lcdDisplayGroupBox) self.lcdDisplayGroupBox.setLayout(self.lcdDisplayGroupBoxLayout) self.timeDisplay = QLCDNumber(8, sizePolicy=self.size_policy) self.timeDisplay.setFixedHeight(100) self.timeDisplay.display("00:00:00") self.lcdDisplayGroupBoxLayout.addWidget(self.timeDisplay) #BUTTONS self.buttonWidget = QWidget() self.buttonWidgetLayout = QHBoxLayout(self.buttonWidget) self.buttonWidget.setLayout(self.buttonWidgetLayout) self.playButton = self.make_button("start", disabled=False) self.resetButton = self.make_button("reset") self.pauseButton = self.make_button("pause") self.buttonWidgetLayout.addWidget(self.pauseButton) self.buttonWidgetLayout.addWidget(self.playButton) self.buttonWidgetLayout.addWidget(self.resetButton) #CENTRALWIDGET self.pomodoroWidgetLayout.addWidget(self.workGroupBox) self.pomodoroWidgetLayout.addWidget(self.restGroupBox) self.pomodoroWidgetLayout.addWidget(self.otherGroupBox) self.pomodoroWidgetLayout.addWidget(self.lcdDisplayGroupBox) self.pomodoroWidgetLayout.addWidget(self.buttonWidget) #CREATE TASKS TAB self.tasksWidget = QWidget(self.tabWidget) self.tasksWidgetLayout = QVBoxLayout(self.tasksWidget) self.tasksWidget.setLayout(self.tasksWidgetLayout) self.inputWidget = QWidget() self.inputWidget.setFixedHeight(50) self.inputWidgetLayout = QHBoxLayout(self.inputWidget) self.inputWidgetLayout.setContentsMargins(0, 0, 0, 0) self.inputWidget.setLayout(self.inputWidgetLayout) self.taskTextEdit = QTextEdit( placeholderText="Describe your task briefly.", undoRedoEnabled=True) self.inputButtonContainer = QWidget() self.inputButtonContainerLayout = QVBoxLayout( self.inputButtonContainer) self.inputButtonContainerLayout.setContentsMargins(0, 0, 0, 0) self.inputButtonContainer.setLayout(self.inputButtonContainerLayout) self.acceptTaskButton = QToolButton(icon=QIcon("icons/check.png")) self.deleteTaskButton = QToolButton(icon=QIcon("icons/trash.png")) self.inputButtonContainerLayout.addWidget(self.acceptTaskButton) self.inputButtonContainerLayout.addWidget(self.deleteTaskButton) self.inputWidgetLayout.addWidget(self.taskTextEdit) self.inputWidgetLayout.addWidget(self.inputButtonContainer) self.tasksTableWidget = QTableWidget(0, 1) self.tasksTableWidget.setHorizontalHeaderLabels(["Tasks"]) self.tasksTableWidget.horizontalHeader().setStretchLastSection(True) self.tasksTableWidget.verticalHeader().setVisible(False) self.tasksTableWidget.setWordWrap(True) self.tasksTableWidget.setTextElideMode(Qt.ElideNone) self.tasksTableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tasksTableWidget.setSelectionMode( QAbstractItemView.SingleSelection) self.insert_tasks(*settings.value("tasks/tasks", [])) self.tasksWidgetLayout.addWidget(self.inputWidget) self.tasksWidgetLayout.addWidget(self.tasksTableWidget) #CREATE STATISTICS TAB self.statisticsWidget = QWidget() self.statisticsWidgetLayout = QVBoxLayout(self.statisticsWidget) self.statisticsWidget.setLayout(self.statisticsWidgetLayout) self.statisticsWorkTimeGroupBox = QGroupBox("Work Time") self.statisticsWorkTimeGroupBoxLayout = QHBoxLayout() self.statisticsWorkTimeGroupBox.setLayout( self.statisticsWorkTimeGroupBoxLayout) self.statisticsWorkTimeDisplay = QLCDNumber(8) self.statisticsWorkTimeDisplay.display("00:00:00") self.statisticsWorkTimeGroupBoxLayout.addWidget( self.statisticsWorkTimeDisplay) self.statisticsRestTimeGroupBox = QGroupBox("Rest Time") self.statisticsRestTimeGroupBoxLayout = QHBoxLayout() self.statisticsRestTimeGroupBox.setLayout( self.statisticsRestTimeGroupBoxLayout) self.statisticsRestTimeDisplay = QLCDNumber(8) self.statisticsRestTimeDisplay.display("00:00:00") self.statisticsRestTimeGroupBoxLayout.addWidget( self.statisticsRestTimeDisplay) self.statisticsTotalTimeGroupBox = QGroupBox("Total Time") self.statisticsTotalTimeGroupBoxLayout = QHBoxLayout() self.statisticsTotalTimeGroupBox.setLayout( self.statisticsTotalTimeGroupBoxLayout) self.statisticsTotalTimeDisplay = QLCDNumber(8) self.statisticsTotalTimeDisplay.display("00:00:00") self.statisticsTotalTimeGroupBoxLayout.addWidget( self.statisticsTotalTimeDisplay) self.statisticsWidgetLayout.addWidget(self.statisticsTotalTimeGroupBox) self.statisticsWidgetLayout.addWidget(self.statisticsWorkTimeGroupBox) self.statisticsWidgetLayout.addWidget(self.statisticsRestTimeGroupBox) #ADD TABS self.timerTab = self.tabWidget.addTab(self.pomodoroWidget, QIcon("icons/timer.png"), "Timer") self.tasksTab = self.tabWidget.addTab(self.tasksWidget, QIcon("icons/tasks.png"), "Tasks") self.statisticsTab = self.tabWidget.addTab( self.statisticsWidget, QIcon("icons/statistics.png"), "Statistics") self.setCentralWidget(self.tabWidget) def make_button(self, text, iconPath=None, disabled=True): button = QPushButton(text, sizePolicy=self.size_policy) if iconPath: button.setIcon(QIcon(iconPath)) button.setDisabled(disabled) return button def setup_trayicon(self): self.trayIcon = QSystemTrayIcon(QIcon("icons/tomato.png")) self.trayIcon.setContextMenu(QMenu()) self.quitAction = self.trayIcon.contextMenu().addAction( QIcon("icons/exit.png"), "Quit", self.exit) self.quitAction.triggered.connect(self.exit) self.trayIcon.activated.connect(self.onActivate) self.trayIcon.show() def exit(self): self.close() app = QApplication.instance() if app: app.quit() def onActivate(self, reason): if reason == QSystemTrayIcon.Trigger: self.show()
class AnimateMainButtons(object): def __init__(self, context): self.context = context self.index = list() self.alpha = 0 self.animation_number = -1; self.context.btn_new_card.setStyleSheet('color: rgb(0,0,0,%s)' % 0) self.context.btn_checkout.setStyleSheet('color: rgb(0,0,0,%s)' % 0) self.context.btn_charge.setStyleSheet('color: rgb(0,0,0,%s)' % 0) self.context.btn_vip.setStyleSheet('color: rgb(0,0,0,%s)' % 0) self.context.btn_reports.setStyleSheet('color: rgb(0,0,0,%s)' % 0) self.init_button_animation() def initButtonGeometryAnimation(self, element, start, end, duration=500, loopcount=1): element.setStartValue(start) element.setEndValue(end) element.setDuration(duration) element.setLoopCount(loopcount) element.setEasingCurve(QEasingCurve.OutCurve) def init_button_animation(self): self.b_position = [] self.b_position.append( self.animation_button_geometry(self.context.btn_new_card, self.context.btn_new_card.geometry())) self.b_position.append( self.animation_button_geometry(self.context.btn_checkout, self.context.btn_checkout.geometry())) self.b_position.append( self.animation_button_geometry(self.context.btn_charge, self.context.btn_charge.geometry())) self.b_position.append(self.animation_button_geometry(self.context.btn_vip, self.context.btn_vip.geometry())) self.b_position.append( self.animation_button_geometry(self.context.btn_reports, self.context.btn_reports.geometry())) self.timers = [] for i in range(len(self.b_position)): self.timers.append(QTimer()) self.timers[i].timeout.connect(self.start_moving) self.timers[i].setSingleShot(True) self.timers[i].start(i * 100) self.timer = QTimer() self.timer.timeout.connect(self.animation_button_alpha) self.timer.start(4) def start_moving(self): self.animation_number += 1 self.b_position[self.animation_number].start() self.index.append(self.animation_number) def animation_button_geometry(self, element, pos): start_x = 0 start_y = pos.y() start_width = pos.width() start_height = pos.height() start = QRect(start_x, start_y, start_width, start_height) end = pos self.anim = QPropertyAnimation(element, 'geometry'.encode()) self.initButtonGeometryAnimation(self.anim, start=start, end=end) return self.anim def animation_button_alpha(self): if 0 in self.index: alpha = self.context.btn_new_card.palette().color(QPalette.Text).alpha() alpha += 1 if alpha < 256: self.context.btn_new_card.setStyleSheet('color: rgb(0,0,0,%s)' % alpha) if 1 in self.index: alpha = self.context.btn_checkout.palette().color(QPalette.Text).alpha() alpha += 1 if alpha < 256: self.context.btn_checkout.setStyleSheet('color: rgb(0,0,0,%s)' % alpha) if 2 in self.index: alpha = self.context.btn_charge.palette().color(QPalette.Text).alpha() alpha += 1 if alpha < 256: self.context.btn_charge.setStyleSheet('color: rgb(0,0,0,%s)' % alpha) if 3 in self.index: alpha = self.context.btn_vip.palette().color(QPalette.Text).alpha() alpha += 1 if alpha < 256: self.context.btn_vip.setStyleSheet('color: rgb(0,0,0,%s)' % alpha) if 4 in self.index: alpha = self.context.btn_reports.palette().color(QPalette.Text).alpha() alpha += 1 if alpha < 256: self.context.btn_reports.setStyleSheet('color: rgb(0,0,0,%s)' % alpha) if alpha == 255: self.timer.disconnect()
class YTD(QWidget): def __init__(self, path="home/wasptheslimy/Music/YoutubeDownloads/"): super().__init__() self.setStyleSheet("background-color: #697268") self.path = path self.resize(480, 140) self.initUI() self.setWindowTitle("Youtbe Downloader") def initUI(self): self.verticalLinkLayout = QVBoxLayout() self.headerHorizantalLayout = QHBoxLayout() self.headerHorizantalLayout.addStretch() cssTitle = """ font-family: 'Aguero Serif'; font-size:48px; background-color: #4e5340; color :#fffcf4; padding-top : 8px; padding-bottom : 8px; padding-left:12px; padding-right:12px; border-style: solid; border-width: 4px; border-color : #22181c; """ cssIcon = """ background-color:#fffcf4; padding = 1px; border-style: solid; border-width: 4px; border-color : #4e5340; border-radius:25px; """ self.YoutubeIcon = QLabel() self.YoutubeIcon.setPixmap( QPixmap( "/home/wasptheslimy/Desktop/youtube_Downloader/Icons/youtubeAdjusted.png" )) self.YoutubeTitle = QLabel() self.YoutubeIcon.setStyleSheet(cssIcon) self.YoutubeTitle.setStyleSheet(cssTitle) self.YoutubeTitle.setText("Youtube Downloader") self.headerHorizantalLayout.addWidget(self.YoutubeIcon) self.headerHorizantalLayout.addWidget(self.YoutubeTitle) #self.headerHorizantalLayout. self.headerHorizantalLayout.addStretch() self.verticalLinkLayout.addLayout(self.headerHorizantalLayout) cssLinkLabel = """ background-color:#240b36; color: #f2dc5d; border-style: solid; border-width: 1px; border-color :#697268; border-radius: 5px; font-family:'Immani Demo'; padding:5px; """ cssVideoLink = """ background-color:#f6e8ea; color:#22181c; border-style: solid; border-width: 2px; border-color :#697268; """ self.LinkLayout = QHBoxLayout() self.LinkLabel = QLabel() self.LinkLabel.setText("Youtube Link : ") self.videoLink = QLineEdit("Enter the youtube link") self.LinkLayout.addWidget(self.LinkLabel) self.LinkLayout.addWidget(self.videoLink) self.verticalLinkLayout.addLayout(self.LinkLayout) self.verticalLinkLayout.addStretch() self.LinkLabel.setStyleSheet(cssLinkLabel) self.videoLink.setStyleSheet(cssVideoLink) self.path = self.getPath() cssPathLabel = """ background-color:#f6e8ea; color:#22181c; border-style: solid; border-width: 2px; border-radius:1px; border-color :#5a0001; font-family:'Times New Roman', Times, serif; font-size:14px; """ self.PathLabel = QLabel() self.PathLabel.setStyleSheet(cssPathLabel) self.Pathscheme = "Current Download PATH : {}" self.PathLabel.setText(self.Pathscheme.format(self.path)) self.horizontalOptionLayout = QHBoxLayout() self.newPath = QPushButton("Enter New Path") self.newPath.setStyleSheet( "background-color:#aac0aa;color:#405858;font-family:'Times New Roman', Times, serif;" ) self.newPath.clicked.connect(self.newPathFunction) self.listmod = QPushButton("List Download Mod") self.listmod.setStyleSheet( "background-color:#aac0aa;color:#405858;font-family:'Times New Roman', Times, serif;" ) self.listmod.clicked.connect(self.listmodFunction) self.horizontalOptionLayout.addWidget(self.newPath) self.horizontalOptionLayout.addWidget(self.listmod) self.horizontalOptionLayout.addStretch() #QFileDialog./home/wasptheslimy/Desktop/ self.horizontalControlLayout = QHBoxLayout() self.Download = QPushButton("Download") self.Download.setStyleSheet( "background-color:#aac0aa;color:#202840;font-family:'Times New Roman', Times, serif;" ) self.Download.clicked.connect(self.DownFunc) self.Cancel = QPushButton("Cancel") self.Cancel.setStyleSheet( "background-color:#aac0aa;color:#202840;font-family:'Times New Roman', Times, serif;" ) self.Cancel.clicked.connect(self.cancelFunc) self.horizontalControlLayout.addWidget(self.Download) self.horizontalControlLayout.addWidget(self.Cancel) self.verticalLinkLayout.addWidget(self.PathLabel) self.verticalLinkLayout.addLayout(self.horizontalOptionLayout) self.verticalLinkLayout.addStretch() self.verticalLinkLayout.addLayout(self.horizontalControlLayout) self.setLayout(self.verticalLinkLayout) def newPathFunction(self): self.timer = QTimer() self.timer.timeout.connect(self.pathUpdate) self.timer.start(1000) self.popWindow = PathPage() #self.popWindow.setGeometry(548, 346, 303, 140) self.popWindow.show() def pathUpdate(self): if self.path != self.getPath(): self.path = self.getPath() self.PathLabel.setText(self.Pathscheme.format(self.path)) self.timer.disconnect() else: pass def getPath(self): with open("path.txt", "r") as pathFile: self.path = pathFile.readlines()[-1][:-1] return self.path def cancelFunc(self): self.close() def DownFunc(self): url = self.videoLink.text() if 'www.youtube.com' in url.split("/"): mp3Download([url], self.path) else: print("[ERROR] Invalid URL") exit() def listmodFunction(self): self.popWindow = ListDownload(self.path) self.popWindow.show()
class client(QMainWindow): def __init__(self): super().__init__() # self.localPath = os.environ['HOME'] self.localPath = '/' self.serverPath = '/' self.linkData = [] self.serverFileInfo = [] self.waitingTaskQueue = [] self.finishedTaskQueue = [] self.createServerDirQueue = [] self.downloadingTask = [] self.connectionNow = { 'hostname': '', 'username': '', 'passwd': '', 'port': 21 } self.FTP = None self.clearFlag = 0 self.t1 = None self.timer = QTimer(self) self.lock = threading.Lock() self.Mutex = threading.Semaphore(1) self.initUI() def initUI(self): self.setGeometry(100, 100, 1100, 600) self.setMinimumWidth(650) self.setWindowTitle('ftpClient') self.initMenuBar() self.initCenterWidget() self.show() def initMenuBar(self): linkManage = QAction('管理连接(&L)', self) linkManage.setShortcut('Ctrl+M') linkManage.setStatusTip('管理所有连接') linkManage.triggered.connect(self.startLinkManageDialog) exitAct = QAction('退出(&E)', self) exitAct.setShortcut('Ctrl+Q') exitAct.setStatusTip('退出程序') exitAct.triggered.connect(qApp.quit) menuBar = self.menuBar() fileMenu = menuBar.addMenu('文件(&F)') fileMenu.addAction(linkManage) fileMenu.addAction(exitAct) def initCenterWidget(self): centerWidget = QWidget() self.centerBox = QVBoxLayout() self.centerBox.setAlignment(Qt.AlignTop) self.initQuickLink() localWidget = QWidget() serverWidget = QWidget() localWidgetLayout = QVBoxLayout() serverWidgetLayout = QVBoxLayout() localLabelLayout = QHBoxLayout() serverLabelLayout = QHBoxLayout() self.localPathLineEdit = QLineEdit() self.serverPathLineEdit = QLineEdit() self.localPathLineEdit.setFocusPolicy(Qt.NoFocus) self.serverPathLineEdit.setFocusPolicy(Qt.NoFocus) self.initLocalFileBox() self.initServerFileBox() localLabelLayout.addWidget(QLabel('本地文件:')) localLabelLayout.addWidget(self.localPathLineEdit) serverLabelLayout.addWidget(QLabel('服务器文件:')) serverLabelLayout.addWidget(self.serverPathLineEdit) localWidgetLayout.addLayout(localLabelLayout) localWidgetLayout.addWidget(self.localFileTreeView) localWidgetLayout.setContentsMargins(0, 0, 0, 0) serverWidgetLayout.addLayout(serverLabelLayout) serverWidgetLayout.addWidget(self.serverFileTree) serverWidgetLayout.setContentsMargins(0, 0, 0, 0) localWidget.setLayout(localWidgetLayout) localWidget.setContentsMargins(0, 0, 0, 0) serverWidget.setLayout(serverWidgetLayout) serverWidget.setContentsMargins(0, 0, 0, 0) self.taskQueueTreeWidget = QTreeWidget() self.taskQueueTreeWidget.setColumnCount(6) self.taskQueueTreeWidget.setHeaderLabels( ['文件名', '本地文件夹', '传输方向', ' 远程文件夹', '文件大小', '当前状态']) self.taskQueueTreeWidget.setItemDelegate(MyDelegate()) taskQueueWidget = QWidget() taskQueueLayout = QVBoxLayout() taskQueueLayout.addWidget(QLabel('任务队列:')) taskQueueLayout.addWidget(self.taskQueueTreeWidget) taskQueueLayout.setContentsMargins(0, 0, 0, 0) taskQueueWidget.setLayout(taskQueueLayout) taskQueueWidget.setContentsMargins(0, 0, 0, 0) splitter1 = QSplitter(Qt.Horizontal) splitter2 = QSplitter(Qt.Vertical) splitter3 = QSplitter(Qt.Vertical) splitter4 = QSplitter(Qt.Vertical) splitter2.addWidget(localWidget) splitter2.addWidget(self.localFileTreeWidget) splitter3.addWidget(serverWidget) splitter3.addWidget(self.serverFileTable) splitter1.addWidget(splitter2) splitter1.addWidget(splitter3) splitter4.addWidget(splitter1) splitter4.addWidget(taskQueueWidget) self.centerBox.addWidget(splitter4) centerWidget.setLayout(self.centerBox) self.setCentralWidget(centerWidget) self.refreshTableButton.clicked.connect(self.tableRefresh) def initQuickLink(self): self.hostInput = QLineEdit(self) self.userNameInput = QLineEdit(self) self.passwdInput = QLineEdit(self) self.portInput = QLineEdit('21', self) self.quickLoginButton = QPushButton('快速连接', self) self.anonymousLoginCheckBox = QCheckBox('匿名连接') self.refreshTableButton = QPushButton('刷新文件列表') self.passwdInput.setEchoMode(QLineEdit.Password) self.portInput.setValidator(QIntValidator(0, 65535)) self.portInput.setMaximumWidth(40) quickLinkBox = QHBoxLayout() quickLinkBox.addWidget(QLabel(' 主机:', self)) quickLinkBox.addWidget(self.hostInput) quickLinkBox.addWidget(QLabel(' 用户名:', self)) quickLinkBox.addWidget(self.userNameInput) quickLinkBox.addWidget(QLabel(' 密码:', self)) quickLinkBox.addWidget(self.passwdInput) quickLinkBox.addWidget(QLabel(' 端口:', self)) quickLinkBox.addWidget(self.portInput) quickLinkBox.addWidget(self.quickLoginButton) quickLinkBox.addWidget(self.anonymousLoginCheckBox) quickLinkBox.addWidget(self.refreshTableButton) quickLinkBox.addStretch(1) self.centerBox.addLayout(quickLinkBox) self.quickLoginButton.clicked.connect(self.connectFromQuickLink) self.anonymousLoginCheckBox.stateChanged.connect( self.quickLinkCheckBoxChanged) def initLocalFileBox(self): self.localFileTreeView = QTreeView() self.localFileTreeWidget = QTreeWidget() self.localDirModel = ChangedQDirModel() self.localFileTreeView.setModel(self.localDirModel) self.localFileTreeView.setColumnWidth(0, 240) self.localFileTreeView.setColumnWidth(2, 60) self.localFileTreeView.clicked.connect(self.localTreeClicked) self.localFileTreeWidget.setColumnCount(4) self.localFileTreeWidget.setHeaderLabels( ['文件名', '文件大小', '文件类型', '修改时间']) self.localFileTreeWidget.setColumnWidth(0, 240) self.localFileTreeWidget.setColumnWidth(2, 60) self.localFileTreeWidget.setItemDelegate(MyDelegate()) self.localFileTable = [] self.localFileRefesh() self.localFileTreeWidget.doubleClicked.connect( self.localTableDoubleClicked) self.localFileTreeWidget.itemPressed.connect( self.localTableRightClicked) def initServerFileBox(self): self.serverFileTree = QTreeWidget() self.serverFileTable = QTreeWidget() self.serverFileTree.setHeaderLabels(['目录结构']) self.serverFileTree.setItemDelegate(MyDelegate()) self.serverFileTable.setColumnCount(5) self.serverFileTable.setHeaderLabels( ['文件名', '文件类型', '文件大小', '权限', '修改时间']) self.serverFileTable.setColumnWidth(0, 240) self.serverFileTable.setColumnWidth(1, 60) self.serverFileTable.setColumnWidth(2, 60) self.serverFileTable.setColumnWidth(3, 70) self.serverFileTable.setItemDelegate(MyDelegate()) self.serverFileTree.itemExpanded.connect(self.serverFileTreeRefresh) self.serverFileTree.itemClicked.connect(self.serverFileTreeClicked) self.serverFileTable.doubleClicked.connect( self.serverTableDoubleClicked) self.serverFileTable.itemPressed.connect(self.serverTableRightClicked) def connectFromQuickLink(self): hostName = self.hostInput.text() userName = self.userNameInput.text() passwd = self.passwdInput.text() port = int(self.portInput.text()) with open('linkdata.json', 'r') as f: self.linkData = json.load(f) data = { 'hostname': hostName, 'username': userName, 'passwd': passwd, 'port': port, 'remark': '来自快速连接' } self.linkData[userName + '@' + hostName + ':' + str(port) + ' ' + '来自快速连接'] = data with open('linkdata.json', 'w') as f: json.dump(self.linkData, f) try: self.aNewConnection(hostName, userName, passwd, port) except socket.gaierror: QMessageBox.information(self, '主机名错误', '主机名错误,请输入正确的主机名', QMessageBox.Ok, QMessageBox.Ok) return except ConnectionRefusedError: QMessageBox.information(self, '连接出错', '连接失败,请检查是否输入了正确的主机名或端口', QMessageBox.Ok, QMessageBox.Ok) return except ftplib.error_perm: QMessageBox.information(self, '登陆出错', '登陆失败,请检查是否输入了正确的用户名或密码', QMessageBox.Ok, QMessageBox.Ok) return def localFileRefesh(self): self.localFileTable.clear() self.localFileTreeWidget.clear() if self.localPath != '/': node = QTreeWidgetItem(self.localFileTreeWidget) node.setText(0, '..') self.localFileTable.append(node) for i in os.listdir(self.localPath): node = QTreeWidgetItem(self.localFileTreeWidget) node.setText(0, i) tempPath = os.path.join(self.localPath, i) if os.path.isfile(tempPath): node.setText(1, str(os.path.getsize(tempPath))) node.setText(2, 'File') elif os.path.isdir(tempPath): node.setText(1, '') node.setText(2, 'Folder') elif os.path.islink(tempPath): node.setText(1, '') node.setText(2, 'Shortcut') elif os.path.ismount(tempPath): node.setText(1, '') node.setText(2, 'Mount') try: node.setText(3, TimeStampToTime(os.path.getmtime(tempPath))) except FileNotFoundError: pass except PermissionError: pass self.localFileTable.append(node) self.localPathLineEdit.setText(self.localPath) for i in self.localFileTable: self.localFileTreeWidget.addTopLevelItem(i) def serverFileTableRefresh(self): self.Mutex.acquire() try: fileinfo = self.FTP.getdirinfo(self.serverPath) except ftplib.error_temp: self.reconnect() fileinfo = self.FTP.getdirinfo(self.serverPath) self.Mutex.release() for i in fileinfo: node = QTreeWidgetItem(self.serverFileTable) node.setText(0, i[0]) node.setText(1, i[1]) node.setText(2, i[2]) node.setText(3, i[3]) node.setText(4, i[4]) self.serverFileInfo.append(node) self.serverPathLineEdit.setText(self.serverPath) for i in self.serverFileInfo: self.serverFileTable.addTopLevelItem(i) def serverFileTreeRefresh(self, item): if self.clearFlag == 0: self.clearFlag = 1 return path = item.text(0) fatherNode = item while fatherNode != self.serverFileTreeRoot: fatherNode = fatherNode.parent() path = fatherNode.text(0) + '/' + path path = path[1:] + '/' childrenItemList = item.takeChildren() for i in childrenItemList: item.removeChild(i) self.Mutex.acquire() fileinfo = self.FTP.getdirinfo(path) for i in fileinfo: if i[1] == 'Folder': node = QTreeWidgetItem(item) node.setText(0, i[0]) tempinfo = self.FTP.getdirinfo(path + i[0]) for j in tempinfo: if j[1] == 'Folder': tempnode = QTreeWidgetItem(node) tempnode.setText(0, j[0]) node.addChild(tempnode) item.addChild(node) self.Mutex.release() def serverFileTreeClicked(self, item, int_p): path = item.text(0) fatherNode = item while fatherNode != self.serverFileTreeRoot: fatherNode = fatherNode.parent() path = fatherNode.text(0) + '/' + path if path != '/': self.serverPath = path[1:] else: self.serverPath = path self.serverFileTable.clear() self.serverFileInfo.clear() if self.serverPath != '/': node = QTreeWidgetItem(self.serverFileTable) node.setText(0, '..') self.serverFileInfo.append(node) self.serverFileTableRefresh() def serverTableDoubleClicked(self, index): if qApp.mouseButtons() == Qt.RightButton: return if self.serverFileInfo[index.row()].text(1) == 'File': return if index.row() == 0: if self.serverPath == '/': self.localPath = self.serverPath + self.serverFileInfo[0].text( 0) else: tempPath = self.serverPath.split('/')[:-1] self.serverPath = '' for i in tempPath: self.serverPath = self.serverPath + '/' + i if self.serverPath != '/': self.serverPath = self.serverPath[1:] else: self.serverPath = os.path.join( self.serverPath, self.serverFileInfo[index.row()].text(0)) self.serverFileTable.clear() self.serverFileInfo.clear() if self.serverPath != '/': node = QTreeWidgetItem(self.serverFileTable) node.setText(0, '..') self.serverFileInfo.append(node) self.serverFileTableRefresh() def serverTableRightClicked(self, item, int_p): if item.text(0) == '..': return if qApp.mouseButtons() == Qt.RightButton: serverMenu = QMenu() downLoadFile = QAction('download') downLoadFolder = QAction('downloadfolder') downLoadFile.triggered.connect(self.downloadFile) downLoadFolder.triggered.connect(self.downloadFolder) if item.text(1) == 'Folder': serverMenu.addAction(downLoadFolder) else: serverMenu.addAction(downLoadFile) serverMenu.exec_(QCursor.pos()) def downloadFile(self): filename = self.serverFileTable.selectedItems()[0].text(0) self.lock.acquire() try: self.waitingTaskQueue.append({ 'filename': filename, 'localpath': self.localPath, 'direction': '<--', 'serverpath': self.serverPath, 'filesize': self.serverFileTable.selectedItems()[0].text(2) }) finally: self.lock.release() self.taskQueueRefresh() if len(self.downloadingTask) == 0: if self.t1 == None: self.t1 = threading.Thread(target=self.taskQueueOpertion) self.t1.start() else: if self.t1.is_alive(): pass else: self.t1 = threading.Thread(target=self.taskQueueOpertion) self.t1.start() def downloadFolder(self): folderpath = os.path.join( self.serverPath, self.serverFileTable.selectedItems()[0].text(0)) if self.localPath == '/': os.mkdir('/' + self.serverFileTable.selectedItems()[0].text(0)) else: os.mkdir(self.localPath + '/' + self.serverFileTable.selectedItems()[0].text(0)) self.Mutex.acquire() self.lock.acquire() try: self.traversalServerDir(folderpath, self.localPath, self.serverPath) except ftplib.error_temp: self.reconnect() self.traversalServerDir(folderpath, self.localPath, self.serverPath) finally: self.lock.release() self.Mutex.release() self.taskQueueRefresh() if len(self.downloadingTask) == 0: if self.t1 == None: self.t1 = threading.Thread(target=self.taskQueueOpertion) self.t1.start() else: if self.t1.is_alive(): pass else: self.t1 = threading.Thread(target=self.taskQueueOpertion) self.t1.start() def localTreeClicked(self, index): if self.localDirModel.fileInfo(index).isDir(): self.localPath = self.localDirModel.filePath(index) else: self.localPath = self.localDirModel.filePath(index) tempPath = self.localPath.split('/')[:-1] self.localPath = '' for i in tempPath: self.localPath = self.localPath + '/' + i if self.localPath != '/': self.localPath = self.localPath[1:] self.localFileRefesh() def localTableDoubleClicked(self, index): if qApp.mouseButtons() == Qt.RightButton: return if os.path.isdir( os.path.join( self.localPath, self.localFileTable[index.row()].text(0))) == False: return if index.row() == 0: if self.localPath == '/': self.localPath = self.localPath + self.localFileTable[0].text( 0) else: tempPath = self.localPath.split('/')[:-1] self.localPath = '' for i in tempPath: self.localPath = self.localPath + '/' + i if self.localPath != '/': self.localPath = self.localPath[1:] else: self.localPath = os.path.join( self.localPath, self.localFileTable[index.row()].text(0)) self.localFileRefesh() def localTableRightClicked(self, item, int_p): if item.text(0) == '..': return if qApp.mouseButtons() == Qt.RightButton: localMenu = QMenu() upLoadFile = QAction('upload') upLoadFolder = QAction('uploadfolder') upLoadFile.triggered.connect(self.uploadFile) upLoadFolder.triggered.connect(self.uploadFolder) if item.text(2) == 'Folder': localMenu.addAction(upLoadFolder) else: localMenu.addAction(upLoadFile) localMenu.exec_(QCursor.pos()) def uploadFile(self): filename = self.localFileTreeWidget.selectedItems()[0].text(0) self.lock.acquire() try: self.waitingTaskQueue.append({ 'filename': filename, 'localpath': self.localPath, 'direction': '-->', 'serverpath': self.serverPath, 'filesize': self.localFileTreeWidget.selectedItems()[0].text(1) }) finally: self.lock.release() self.taskQueueRefresh() if len(self.downloadingTask) == 0: if self.t1 == None: self.t1 = threading.Thread(target=self.taskQueueOpertion) self.t1.start() else: if self.t1.is_alive(): pass else: self.t1 = threading.Thread(target=self.taskQueueOpertion) self.t1.start() def uploadFolder(self): folderpath = os.path.join( self.localPath, self.localFileTreeWidget.selectedItems()[0].text(0)) self.lock.acquire() try: self.traversalLocalDir(folderpath, self.localPath, self.serverPath) except ftplib.error_temp: self.reconnect() self.traversalLocalDir(folderpath, self.localPath, self.serverPath) finally: self.lock.release() self.taskQueueRefresh() if len(self.downloadingTask) == 0: if self.t1 == None: self.t1 = threading.Thread(target=self.taskQueueOpertion) self.t1.start() else: if self.t1.is_alive(): pass else: self.t1 = threading.Thread(target=self.taskQueueOpertion) self.t1.start() def taskQueueOpertion(self): while len(self.createServerDirQueue) != 0: self.FTP.mkd(self.createServerDirQueue[0]) self.createServerDirQueue.pop(0) while len(self.waitingTaskQueue) != 0: self.lock.acquire() try: self.downloadingTask.append(self.waitingTaskQueue[0]) print('taskadded' + str(self.waitingTaskQueue[0])) self.waitingTaskQueue.pop(0) print('self.waitingTaskQueue.pop(0)') finally: self.lock.release() print('before self.taskQueueRefresh()') self.Mutex.acquire() if self.downloadingTask[0]['direction'] == '<--': if ' ' in self.downloadingTask[0]['filename']: localName = self.downloadingTask[0]['filename'].replace( ' ', '_') else: localName = self.downloadingTask[0]['filename'] with open( self.downloadingTask[0]['localpath'] + '/' + localName, 'wb') as fp: if self.downloadingTask[0]['serverpath'] == '/': tempserverpath = '/' + self.downloadingTask[0][ 'filename'] else: tempserverpath = self.downloadingTask[0][ 'serverpath'] + '/' + self.downloadingTask[0][ 'filename'] try: self.FTP.retrbinary('RETR ' + tempserverpath, fp.write, 10240) except ftplib.error_temp: self.reconnect() self.FTP.retrbinary('RETR ' + tempserverpath, fp.write, 10240) self.FTP.set_debuglevel(0) elif self.downloadingTask[0]['direction'] == '-->': if self.downloadingTask[0]['serverpath'] == '/': tempserverpath = '/' + self.downloadingTask[0]['filename'] else: tempserverpath = self.downloadingTask[0][ 'serverpath'] + '/' + self.downloadingTask[0][ 'filename'] with open( self.downloadingTask[0]['localpath'] + '/' + self.downloadingTask[0]['filename'], 'rb') as fp: try: self.FTP.storbinary('STOR ' + tempserverpath, fp, 10240) except ftplib.error_temp: self.reconnect() self.FTP.storbinary('STOR ' + tempserverpath, fp, 10240) self.FTP.set_debuglevel(0) self.Mutex.release() self.lock.acquire() try: self.finishedTaskQueue.insert(0, self.downloadingTask[0]) print('finish' + str(self.downloadingTask[0])) self.downloadingTask.clear() print('downloadingTask.clear()') finally: self.lock.release() print('after self.taskQueueRefresh()') def startLinkManageDialog(self): with open('linkdata.json', 'r') as f: self.linkData = json.load(f) self.linkManageDialog = QDialog() self.linkManageDialog.setModal(True) linkManageLayout = QVBoxLayout() self.linkManageDialog.setLayout(linkManageLayout) self.linkManageDialog.setWindowTitle('连接管理') linkDisplayLayout = QHBoxLayout() bottomButtomGroupLayout = QHBoxLayout() connectButtom = QPushButton('连接') confirmButtom = QPushButton('确定') cancleButtom = QPushButton('取消') bottomButtomGroupLayout.addStretch(1) bottomButtomGroupLayout.addWidget(connectButtom) bottomButtomGroupLayout.addWidget(confirmButtom) bottomButtomGroupLayout.addWidget(cancleButtom) linkManageLayout.addLayout(linkDisplayLayout) linkManageLayout.addLayout(bottomButtomGroupLayout) linkListLayout = QVBoxLayout() linkEditLayout = QVBoxLayout() linkDisplayLayout.addLayout(linkListLayout) linkDisplayLayout.addLayout(linkEditLayout) self.linkList = QListWidget() addLinkButton = QPushButton('新建') removeLinkButton = QPushButton('删除') linkManageButtonGroupLayout = QHBoxLayout() linkManageButtonGroupLayout.addWidget(addLinkButton) linkManageButtonGroupLayout.addWidget(removeLinkButton) linkListLayout.addWidget(QLabel('连接列表:'), 0, Qt.AlignTop) linkListLayout.addWidget(self.linkList) linkListLayout.addLayout(linkManageButtonGroupLayout) hBox1 = QHBoxLayout() hBox2 = QHBoxLayout() hBox3 = QHBoxLayout() hBox4 = QHBoxLayout() hBox5 = QHBoxLayout() hBox6 = QHBoxLayout() self.host = QLineEdit() self.userName = QLineEdit() self.passwd = QLineEdit() self.port = QLineEdit() self.remark = QLineEdit() self.passwd.setEchoMode(QLineEdit.Password) self.port.setValidator(QIntValidator(0, 65535)) self.anonymousLogin = QCheckBox('匿名登录') confirmEdit = QPushButton('确定修改') confirmEdit.setFixedWidth(80) self.anonymousLogin.stateChanged.connect( self.linkManageCheckBoxChanged) hBox1.addWidget(QLabel('主机: ')) hBox1.addWidget(self.host) hBox2.addWidget(QLabel('用户名:')) hBox2.addWidget(self.userName) hBox3.addWidget(QLabel('密码: ')) hBox3.addWidget(self.passwd) hBox4.addWidget(QLabel('端口: ')) hBox4.addWidget(self.port) hBox6.addWidget(QLabel('备注: ')) hBox6.addWidget(self.remark) hBox5.addWidget(self.anonymousLogin) hBox5.addWidget(confirmEdit, Qt.AlignRight) linkEditLayout.addLayout(hBox1) linkEditLayout.addLayout(hBox2) linkEditLayout.addLayout(hBox3) linkEditLayout.addLayout(hBox4) linkEditLayout.addLayout(hBox6) linkEditLayout.addLayout(hBox5) for key in self.linkData: item = QListWidgetItem(self.linkList) item.setText(key) self.linkList.setCurrentRow(0) if len(self.linkData) != 0: tempdata = self.linkData[self.linkList.currentItem().text()] self.host.setText(tempdata['hostname']) self.port.setText(str(tempdata['port'])) self.remark.setText(tempdata['remark']) if tempdata['username'] == 'anonymous': self.anonymousLogin.setCheckState(Qt.Checked) else: self.userName.setText(tempdata['username']) self.passwd.setText(tempdata['passwd']) cancleButtom.clicked.connect(self.linkManageDialog.close) self.linkList.itemClicked.connect(self.listItemClicked) addLinkButton.clicked.connect(self.addNewLink) confirmEdit.clicked.connect(self.confirmEditLink) confirmButtom.clicked.connect(self.saveData) removeLinkButton.clicked.connect(self.removeLink) connectButtom.clicked.connect(self.connectFromDialog) self.linkManageDialog.show() def connectFromDialog(self): hostName = self.host.text() userName = self.userName.text() passwd = self.passwd.text() port = int(self.port.text()) try: self.aNewConnection(hostName, userName, passwd, port) except socket.gaierror: QMessageBox.information(self, '主机名错误', '主机名错误,请输入正确的主机名', QMessageBox.Ok, QMessageBox.Ok) return except ConnectionRefusedError: QMessageBox.information(self, '连接出错', '连接失败,请检查是否输入了正确的主机名或端口', QMessageBox.Ok, QMessageBox.Ok) return except ftplib.error_perm: QMessageBox.information(self, '登陆出错', '登陆失败,请检查是否输入了正确的用户名或密码', QMessageBox.Ok, QMessageBox.Ok) return self.saveData() def removeLink(self): if len(self.linkData) == 0: return rowNow = self.linkList.currentRow() itemNow = self.linkList.currentItem() self.linkData.pop(itemNow.text()) self.linkList.removeItemWidget(itemNow) self.linkList.clear() for key in self.linkData: item = QListWidgetItem(self.linkList) item.setText(key) if len(self.linkData) == 0: self.host.setText('') self.port.setText('') self.anonymousLogin.setCheckState(Qt.Unchecked) self.userName.setText('') self.passwd.setText('') self.remark.setText('') return elif len(self.linkData) < rowNow + 1: rowNow = len(self.linkData) - 1 self.linkList.setCurrentRow(len(self.linkData) - 1) else: self.linkList.setCurrentRow(rowNow) self.listItemClicked(self.linkList.currentItem()) def saveData(self): self.confirmEditLink() with open('linkdata.json', 'w') as f: json.dump(self.linkData, f) self.linkManageDialog.close() def confirmEditLink(self): hostName = self.host.text() userName = self.userName.text() passwd = self.passwd.text() port = int(self.port.text()) remark = self.remark.text() data = { 'hostname': hostName, 'username': userName, 'passwd': passwd, 'port': port, "remark": remark } self.linkData.pop(self.linkList.currentItem().text()) self.linkData[userName + '@' + hostName + ':' + str(port) + ' ' + remark] = data self.linkList.clear() for key in self.linkData: item = QListWidgetItem(self.linkList) item.setText(key) self.linkList.setCurrentRow(len(self.linkList) - 1) def addNewLink(self): if '新连接' in self.linkData: return self.linkData['新连接'] = { 'hostname': '', 'username': '', 'passwd': '', 'port': '', 'remark': '' } item = QListWidgetItem(self.linkList) item.setText('新连接') self.linkList.setCurrentRow(len(self.linkList) - 1) self.host.setText('') self.port.setText('21') self.anonymousLogin.setCheckState(Qt.Unchecked) self.userName.setText('') self.passwd.setText('') self.remark.setText('') def listItemClicked(self, item): tempdata = self.linkData[item.text()] self.host.setText(tempdata['hostname']) self.port.setText(str(tempdata['port'])) self.remark.setText(tempdata['remark']) if tempdata['username'] == 'anonymous': self.anonymousLogin.setCheckState(Qt.Checked) else: self.anonymousLogin.setCheckState(Qt.Unchecked) self.userName.setText(tempdata['username']) self.passwd.setText(tempdata['passwd']) def quickLinkCheckBoxChanged(self): if self.anonymousLoginCheckBox.checkState() == Qt.Checked: self.userNameInput.setText('anonymous') self.passwdInput.setText('') self.userNameInput.setEnabled(False) self.passwdInput.setEnabled(False) elif self.anonymousLoginCheckBox.checkState() == Qt.Unchecked: self.userNameInput.setText('') self.passwdInput.setText('') self.userNameInput.setEnabled(True) self.passwdInput.setEnabled(True) def linkManageCheckBoxChanged(self): if self.anonymousLogin.checkState() == Qt.Checked: self.userName.setText('anonymous') self.passwd.setText('') self.userName.setEnabled(False) self.passwd.setEnabled(False) elif self.anonymousLogin.checkState() == Qt.Unchecked: self.userName.setText('') self.passwd.setText('') self.userName.setEnabled(True) self.passwd.setEnabled(True) def aNewConnection(self, host, username, passwd, port): if self.FTP != None: try: self.FTP.quit() except AttributeError: pass except EOFError: pass self.FTP = myFtp() self.FTP.set_pasv(True) self.connectionNow['hostname'] = host self.connectionNow['username'] = username self.connectionNow['passwd'] = passwd self.connectionNow['port'] = port self.FTP.connect(host, port) self.FTP.login(username, passwd) self.serverFileInfo = [] self.serverPath = '/' self.serverFileTable.clear() self.serverFileTree.clear() self.serverFileTreeRoot = QTreeWidgetItem(self.serverFileTree) self.serverFileTreeRoot.setText(0, '/') self.serverFileTree.addTopLevelItem(self.serverFileTreeRoot) self.Mutex.acquire() fileinfo = self.FTP.getdirinfo(self.serverPath) for i in fileinfo: if i[1] == 'Folder': node = QTreeWidgetItem(self.serverFileTreeRoot) node.setText(0, i[0]) tempinfo = self.FTP.getdirinfo(self.serverPath + i[0]) for j in tempinfo: if j[1] == 'Folder': tempnode = QTreeWidgetItem(node) tempnode.setText(0, j[0]) node.addChild(tempnode) self.serverFileTreeRoot.addChild(node) self.Mutex.release() self.serverFileTreeRoot.setExpanded(True) self.serverFileTableRefresh() if self.timer.isActive(): self.timer.disconnect() self.timer.timeout.connect(self.taskQueueRefresh) self.timer.start(500) def reconnect(self): self.FTP.connect(self.connectionNow['hostname'], self.connectionNow['port']) self.FTP.login(self.connectionNow['username'], self.connectionNow['passwd']) def taskQueueRefresh(self): self.lock.acquire() try: self.taskQueueTreeWidget.clear() if len(self.downloadingTask) != 0: node = QTreeWidgetItem(self.taskQueueTreeWidget) node.setText(0, self.downloadingTask[0]['filename']) node.setText(1, self.downloadingTask[0]['localpath']) node.setText(2, self.downloadingTask[0]['direction']) node.setText(3, self.downloadingTask[0]['serverpath']) node.setText(4, self.downloadingTask[0]['filesize']) node.setText(5, '正在传输') self.taskQueueTreeWidget.addTopLevelItem(node) for i in self.waitingTaskQueue: node = QTreeWidgetItem(self.taskQueueTreeWidget) node.setText(0, i['filename']) node.setText(1, i['localpath']) node.setText(2, i['direction']) node.setText(3, i['serverpath']) node.setText(4, i['filesize']) node.setText(5, '等待传输') self.taskQueueTreeWidget.addTopLevelItem(node) for i in self.finishedTaskQueue: node = QTreeWidgetItem(self.taskQueueTreeWidget) node.setText(0, i['filename']) node.setText(1, i['localpath']) node.setText(2, i['direction']) node.setText(3, i['serverpath']) node.setText(4, i['filesize']) node.setText(5, '传输完成') self.taskQueueTreeWidget.addTopLevelItem(node) finally: self.lock.release() def tableRefresh(self): if self.connectionNow['hostname'] != '': self.serverFileTable.clear() self.serverFileInfo.clear() if self.serverPath != '/': node = QTreeWidgetItem(self.serverFileTable) node.setText(0, '..') self.serverFileInfo.append(node) self.serverFileTableRefresh() self.localFileRefesh() def traversalLocalDir(self, dir, localpath, serverpath): fs = os.listdir(dir) for i in fs: temppath = os.path.join(dir, i) if os.path.isdir(temppath) == False: if serverpath == '/' and localpath == '/': print(i + ' ' + dir + ' ' + dir + ' ' + str(os.path.getsize(temppath))) self.waitingTaskQueue.append({ 'filename': i, 'localpath': dir, 'direction': '-->', 'serverpath': dir, 'filesize': str(os.path.getsize(temppath)) }) elif serverpath == '/': self.waitingTaskQueue.append({ 'filename': i, 'localpath': dir, 'direction': '-->', 'serverpath': dir[len(localpath):], 'filesize': str(os.path.getsize(temppath)) }) print(i + ' ' + dir + ' ' + dir[len(localpath):] + ' ' + str(os.path.getsize(temppath))) elif localpath == '/': self.waitingTaskQueue.append({ 'filename': i, 'localpath': dir, 'direction': '-->', 'serverpath': serverpath + dir, 'filesize': str(os.path.getsize(temppath)) }) print(i + ' ' + dir + ' ' + serverpath + dir + ' ' + str(os.path.getsize(temppath))) else: self.waitingTaskQueue.append({ 'filename': i, 'localpath': dir, 'direction': '-->', 'serverpath': serverpath + dir[len(localpath):], 'filesize': str(os.path.getsize(temppath)) }) print(i + ' ' + dir + ' ' + serverpath + dir[len(localpath):] + ' ' + str(os.path.getsize(temppath))) else: if serverpath == '/' and localpath == '/': print(i + ' ' + temppath + ' ' + temppath) self.createServerDirQueue.append(temppath) elif serverpath == '/': print(i + ' ' + temppath + ' ' + temppath[len(localpath):]) self.createServerDirQueue.append(temppath[len(localpath):]) elif localpath == '/': print(i + ' ' + temppath + ' ' + serverpath + temppath) self.createServerDirQueue.append(serverpath + temppath) else: print(i + ' ' + temppath + ' ' + serverpath + temppath[len(localpath):]) self.createServerDirQueue.append(serverpath + temppath[len(localpath):]) self.traversalLocalDir(temppath, localpath, serverpath) def traversalServerDir(self, dir, localpath, serverpath): fs = self.FTP.getdirinfo(dir) for i in fs: temppath = os.path.join(dir, i[0]) if i[1] == 'File': if serverpath == '/' and localpath == '/': print(i[0] + ' ' + dir + ' ' + dir + ' ' + i[2]) self.waitingTaskQueue.append({ 'filename': i[0], 'localpath': dir, 'direction': '<--', 'serverpath': dir, 'filesize': i[2] }) elif serverpath == '/': self.waitingTaskQueue.append({ 'filename': i[0], 'localpath': localpath + dir, 'direction': '<--', 'serverpath': dir, 'filesize': i[2] }) print(i[0] + ' ' + localpath + dir + ' ' + dir + ' ' + i[2]) elif localpath == '/': self.waitingTaskQueue.append({ 'filename': i[0], 'localpath': dir[len(serverpath):], 'direction': '<--', 'serverpath': dir, 'filesize': i[2] }) print(i[0] + ' ' + dir[len(serverpath):] + ' ' + dir + ' ' + i[2]) else: self.waitingTaskQueue.append({ 'filename': i[0], 'localpath': localpath + dir[len(serverpath):], 'direction': '<--', 'serverpath': dir, 'filesize': i[2] }) print(i[0] + ' ' + localpath + dir[len(serverpath):] + ' ' + dir + ' ' + i[2]) else: if serverpath == '/' and localpath == '/': print(i[0] + ' ' + temppath + ' ' + temppath) os.makedirs(temppath) elif serverpath == '/': print(i[0] + ' ' + localpath + temppath + ' ' + temppath) os.makedirs(localpath + temppath) elif localpath == '/': print(i[0] + ' ' + temppath[len(serverpath):] + ' ' + temppath) os.makedirs(temppath[len(serverpath):]) else: print(i[0] + ' ' + localpath + temppath[len(serverpath):] + ' ' + temppath) os.makedirs(localpath + temppath[len(serverpath):]) self.traversalServerDir(temppath, localpath, serverpath)