def hasChildren(self, idx): item = self.itemFromIndex(idx) if not item: return True node = item.data() if node in self._fetched: return QStandardItemModel.hasChildren(self, idx) return True
class OutboundSocksPanel(QWidget): def __init__(self): super().__init__() self.outboundSocksJSONFile = { "servers": [{ "address": "127.0.0.1", "port": 1080, "users": [{ "user": "******", "pass": "******", "level": 0 }] }] } self.translate = QCoreApplication.translate self.labeloutSocks = (self.translate("OutboundSocksPanel", "Address"), self.translate("OutboundSocksPanel", "Port"), self.translate("OutboundSocksPanel", "Level")) def createOutboundSocksSettingPanel(self): labelAddress = QLabel( self.translate("OutboundSocksPanel", "Server Address: "), self) self.lineEditOutboundSocksAddress = QLineEdit() labelPort = QLabel( self.translate("OutboundSocksPanel", "Port: "), self) self.spinBoxOutboundSocksPort = QSpinBox() labelUserName = QLabel( self.translate("OutboundSocksPanel", "User: "******"OutboundSocksPanel", "Password: "******"OutboundSocksPanel", "User Level: ")) self.spinBoxOutboundSocksuerLevel = QSpinBox() self.spinBoxOutboundSocksuerLevel.setRange(0, 65535) self.spinBoxOutboundSocksuerLevel.setValue(0) self.spinBoxOutboundSocksuerLevel.setDisabled(True) hboxuserLevel = QHBoxLayout() hboxuserLevel.addWidget(labelLevel) hboxuserLevel.addWidget(self.spinBoxOutboundSocksuerLevel) hboxuserLevel.addStretch() self.btnOutboundSocksClear = QPushButton( self.translate("OutboundSocksPanel", "Clear")) self.btnOutboundSocksChange = QPushButton( self.translate("OutboundSocksPanel", "Modify")) self.btnOutboundSocksAdd = QPushButton( self.translate("OutboundSocksPanel", "Add")) self.btnOutboundSocksDelete = QPushButton( self.translate("OutboundSocksPanel", "Delete")) self.spinBoxOutboundSocksPort.setRange(0, 65535) self.spinBoxOutboundSocksPort.setValue(1080) hboxAdress = QHBoxLayout() hboxAdress.addWidget(labelAddress) hboxAdress.addWidget(self.lineEditOutboundSocksAddress) hboxAdress.addWidget(labelPort) hboxAdress.addWidget(self.spinBoxOutboundSocksPort) hboxAdress.addStretch() hboxUser = QHBoxLayout() hboxUser.addWidget(labelUserName) hboxUser.addWidget(self.lineEditOutboundSocksUserName) hboxUser.addWidget(labelPassowrd) hboxUser.addWidget(self.lineEditOutboundSocksPassword) hboxUser.addStretch() vboxBtnUser = QVBoxLayout() vboxBtnUser.addStretch() vboxBtnUser.addWidget(self.btnOutboundSocksAdd) vboxBtnUser.addWidget(self.btnOutboundSocksClear) vboxBtnUser.addWidget(self.btnOutboundSocksChange) vboxBtnUser.addWidget(self.btnOutboundSocksDelete) self.treeViewoutSocksAddress = treeViewoutSocksAddress = QTreeView() treeViewoutSocksAddress.setSelectionMode(QAbstractItemView.SingleSelection) treeViewoutSocksAddress.setSelectionBehavior(QAbstractItemView.SelectRows) treeViewoutSocksAddress.setUniformRowHeights(True) treeViewoutSocksAddress.setEditTriggers(QAbstractItemView.NoEditTriggers) self.treeViewoutSocksAddressMode = QStandardItemModel() self.treeViewoutSocksAddressMode.setHorizontalHeaderLabels(self.labeloutSocks) self.treeViewoutSocksAddress.setModel(self.treeViewoutSocksAddressMode) hboxtreeView = QHBoxLayout() hboxtreeView.addWidget(self.treeViewoutSocksAddress) hboxtreeView.addLayout(vboxBtnUser) vboxOutboundSocks = QVBoxLayout() vboxOutboundSocks.addLayout(hboxAdress) vboxOutboundSocks.addLayout(hboxUser) vboxOutboundSocks.addLayout(hboxuserLevel) vboxOutboundSocks.addLayout(hboxtreeView) groupBoxOutboundSocksPanel = QGroupBox( self.translate("OutboundSocksPanel", "Socks"), self) groupBoxOutboundSocksPanel.setLayout(vboxOutboundSocks) if (v2rayshellDebug): self.__btnDebug = QPushButton("__DebugTest", self) vboxOutboundSocks.addWidget(self.__btnDebug) self.__btnDebug.clicked.connect(self.__DebugTest) self.settingOutboundSocksPanelFromJSONFile(self.outboundSocksJSONFile, True) self.createOutboundSocksPanelSignals() return groupBoxOutboundSocksPanel def createOutboundSocksPanelSignals(self): self.btnOutboundSocksAdd.clicked.connect(self.onbtnOutboundSocksAdd) self.btnOutboundSocksChange.clicked.connect(self.onbtnOutboundSocksChange) self.btnOutboundSocksClear.clicked.connect(self.onbtnOutboundSocksClear) self.btnOutboundSocksDelete.clicked.connect(self.onbtnOutboundSocksDelete) self.treeViewoutSocksAddress.clicked.connect(self.ontreeViewoutSocksAddressClicked) def onbtnOutboundSocksAdd(self): if (self.treeViewoutSocksAddressMode.rowCount() == 0): if (self.lineEditOutboundSocksAddress.text() == ""): return self.treeViewoutSocksAddressMode.appendRow((QStandardItem(str(self.lineEditOutboundSocksAddress.text())), QStandardItem(str(self.spinBoxOutboundSocksPort.value())))) self.treeViewoutSocksAddress.setCurrentIndex(self.treeViewoutSocksAddressMode.index(0, 0)) self.btnOutboundSocksDelete.setEnabled(True) self.lineEditOutboundSocksUserName.setEnabled(True) self.lineEditOutboundSocksPassword.setEnabled(True) self.spinBoxOutboundSocksuerLevel.setEnabled(True) self.onbtnOutboundSocksClear() return itemSelection = self.treeViewoutSocksAddress.selectionModel() rowCurrent = itemSelection.selectedIndexes()[0] root = rowCurrent.parent().row() row = rowCurrent.row() rowCountAdrress = self.treeViewoutSocksAddressMode.rowCount() addresses = [] for i in range(rowCountAdrress): addresses.append(self.treeViewoutSocksAddressMode.item(i, 0).text()+":"+self.treeViewoutSocksAddressMode.item(i, 1).text()) address = self.lineEditOutboundSocksAddress.text()+":"+str(self.spinBoxOutboundSocksPort.value()) if (root == -1): if (address not in addresses and self.lineEditOutboundSocksAddress.text() != ""): self.treeViewoutSocksAddressMode.appendRow((QStandardItem(str(self.lineEditOutboundSocksAddress.text())), QStandardItem(str(self.spinBoxOutboundSocksPort.value())))) self.treeViewoutSocksAddress.setCurrentIndex( self.treeViewoutSocksAddressMode.index(self.treeViewoutSocksAddressMode.rowCount()-1, 0)) self.lineEditOutboundSocksUserName.setEnabled(True) self.lineEditOutboundSocksPassword.setEnabled(True) self.spinBoxOutboundSocksuerLevel.setEnabled(True) self.onbtnOutboundSocksClear() if ((self.treeViewoutSocksAddressMode.item(row).hasChildren() == False) and (self.lineEditOutboundSocksUserName.text() != "" or self.lineEditOutboundSocksPassword.text() != "")): self.treeViewoutSocksAddressMode.item(row).appendRow((QStandardItem(str(self.lineEditOutboundSocksUserName.text())), QStandardItem(str(self.lineEditOutboundSocksPassword.text())), QStandardItem(str(self.spinBoxOutboundSocksuerLevel.value())))) self.lineEditOutboundSocksUserName.setDisabled(True) self.lineEditOutboundSocksPassword.setDisabled(True) self.spinBoxOutboundSocksuerLevel.setDisabled(True) self.onbtnOutboundSocksClear() else: if (self.lineEditOutboundSocksUserName.text() == "" or self.lineEditOutboundSocksPassword.text() == ""): return ### an empty name or password will not be added self.treeViewoutSocksAddressMode.item(root).appendRow((QStandardItem(str(self.lineEditOutboundSocksUserName.text())), QStandardItem(str(self.lineEditOutboundSocksPassword.text())), QStandardItem(str(self.spinBoxOutboundSocksuerLevel.value())))) ### the newest item always will be selected self.treeViewoutSocksAddress.setCurrentIndex( self.treeViewoutSocksAddressMode.index( self.treeViewoutSocksAddressMode.item(root).rowCount()-1, 0, rowCurrent.parent())) self.onbtnOutboundSocksClear() def onbtnOutboundSocksChange(self): if (self.treeViewoutSocksAddressMode.rowCount() == 0): return itemSelection = self.treeViewoutSocksAddress.selectionModel() rowCurrent = itemSelection.selectedIndexes()[0] root = rowCurrent.parent().row() row = rowCurrent.row() rowCountAdrress = self.treeViewoutSocksAddressMode.rowCount() addresses = [] for i in range(rowCountAdrress): addresses.append( self.treeViewoutSocksAddressMode.item(i, 0).text()+":"+self.treeViewoutSocksAddressMode.item(i, 1).text()) address = self.lineEditOutboundSocksAddress.text()+":"+str(self.spinBoxOutboundSocksPort.value()) if (root == -1): if (address in addresses or self.lineEditOutboundSocksAddress.text() == ""): return users = [] ### change address or port will delete child items, now save child items userNumber = self.treeViewoutSocksAddressMode.item(row).rowCount() if (self.treeViewoutSocksAddressMode.hasChildren()): for i in range(userNumber): users.append((self.treeViewoutSocksAddressMode.item(row).child(i, 0).text(), self.treeViewoutSocksAddressMode.item(row).child(i, 1).text())) self.treeViewoutSocksAddressMode.setItem(row, 0, QStandardItem(self.lineEditOutboundSocksAddress.text())) self.treeViewoutSocksAddressMode.setItem(row, 1, QStandardItem(str(self.spinBoxOutboundSocksPort.value()))) self.treeViewoutSocksAddress.setCurrentIndex(self.treeViewoutSocksAddressMode.index(row, 0)) ### restore child items if (self.treeViewoutSocksAddressMode.hasChildren()): for i in range(userNumber): self.treeViewoutSocksAddressMode.item(row).appendRow((QStandardItem(users[i][0]), QStandardItem(users[i][1]), QStandardItem(users[i][2]))) else: if (self.lineEditOutboundSocksUserName.text() == "" or self.lineEditOutboundSocksPassword.text() == ""): return self.treeViewoutSocksAddressMode.item(root).setChild(row, 0, QStandardItem(self.lineEditOutboundSocksUserName.text())) self.treeViewoutSocksAddressMode.item(root).setChild(row, 1, QStandardItem(self.lineEditOutboundSocksPassword.text())) self.treeViewoutSocksAddressMode.item(root).setChild(row, 2, QStandardItem(str(self.spinBoxOutboundSocksuerLevel.value()))) self.treeViewoutSocksAddress.setCurrentIndex(self.treeViewoutSocksAddressMode.index(row, 0, rowCurrent.parent())) def onbtnOutboundSocksClear(self): if (self.lineEditOutboundSocksAddress.isEnabled()): self.lineEditOutboundSocksAddress.clear() self.spinBoxOutboundSocksPort.setValue(1080) if (self.lineEditOutboundSocksUserName.isEnabled()): self.lineEditOutboundSocksPassword.clear() self.lineEditOutboundSocksUserName.clear() self.spinBoxOutboundSocksuerLevel.setValue(0) def onbtnOutboundSocksDelete(self): if (self.treeViewoutSocksAddressMode.rowCount() == 0): return itemSelection = self.treeViewoutSocksAddress.selectionModel() rowCurrent = itemSelection.selectedIndexes()[0] root = rowCurrent.parent().row() row = rowCurrent.row() if (root == -1): self.treeViewoutSocksAddressMode.removeRow(row) if (self.treeViewoutSocksAddressMode.rowCount() == 0): self.lineEditOutboundSocksAddress.setEnabled(True) self.spinBoxOutboundSocksPort.setEnabled(True) self.lineEditOutboundSocksUserName.setDisabled(True) self.lineEditOutboundSocksPassword.setDisabled(True) self.onbtnOutboundSocksClear() else: self.treeViewoutSocksAddressMode.item(root).removeRow(row) self.onbtnOutboundSocksClear() def ontreeViewoutSocksAddressClicked(self): itemSelection = self.treeViewoutSocksAddress.selectionModel() rowCurrent = itemSelection.selectedIndexes()[0] root = rowCurrent.parent().row() row = rowCurrent.row() if (root == -1): address = self.treeViewoutSocksAddressMode.item(row, 0).text() port = self.treeViewoutSocksAddressMode.item(row, 1).text() self.lineEditOutboundSocksAddress.setText(address) self.spinBoxOutboundSocksPort.setValue(int(port)) self.lineEditOutboundSocksPassword.clear() self.lineEditOutboundSocksUserName.clear() self.lineEditOutboundSocksAddress.setEnabled(True) self.spinBoxOutboundSocksPort.setEnabled(True) if (self.treeViewoutSocksAddressMode.item(row).hasChildren()): self.lineEditOutboundSocksUserName.setEnabled(False) self.lineEditOutboundSocksPassword.setEnabled(False) self.spinBoxOutboundSocksuerLevel.setEnabled(False) else: self.lineEditOutboundSocksUserName.setEnabled(True) self.lineEditOutboundSocksPassword.setEnabled(True) self.spinBoxOutboundSocksuerLevel.setEnabled(True) else: address = self.treeViewoutSocksAddressMode.item(root, 0).text() port = self.treeViewoutSocksAddressMode.item(root, 1).text() user = self.treeViewoutSocksAddressMode.item(root).child(row, 0).text() password = self.treeViewoutSocksAddressMode.item(root).child(row, 1).text() level = self.treeViewoutSocksAddressMode.item(root).child(row, 2).text() self.lineEditOutboundSocksUserName.setText(user) self.lineEditOutboundSocksPassword.setText(password) self.lineEditOutboundSocksAddress.setText(address) self.spinBoxOutboundSocksPort.setValue(int(port)) self.spinBoxOutboundSocksuerLevel.setValue(int(level)) self.lineEditOutboundSocksAddress.setEnabled(False) self.spinBoxOutboundSocksPort.setEnabled(False) self.lineEditOutboundSocksUserName.setEnabled(True) self.lineEditOutboundSocksPassword.setEnabled(True) self.spinBoxOutboundSocksuerLevel.setEnabled(True) def settingOutboundSocksPanelFromJSONFile(self, outboundSocksJSONFile = {}, openFromJSONFile = True): logbook.setisOpenJSONFile(openFromJSONFile) self.treeViewoutSocksAddressMode.setRowCount(0) if (outboundSocksJSONFile == None): outboundSocksJSONFile = {} try: outboundSocksJSONFile["servers"] except KeyError as e: logbook.writeLog("OutboundSocks", "KeyError", e) outboundSocksJSONFile["servers"] = [] servers = outboundSocksJSONFile["servers"] serversNumber = len(servers) ### just show the first server detail in TabelWidget if (serversNumber > 0): try: self.lineEditOutboundSocksAddress.setText(servers[0]["address"]) except KeyError as e: logbook.writeLog("OutboundSocks", "KeyError", e) try: self.spinBoxOutboundSocksPort.setValue(int(servers[0]["port"])) except KeyError as e: logbook.writeLog("OutboundSocks", "KeyError", e) except (ValueError, TypeError) as e: logbook.writeLog("OutboundSocks", "ValueError or TypeError", e) for i in range(serversNumber): try: usersNumber = len(servers[i]["users"]) except KeyError as e: logbook.writeLog("OutboundSocks", "KeyError", e) usersNumber = 0 try: users = servers[i]["users"] except KeyError as e: logbook.writeLog("OutboundSocks", "KeyError", e) try: serverAddress = QStandardItem(str(servers[i]["address"])) except KeyError as e: logbook.writeLog("OutboundSocks", "KeyError", e) try: serverPort = QStandardItem(str(servers[i]["port"])) except KeyError as e: logbook.writeLog("OutboundSocks", "KeyError", e) for j in range(usersNumber): try: user = QStandardItem(str(users[j]["user"])) password = QStandardItem(str(users[j]["pass"])) try: level = QStandardItem(str(users[j]["level"])) except Exception: level = QStandardItem("0") try: self.treasureChest.addLevel(users[j]["level"]) except Exception: pass serverAddress.appendRow((user, password, level)) except KeyError as e: logbook.writeLog("OutboundSocks set user", "KeyError", e) except TypeError as e: logbook.writeLog("OutboundSocks set user", "TypeError", e) except ValueError as e: logbook.writeLog("OutboundSocks set user", "ValueError", e) except: logbook.writeLog("OutboundSocks set user", "unkonw") self.treeViewoutSocksAddressMode.appendRow((serverAddress, serverPort)) ### make a sure add items success if (self.treeViewoutSocksAddressMode.rowCount() > 0): ### if there no any selectItem, "Add, Modify, Delete" button's slot will crash self.treeViewoutSocksAddress.setCurrentIndex(self.treeViewoutSocksAddressMode.index(0, 0)) def createOutboundSocksJSONFile(self): outboundSocksJSONFile = {} outboundSocksJSONFile["servers"] = [] serversNumber = self.treeViewoutSocksAddressMode.rowCount() if (serversNumber < 0): return self.outboundSocksJSONFile for i in range(serversNumber): server = {} user = {} server["address"] = self.treeViewoutSocksAddressMode.item(i, 0).text() server["port"] = int(self.treeViewoutSocksAddressMode.item(i, 1).text()) server["users"] = [] if (self.treeViewoutSocksAddressMode.hasChildren()): usersNumber = self.treeViewoutSocksAddressMode.item(i).rowCount() for j in range(usersNumber): user["user"] = self.treeViewoutSocksAddressMode.item(i).child(j, 0).text() user["pass"] = self.treeViewoutSocksAddressMode.item(i).child(j, 1).text() user["level"] = int(self.treeViewoutSocksAddressMode.item(i).child(j, 2).text()) try: self.treasureChest.addLevel(user["level"]) except Exception: pass server["users"].append(copy.deepcopy(user)) else: server["users"] = [] outboundSocksJSONFile["servers"].append(copy.deepcopy(server)) return outboundSocksJSONFile def clearoutboundSocksPanel(self): self.treeViewoutSocksAddressMode.setRowCount(0) self.lineEditOutboundSocksAddress.clear() self.lineEditOutboundSocksPassword.clear() self.lineEditOutboundSocksUserName.clear() self.spinBoxOutboundSocksPort.setValue(1080) self.spinBoxOutboundSocksuerLevel.setValue(0) def __DebugTest(self): import json print(json.dumps(self.createOutboundSocksJSONFile(), indent = 4, sort_keys = False))
class OutboundVmessSettingPanel(QWidget): def __init__(self): super().__init__() self.outboundVmessJSONFile = { "vnext": [{ "address": "127.0.0.1", "port": 443, "users": [{ "id": "27848739-7e62-4138-9fd3-098a63964b6b", "alterId": 10, "security": "aes-128-cfb", "level": 0 }] }] } self.translate = QCoreApplication.translate self.labeloutVmess = (self.translate("OutboundVmessSettingPanel", "Address/AlterID"), self.translate("OutboundVmessSettingPanel", "Port/Method"), self.translate("OutboundVmessSettingPanel", "UUID"), self.translate("OutboundVmessSettingPanel", "Level")) self.listMethodoutVmess = "auto", "aes-128-cfb", "aes-128-gcm", "chacha20-poly1305", "none" def createOutboundVmessPanel(self): self.treeViewOutboundVmessAddress = treeViewOutboundVmessAddress = QTreeView( ) treeViewOutboundVmessAddress.setSelectionMode( QAbstractItemView.SingleSelection) treeViewOutboundVmessAddress.setSelectionBehavior( QAbstractItemView.SelectRows) treeViewOutboundVmessAddress.setUniformRowHeights(True) self.treeViewOutboundVmessAddressMode = QStandardItemModel() self.treeViewOutboundVmessAddressMode.setHorizontalHeaderLabels( self.labeloutVmess) treeViewOutboundVmessAddress.setModel( self.treeViewOutboundVmessAddressMode) treeViewOutboundVmessAddress.setItemDelegateForColumn( 1, toolbox.ComboBoxSpinBoxDelegate(self.listMethodoutVmess)) self.btnoutVmessNewUser = QPushButton( self.translate("OutboundVmessSettingPanel", "New User")) self.btnoutVmessNewServer = QPushButton( self.translate("OutboundVmessSettingPanel", "New Sever")) self.btnoutVmessDelete = QPushButton( self.translate("OutboundVmessSettingPanel", "Delete")) vboxBtn = QVBoxLayout() vboxBtn.addWidget(QLabel()) vboxBtn.addWidget(QLabel()) vboxBtn.addWidget(QLabel()) vboxBtn.addWidget(QLabel()) vboxBtn.addWidget(self.btnoutVmessNewUser) vboxBtn.addWidget(self.btnoutVmessNewServer) vboxBtn.addWidget(self.btnoutVmessDelete) hboxTreeView = QHBoxLayout() hboxTreeView.addWidget(treeViewOutboundVmessAddress) hboxTreeView.addLayout(vboxBtn) vboxOutBoundVmessUser = QVBoxLayout() vboxOutBoundVmessUser.addLayout(hboxTreeView) groupBoxOutboundVmessUser = QGroupBox("", self) groupBoxOutboundVmessUser.setLayout(vboxOutBoundVmessUser) self.createOutboundVmessPanelSignals() if (v2rayshellDebug): self.__btnDebug = QPushButton("__DebugTest", self) self.__btnDebug.clicked.connect(self.__DebugTest) vboxOutBoundVmessUser.addWidget(self.__btnDebug) self.setLayout(vboxOutBoundVmessUser) self.settingOutboundVmessPanelFromJSONFile( self.outboundVmessJSONFile, True) return return groupBoxOutboundVmessUser def createOutboundVmessPanelSignals(self): self.btnoutVmessDelete.clicked.connect(self.onbtnoutVmessDelete) self.btnoutVmessNewServer.clicked.connect(self.onbtnoutVmessNewServer) self.btnoutVmessNewUser.clicked.connect(self.onbtnoutVmessNewUser) def onbtnoutVmessDelete(self): if (not self.treeViewOutboundVmessAddressMode.rowCount()): return itemSelection = self.treeViewOutboundVmessAddress.selectionModel() rowCurrent = itemSelection.selectedIndexes()[0] root = rowCurrent.parent().row() row = rowCurrent.row() if (root == -1): self.treeViewOutboundVmessAddressMode.removeRow(row) else: self.treeViewOutboundVmessAddressMode.item(root).removeRow(row) def onbtnoutVmessNewUser(self): rowCount = self.treeViewOutboundVmessAddressMode.rowCount() if not rowCount: self.newRowOutVmess(rowCount) self.treeViewOutboundVmessAddress.setCurrentIndex( self.treeViewOutboundVmessAddressMode.index(rowCount, 0)) return itemSelection = self.treeViewOutboundVmessAddress.selectionModel() rowCurrent = itemSelection.selectedIndexes()[0] root = rowCurrent.parent().row() row = rowCurrent.row() if root == -1: self.treeViewOutboundVmessAddressMode.item(row).appendRow( (QStandardItem("10"), QStandardItem("aes-128-cfb"), QStandardItem(self.createUUID()), QStandardItem("0"))) self.treeViewOutboundVmessAddress.expand( self.treeViewOutboundVmessAddressMode.index(row, 0)) else: self.treeViewOutboundVmessAddressMode.item(root).appendRow( (QStandardItem("10"), QStandardItem("aes-128-cfb"), QStandardItem(self.createUUID()), QStandardItem("0"))) def onbtnoutVmessNewServer(self): row = self.treeViewOutboundVmessAddressMode.rowCount() if not row: self.newRowOutVmess(row) self.treeViewOutboundVmessAddress.setCurrentIndex( self.treeViewOutboundVmessAddressMode.index(row, 0)) else: r = self.treeViewOutboundVmessAddressMode.item(row - 1) if r.hasChildren(): self.newRowOutVmess(row) def newRowOutVmess(self, row): self.treeViewOutboundVmessAddressMode.setRowCount(row + 1) address = self.treeViewOutboundVmessAddressMode.index(row, 0) port = self.treeViewOutboundVmessAddressMode.index(row, 1) self.treeViewOutboundVmessAddressMode.setData(address, "127.0.0.1") self.treeViewOutboundVmessAddressMode.setData(port, "443") def settingOutboundVmessPanelFromJSONFile(self, outboundVmessJSONFile=None, openFromJSONFile=False): logbook.setisOpenJSONFile(openFromJSONFile) self.treeViewOutboundVmessAddressMode.setRowCount(0) if (not outboundVmessJSONFile): outboundVmessJSONFile = {} try: outboundVmessJSONFile["vnext"] except KeyError as e: logbook.writeLog("OutboundVmess", "KeyError", e) outboundVmessJSONFile["vnext"] = [] addressNumber = len(outboundVmessJSONFile["vnext"]) if (addressNumber): addresses = outboundVmessJSONFile["vnext"] for i in range(addressNumber): try: users = addresses[i]["users"] usersNumber = len(users) serverAddress = QStandardItem(addresses[i]["address"]) serverPort = QStandardItem(str(addresses[i]["port"])) if (usersNumber): for j in range(usersNumber): try: uuidString = QStandardItem(str(users[j]["id"])) except Exception: uuidString = QStandardItem("") try: alterId = QStandardItem( str(users[j]["alterId"])) except Exception: alterId = QStandardItem("") try: security = QStandardItem( str(users[j]["security"])) except Exception: security = QStandardItem("") try: level = QStandardItem(str(users[j]["level"])) except Exception: level = QStandardItem("0") try: self.treasureChest.addLevel(users[j]["level"]) except Exception: pass serverAddress.appendRow( (alterId, security, uuidString, level)) self.treeViewOutboundVmessAddressMode.appendRow( (serverAddress, serverPort)) except KeyError as e: logbook.writeLog("OutboundVmess", "KeyError", e) except: logbook.writeLog("OutboundVmess", "unkonw") if (self.treeViewOutboundVmessAddressMode.rowCount() > 0): self.treeViewOutboundVmessAddress.setCurrentIndex( self.treeViewOutboundVmessAddressMode.index(0, 0)) def createOutboundVmessJSONFile(self): outboundVmessJSONFile = {} outboundVmessJSONFile["vnext"] = [] serversNumber = self.treeViewOutboundVmessAddressMode.rowCount() if (not serversNumber): return for i in range(serversNumber): server = {} user = {} server["address"] = self.treeViewOutboundVmessAddressMode.item( i, 0).text() server["port"] = int( self.treeViewOutboundVmessAddressMode.item(i, 1).text()) server["users"] = [] if (self.treeViewOutboundVmessAddressMode.hasChildren()): usersNumber = self.treeViewOutboundVmessAddressMode.item( i).rowCount() for j in range(usersNumber): user["id"] = self.treeViewOutboundVmessAddressMode.item( i).child(j, 2).text() user["alterId"] = int( self.treeViewOutboundVmessAddressMode.item(i).child( j, 0).text()) user[ "security"] = self.treeViewOutboundVmessAddressMode.item( i).child(j, 1).text() user["level"] = int( self.treeViewOutboundVmessAddressMode.item(i).child( j, 3).text()) try: self.treasureChest.addLevel(user["level"]) except Exception: pass server["users"].append(copy.deepcopy(user)) else: server["users"] = [] outboundVmessJSONFile["vnext"].append(copy.deepcopy(server)) return outboundVmessJSONFile def createUUID(self): return str(uuid.uuid4()) def validateUUID4(self, uuidString): try: uuid.UUID(uuidString, version=4) except ValueError: return False return True def clearOutVmessPanel(self): self.treeViewOutboundVmessAddressMode.setRowCount(0) def __DebugTest(self): import json print( json.dumps(self.createOutboundVmessJSONFile(), indent=4, sort_keys=False))
class OutboundVmessSettingPanel(QWidget): def __init__(self): super().__init__() self.outboundVmessJSONFile = { "vnext": [ { "address": "127.0.0.1", "port": 443, "users": [ { "id": "27848739-7e62-4138-9fd3-098a63964b6b", "alterId": 10, "security": "aes-128-cfb", "level": 0 } ] } ] } self.translate = QCoreApplication.translate self.labeloutVmess = (self.translate("OutboundVmessSettingPanel", "Address/AlterID"), self.translate("OutboundVmessSettingPanel", "Port/Method"), self.translate("OutboundVmessSettingPanel", "UUID"), self.translate("OutboundVmessSettingPanel", "Level")) self.listMethodoutVmess = "auto", "aes-128-cfb", "aes-128-gcm", "chacha20-poly1305", "none" def createOutboundVmessPanel(self): labelAddress = QLabel( self.translate("OutboundVmessSettingPanel", "Server Address: "), self) self.lineEditOutboundVmessAddress = QLineEdit() labelPort = QLabel( self.translate("OutboundVmessSettingPanel", "Port: "), self) self.spinBoxOutboundVmessPort = QSpinBox() self.spinBoxOutboundVmessPort.setRange(0, 65535) self.spinBoxOutboundVmessPort.setValue(443) labelUIID = QLabel( self.translate("OutboundVmessSettingPanel", "UUID: "), self) self.lineEditOutboundVmessUUID = QLineEdit() self.btnoutVmessGerateUUID = QPushButton( self.translate("OutboundVmessSettingPanel", "Gerate UUID"), self) labelAlterID = QLabel( self.translate("OutboundVmessSettingPanel", "AlterID: "), self) self.spinBoxOutboundVmessAlterID = QSpinBox() self.comboBoxOutboundVmessMethod = QComboBox() self.spinBoxOutboundVmessAlterID.setRange(0, 65535) self.lineEditOutboundVmessUUID.setInputMask("HHHHHHHH-HHHH-HHHH-HHHH-HHHHHHHHHHHH;_") self.comboBoxOutboundVmessMethod.addItems(self.listMethodoutVmess) labelLevel = QLabel( self.translate("OutboundVmessSettingPanel", "User Level: ")) self.spinBoxOutboundVmessuserLevel = QSpinBox() self.spinBoxOutboundVmessuserLevel.setRange(0, 65535) self.spinBoxOutboundVmessuserLevel.setValue(0) hboxuserLevel = QHBoxLayout() hboxuserLevel.addWidget(labelLevel) hboxuserLevel.addWidget(self.spinBoxOutboundVmessuserLevel) hboxAddress = QHBoxLayout() hboxAddress.addWidget(labelAddress) hboxAddress.addWidget(self.lineEditOutboundVmessAddress) hboxAddress.addWidget(labelPort) hboxAddress.addWidget(self.spinBoxOutboundVmessPort) hboxAddress.addStretch() self.groupBoxOutBoundVmessAddress = QGroupBox("", self) self.groupBoxOutBoundVmessAddress.setLayout(hboxAddress) hboxID = QHBoxLayout() hboxID.addWidget(labelUIID) hboxID.addWidget(self.lineEditOutboundVmessUUID) hboxID.addWidget(self.btnoutVmessGerateUUID) hboxAlterID = QHBoxLayout() hboxAlterID.addLayout(hboxuserLevel) hboxAlterID.addWidget(labelAlterID) hboxAlterID.addWidget(self.spinBoxOutboundVmessAlterID) hboxAlterID.addWidget(self.comboBoxOutboundVmessMethod) hboxAlterID.addStretch() self.treeViewOutboundVmessAddress = treeViewOutboundVmessAddress = QTreeView() treeViewOutboundVmessAddress.setSelectionMode(QAbstractItemView.SingleSelection) treeViewOutboundVmessAddress.setSelectionBehavior(QAbstractItemView.SelectRows) treeViewOutboundVmessAddress.setUniformRowHeights(True) treeViewOutboundVmessAddress.setEditTriggers(QAbstractItemView.NoEditTriggers) self.treeViewOutboundVmessAddressMode = QStandardItemModel() self.treeViewOutboundVmessAddressMode.setHorizontalHeaderLabels(self.labeloutVmess) treeViewOutboundVmessAddress.setModel(self.treeViewOutboundVmessAddressMode) self.btnoutVmessClear = QPushButton( self.translate("OutboundVmessSettingPanel", "Clear")) self.btnoutVmessChange = QPushButton( self.translate("OutboundVmessSettingPanel", "Modify")) self.btnoutVmessAdd = QPushButton( self.translate("OutboundVmessSettingPanel", "Add")) self.btnoutVmessDelete = QPushButton( self.translate("OutboundVmessSettingPanel", "Delete")) vboxBtn = QVBoxLayout() vboxBtn.addStretch() vboxBtn.addWidget(self.btnoutVmessAdd) vboxBtn.addWidget(self.btnoutVmessClear) vboxBtn.addWidget(self.btnoutVmessChange) vboxBtn.addWidget(self.btnoutVmessDelete) vboxID = QVBoxLayout() vboxID.addLayout(hboxID) vboxID.addLayout(hboxAlterID) self.groupBoxOutBoundVmessID = QGroupBox("", self) self.groupBoxOutBoundVmessID.setLayout(vboxID) self.groupBoxOutBoundVmessID.setEnabled(False) hboxTreeView = QHBoxLayout() hboxTreeView.addWidget(treeViewOutboundVmessAddress) hboxTreeView.addLayout(vboxBtn) vboxOutBoundVmessUser = QVBoxLayout() vboxOutBoundVmessUser.addWidget(self.groupBoxOutBoundVmessAddress) vboxOutBoundVmessUser.addWidget(self.groupBoxOutBoundVmessID) vboxOutBoundVmessUser.addLayout(hboxTreeView) self.groupBoxOutboundVmessUser = QGroupBox("", self) self.groupBoxOutboundVmessUser.setLayout(vboxOutBoundVmessUser) self.createOutboundVmessPanelSignals() if(v2rayshellDebug): self.__btnDebug = QPushButton("__DebugTest", self) self.__btnDebug.clicked.connect(self.__DebugTest) vboxOutBoundVmessUser.addWidget(self.__btnDebug) self.setLayout(vboxOutBoundVmessUser) self.settingOutboundVmessPanelFromJSONFile(self.outboundVmessJSONFile, True) return return self.groupBoxOutboundVmessUser def createOutboundVmessPanelSignals(self): self.btnoutVmessGerateUUID.clicked.connect(self.onbtnoutVmessGerateUUID) self.treeViewOutboundVmessAddress.clicked.connect(self.ontreeViewOutboundVmessAddress) self.btnoutVmessClear.clicked.connect(self.onbtnoutVmessClear) self.btnoutVmessAdd.clicked.connect(self.onbtnoutVmessAdd) self.btnoutVmessDelete.clicked.connect(self.onbtnoutVmessDelete) self.btnoutVmessChange.clicked.connect(self.onbtnoutVmessChange) def onbtnoutVmessGerateUUID(self): self.lineEditOutboundVmessUUID.setText(self.createUUID()) def onbtnoutVmessClear(self): if (self.groupBoxOutBoundVmessAddress.isEnabled()): self.outVmessClearAddress() if (self.groupBoxOutBoundVmessID.isEnabled()): self.outVmessClearID() def outVmessClearID(self): self.lineEditOutboundVmessUUID.clear() self.spinBoxOutboundVmessAlterID.setValue(10) self.comboBoxOutboundVmessMethod.setCurrentText("aes-128-cfb") self.spinBoxOutboundVmessuserLevel.setValue(0) def outVmessClearAddress(self): self.lineEditOutboundVmessAddress.clear() self.spinBoxOutboundVmessPort.setValue(443) def ontreeViewOutboundVmessAddress(self): itemSelection = self.treeViewOutboundVmessAddress.selectionModel() rowCurrent = itemSelection.selectedIndexes()[0] root = rowCurrent.parent().row() row = rowCurrent.row() if (root == -1): self.groupBoxOutBoundVmessAddress.setEnabled(True) address = self.treeViewOutboundVmessAddressMode.item(row, 0).text() port = self.treeViewOutboundVmessAddressMode.item(row, 1).text() self.lineEditOutboundVmessAddress.setText(address) self.spinBoxOutboundVmessPort.setValue(int(port)) self.outVmessClearID() if (self.treeViewOutboundVmessAddressMode.item(row).hasChildren()): self.groupBoxOutBoundVmessID.setEnabled(False) else: self.groupBoxOutBoundVmessID.setEnabled(True) else: self.groupBoxOutBoundVmessID.setEnabled(True) self.groupBoxOutBoundVmessAddress.setEnabled(False) address = self.treeViewOutboundVmessAddressMode.item(root, 0).text() port = self.treeViewOutboundVmessAddressMode.item(root, 1).text() uuidString = self.treeViewOutboundVmessAddressMode.item(root).child(row, 2).text() alterId = self.treeViewOutboundVmessAddressMode.item(root).child(row, 0).text() security = self.treeViewOutboundVmessAddressMode.item(root).child(row, 1).text() level = self.treeViewOutboundVmessAddressMode.item(root).child(row, 3).text() self.lineEditOutboundVmessAddress.setText(address) self.spinBoxOutboundVmessPort.setValue(int(port)) self.lineEditOutboundVmessUUID.setText(uuidString) self.spinBoxOutboundVmessAlterID.setValue(int(alterId)) self.comboBoxOutboundVmessMethod.setCurrentText(security) self.spinBoxOutboundVmessuserLevel.setValue(int(level)) def onbtnoutVmessChange(self): if (not self.treeViewOutboundVmessAddressMode.rowCount()): return itemSelection = self.treeViewOutboundVmessAddress.selectionModel() rowCurrent = itemSelection.selectedIndexes()[0] root = rowCurrent.parent().row() row = rowCurrent.row() rowCountAddress = self.treeViewOutboundVmessAddressMode.rowCount() addresses = [] for i in range(rowCountAddress): # make sure no Duplicate address addresses.append( self.treeViewOutboundVmessAddressMode.item(i, 0).text() + ":" + self.treeViewOutboundVmessAddressMode.item(i, 1).text()) address = self.lineEditOutboundVmessAddress.text() + ":" + str(self.spinBoxOutboundVmessPort.value()) if (root == -1): if ((address in addresses) or (self.lineEditOutboundVmessAddress.text() == "")): return users = [] usersNumber = self.treeViewOutboundVmessAddressMode.item(row).rowCount() if (self.treeViewOutboundVmessAddressMode.hasChildren()): # or use usersNumber > 0 but hasChildren() more clearer for i in range(usersNumber): alterID = self.treeViewOutboundVmessAddressMode.item(row).child(i, 0).text() security = self.treeViewOutboundVmessAddressMode.item(row).child(i, 1).text() uuidString = self.treeViewOutboundVmessAddressMode.item(row).child(i, 2).text() level = self.treeViewOutboundVmessAddressMode.item(row).child(i, 3).text() users.append((alterID, security, uuidString, level)) self.treeViewOutboundVmessAddressMode.setItem( row, 0, QStandardItem(str(self.lineEditOutboundVmessAddress.text()))) self.treeViewOutboundVmessAddressMode.setItem( row, 1, QStandardItem(str(self.spinBoxOutboundVmessPort.value()))) self.treeViewOutboundVmessAddress.setCurrentIndex( self.treeViewOutboundVmessAddressMode.index(row, 0)) if (self.treeViewOutboundVmessAddressMode.hasChildren()): for i in range(usersNumber): alterID = QStandardItem(str(users[i][0])) security = QStandardItem(str(users[i][1])) uuidString = QStandardItem(str(users[i][2])) level = QStandardItem(str(users[i][3])) self.treeViewOutboundVmessAddressMode.item(row).appendRow((alterID, security, uuidString, level)) else: if (self.lineEditOutboundVmessUUID.text() == "----"): return alterID = QStandardItem(str(self.spinBoxOutboundVmessAlterID.value())) security = QStandardItem(str(self.comboBoxOutboundVmessMethod.currentText())) uuidString = QStandardItem(str(self.lineEditOutboundVmessUUID.text())) level = QStandardItem(str(self.spinBoxOutboundVmessuserLevel.value())) self.treeViewOutboundVmessAddressMode.item(root).setChild(row, 0, alterID) self.treeViewOutboundVmessAddressMode.item(root).setChild(row, 1, security) self.treeViewOutboundVmessAddressMode.item(root).setChild(row, 2, uuidString) self.treeViewOutboundVmessAddressMode.item(root).setChild(row, 3, level) self.treeViewOutboundVmessAddress.setCurrentIndex( self.treeViewOutboundVmessAddressMode.index(row, 0, rowCurrent.parent())) def onbtnoutVmessAdd(self): if (not self.treeViewOutboundVmessAddressMode.rowCount()): if (not self.lineEditOutboundVmessAddress.text()): return self.treeViewOutboundVmessAddressMode.appendRow((QStandardItem(str(self.lineEditOutboundVmessAddress.text())), QStandardItem(str(self.spinBoxOutboundVmessPort.value())))) self.treeViewOutboundVmessAddress.setCurrentIndex( self.treeViewOutboundVmessAddressMode.index(0, 0)) self.btnoutVmessDelete.setEnabled(True) self.groupBoxOutBoundVmessID.setEnabled(True) self.onbtnoutVmessClear() return itemSelection = self.treeViewOutboundVmessAddress.selectionModel() rowCurrent = itemSelection.selectedIndexes()[0] root = rowCurrent.parent().row() row = rowCurrent.row() rowCountAddress = self.treeViewOutboundVmessAddressMode.rowCount() addresses = [] for i in range(rowCountAddress): # make sure no Duplicate address addresses.append(self.treeViewOutboundVmessAddressMode.item(i, 0).text() + ":" + self.treeViewOutboundVmessAddressMode.item(i, 1).text()) address = self.lineEditOutboundVmessAddress.text() + ":" + str(self.spinBoxOutboundVmessPort.value()) if (root == -1): # user clicked address if (address not in addresses and self.lineEditOutboundVmessAddress.text() != ""): self.treeViewOutboundVmessAddressMode.appendRow((QStandardItem(str(self.lineEditOutboundVmessAddress.text())), QStandardItem(str(self.spinBoxOutboundVmessPort.value())))) self.treeViewOutboundVmessAddress.setCurrentIndex( self.treeViewOutboundVmessAddressMode.index( self.treeViewOutboundVmessAddressMode.rowCount() - 1, 0)) self.groupBoxOutBoundVmessID.setEnabled(True) self.onbtnoutVmessClear() if (not self.treeViewOutboundVmessAddressMode.item(row).hasChildren() and (self.lineEditOutboundVmessUUID.text() != "----")): alterID = QStandardItem(str(self.spinBoxOutboundVmessAlterID.value())) security = QStandardItem(str(self.comboBoxOutboundVmessMethod.currentText())) uuidString = QStandardItem(str(self.lineEditOutboundVmessUUID.text())) level = QStandardItem(str(self.spinBoxOutboundVmessuserLevel.value())) self.treeViewOutboundVmessAddressMode.item(row).appendRow((alterID, security, uuidString, level)) self.onbtnoutVmessClear() else: if (self.lineEditOutboundVmessUUID.text() == "----"): return # an empty UUID not allow alterID = QStandardItem(str(self.spinBoxOutboundVmessAlterID.value())) security = QStandardItem(str(self.comboBoxOutboundVmessMethod.currentText())) uuidString = QStandardItem(str(self.lineEditOutboundVmessUUID.text())) level = QStandardItem(str(self.spinBoxOutboundVmessuserLevel.value())) self.treeViewOutboundVmessAddressMode.item(root).appendRow((alterID, security, uuidString, level)) # the newest item always will be selected self.treeViewOutboundVmessAddress.setCurrentIndex( self.treeViewOutboundVmessAddressMode.index( self.treeViewOutboundVmessAddressMode.item(root).rowCount() - 1, 0, rowCurrent.parent())) self.onbtnoutVmessClear() def onbtnoutVmessDelete(self): if (not self.treeViewOutboundVmessAddressMode.rowCount()): return itemSelection = self.treeViewOutboundVmessAddress.selectionModel() rowCurrent = itemSelection.selectedIndexes()[0] root = rowCurrent.parent().row() row = rowCurrent.row() if (root == -1): self.treeViewOutboundVmessAddressMode.removeRow(row) if (not self.treeViewOutboundVmessAddressMode.rowCount()): self.onbtnoutVmessClear() self.groupBoxOutBoundVmessAddress.setEnabled(True) self.groupBoxOutBoundVmessID.setDisabled(True) self.onbtnoutVmessClear() else: self.treeViewOutboundVmessAddressMode.item(root).removeRow(row) self.onbtnoutVmessClear() def settingOutboundVmessPanelFromJSONFile(self, outboundVmessJSONFile=None, openFromJSONFile=False): logbook.setisOpenJSONFile(openFromJSONFile) self.treeViewOutboundVmessAddressMode.setRowCount(0) if (not outboundVmessJSONFile): outboundVmessJSONFile = {} try: outboundVmessJSONFile["vnext"] except KeyError as e: logbook.writeLog("OutboundVmess", "KeyError", e) outboundVmessJSONFile["vnext"] = [] addressNumber = len(outboundVmessJSONFile["vnext"]) if (addressNumber): addresses = outboundVmessJSONFile["vnext"] try: self.lineEditOutboundVmessAddress.setText(str(addresses[0]["address"])) except KeyError as e: logbook.writeLog("OutboundVmess", "KeyError", e) except ValueError as e: logbook.writeLog("OutboundVmess", "KeyError", e) try: self.spinBoxOutboundVmessPort.setValue(int(addresses[0]["port"])) except KeyError as e: logbook.writeLog("OutboundVmess", "KeyError", e) except (TypeError, ValueError) as e: logbook.writeLog("OutboundVmess", "ValueError or TypeError", e) for i in range(addressNumber): try: users = addresses[i]["users"] usersNumber = len(users) serverAddress = QStandardItem(addresses[i]["address"]) serverPort = QStandardItem(str(addresses[i]["port"])) if (usersNumber): for j in range(usersNumber): try: uuidString = QStandardItem(str(users[j]["id"])) except Exception: uuidString = QStandardItem("") try: alterId = QStandardItem(str(users[j]["alterId"])) except Exception: alterId = QStandardItem("") try: security = QStandardItem(str(users[j]["security"])) except Exception: security = QStandardItem("") try: level = QStandardItem(str(users[j]["level"])) except Exception: level = QStandardItem("0") try: self.treasureChest.addLevel(users[j]["level"]) except Exception:pass serverAddress.appendRow((alterId, security, uuidString, level)) self.treeViewOutboundVmessAddressMode.appendRow((serverAddress, serverPort)) except KeyError as e: logbook.writeLog("OutboundVmess", "KeyError", e) except: logbook.writeLog("OutboundVmess", "unkonw") if (self.treeViewOutboundVmessAddressMode.rowCount() > 0): self.treeViewOutboundVmessAddress.setCurrentIndex(self.treeViewOutboundVmessAddressMode.index(0, 0)) def createOutboundVmessJSONFile(self): outboundVmessJSONFile = {} outboundVmessJSONFile["vnext"] = [] serversNumber = self.treeViewOutboundVmessAddressMode.rowCount() if (not serversNumber): return self.outboundVmessJSONFile for i in range(serversNumber): server = {} user = {} server["address"] = self.treeViewOutboundVmessAddressMode.item(i, 0).text() server["port"] = int(self.treeViewOutboundVmessAddressMode.item(i, 1).text()) server["users"] = [] if (self.treeViewOutboundVmessAddressMode.hasChildren()): usersNumber = self.treeViewOutboundVmessAddressMode.item(i).rowCount() for j in range(usersNumber): user["id"] = self.treeViewOutboundVmessAddressMode.item(i).child(j, 2).text() user["alterId"] = int(self.treeViewOutboundVmessAddressMode.item(i).child(j, 0).text()) user["security"] = self.treeViewOutboundVmessAddressMode.item(i).child(j, 1).text() user["level"] = int(self.treeViewOutboundVmessAddressMode.item(i).child(j, 3).text()) try: self.treasureChest.addLevel(user["level"]) except Exception: pass server["users"].append(copy.deepcopy(user)) else: server["users"] = [] outboundVmessJSONFile["vnext"].append(copy.deepcopy(server)) return outboundVmessJSONFile def clearoutboundVmessPanel(self): self.treeViewOutboundVmessAddressMode.setRowCount(0) self.lineEditOutboundVmessAddress.clear() self.lineEditOutboundVmessUUID.clear() self.comboBoxOutboundVmessMethod.setCurrentIndex(0) self.spinBoxOutboundVmessAlterID.setValue(10) self.spinBoxOutboundVmessPort.setValue(443) self.spinBoxOutboundVmessuserLevel.setValue(0) def createUUID(self): return str(uuid.uuid4()) def validateUUID4(self, uuidString): try: uuid.UUID(uuidString, version=4) except ValueError: return False return True def __DebugTest(self): import json print(json.dumps(self.createOutboundVmessJSONFile(), indent=4, sort_keys=False))
class MainWindow(QMainWindow): try: OpenAddonAdder = pyqtSignal() OpenSettingsWindow = pyqtSignal() OutputUpdater = pyqtSignal(str) PromptUpdate = pyqtSignal(list) DownloadStart = pyqtSignal() MessageBox = pyqtSignal(str, str, str) UpdateTreeView = pyqtSignal() AddAddon = pyqtSignal(Addon, QTreeView, QStandardItemModel) # UpdateProgressBarValue = pyqtSignal(int) # UpdateProgressBarMax = pyqtSignal(int) RemoveAddon = pyqtSignal(str) ForceUpdateAddon = pyqtSignal(Addon) LoadingGifSignal = pyqtSignal(bool) SetUpdateCount = pyqtSignal(int, int) except Exception as e: print(e) def __init__(self): super(QMainWindow, self).__init__() self.window = QMainWindow() self.window.ui = Ui_MainWindow() self.window.ui.setupUi(self) self.app = QApplication(sys.argv) self.settings = Settings() self.movie = QMovie(":/app/loading_icon.gif") self.download_worker = Worker(self.execute_download) self.update_worker = Worker(self.execute_check_updates) self.tree_model = self.window.ui.tviewAddons self.model = QStandardItemModel(self.tree_model) self.window.ui.actionClose.triggered.connect(self.close) self.window.ui.actionAbout.triggered.connect(self.open_about_window) self.init_ui() def closeEvent(self, event): if self.settings.config['settings']['prompt_to_close']: prompt = QMessageBox.question(self, 'Confirmation', 'Are you sure you want to quit?', QMessageBox.Yes, QMessageBox.No) if prompt == QMessageBox.Yes: event.accept() else: event.ignore() def init_ui(self): self.window.ui.splitter.setStyleSheet(HANDLE_STYLE) self.OpenAddonAdder.connect(self.add_addon) self.OpenSettingsWindow.connect(self.show_settings_window) self.OutputUpdater.connect(self.insert_output_text) self.PromptUpdate.connect(self.prompt_to_update) self.DownloadStart.connect(self.execute_download) self.MessageBox.connect(self.show_message_box) self.UpdateTreeView.connect(self.update_tree_view) self.AddAddon.connect(self.add_addon_to_tree_view) # self.UpdateProgressBarValue.connect(self.set_progress_bar_value) # self.UpdateProgressBarMax.connect(self.set_progress_bar_max) self.RemoveAddon.connect(self.remove_addon) self.ForceUpdateAddon.connect(self.force_update_addon) self.LoadingGifSignal.connect(self.toggle_loading_gif) self.SetUpdateCount.connect(self.set_update_count_value) self.window.ui.actionAddAddon.triggered.connect( self.OpenAddonAdder.emit) self.window.ui.actionSettings.triggered.connect( self.OpenSettingsWindow.emit) self.window.ui.btnCheckForUpdates.clicked.connect( self.update_worker.start) self.window.ui.btnDownloadUpdates.clicked.connect( self.download_worker.start) self.window.ui.actionUpdateTreeView.triggered.connect( self.UpdateTreeView.emit) # self.UpdateProgressBarMax.emit(10) # self.UpdateProgressBarValue.emit(0) # self.window.ui.progressBar.setVisible(False) # Setting the header to hidden removes the drag bar for column width resizing. # self.window.ui.tviewAddons.setHeaderHidden(True) self.UpdateTreeView.emit() self.settings.check_for_wow_directory(self) self.window.ui.label.setHidden(True) self.window.ui.btnDownloadUpdates.setHidden(True) self.window.ui.tviewAddons.setColumnWidth(0, 300) self.window.ui.lblLoadingMovie.setMovie(self.movie) self.window.ui.lblLoadingMovie.setMaximumHeight(50) self.window.ui.lblLoadingMovie.setScaledContents(True) self.window.ui.lblLoadingMovie.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.window.ui.frameUpdate.setVisible(False) @pyqtSlot() def add_addon(self): addon_window = AddonWindow(self.settings, self) addon_window.exec() @pyqtSlot() def open_about_window(self): about_window = AboutWindow(self) about_window.exec() @pyqtSlot(str) def insert_output_text(self, text): self.window.ui.teditOutput.insertPlainText(text + '\n') self.window.ui.teditOutput.moveCursor(QTextCursor.End) @pyqtSlot() def show_settings_window(self): settings_window = SettingsWindow(self) settings_window.exec() @pyqtSlot(int) def set_progress_bar_value(self, val): self.window.ui.progressBar.setValue(val) @pyqtSlot(bool) def toggle_loading_gif(self, enabled): if enabled: self.window.ui.frameUpdate.setVisible(True) self.window.ui.btnCheckForUpdates.setVisible(False) self.movie.start() else: self.window.ui.frameUpdate.setVisible(False) self.window.ui.btnCheckForUpdates.setVisible(True) self.movie.stop() @pyqtSlot(int, int) def set_update_count_value(self, current, max): self.window.ui.lblCurrentUpdate.setText("( {0} out of {1} )".format( current, max)) @pyqtSlot(int) def set_progress_bar_max(self, val): self.window.ui.progressBar.setMaximum(val) @pyqtSlot(Addon, QTreeView, QStandardItemModel) def add_addon_to_tree_view(self, addon, tree_model, model): tree_model.setModel(model) parent_item = QStandardItem(addon.name) col1 = QStandardItem() url_item = QStandardItem() remove_addon_item = QStandardItem() update_addon_item = QStandardItem() # Create a QLabel to allow following hyperlinks in the table. url_label = QLabel() url_label.setTextFormat(1) url_label.setOpenExternalLinks(True) url_text = "<a href=\"" + addon.url + "\"> " + addon.name + "</a>" url_label.setText(url_text) remove_addon_button = QPushButton() remove_addon_button.setText("Remove {0}".format(addon.name)) remove_addon_button.clicked.connect( lambda: self.RemoveAddon.emit(addon.name)) remove_addon_button.setMaximumSize( 80 + (len(remove_addon_button.text()) * 5), 30) update_addon_button = QPushButton() update_addon_button.setText("Force Update {0}".format(addon.name)) update_addon_button.clicked.connect( lambda: self.ForceUpdateAddon.emit(addon)) update_addon_button.setMaximumSize( 80 + (len(remove_addon_button.text()) * 5), 30) curr_ver_item = QStandardItem(addon.current_version) latest_ver_item = QStandardItem(addon.latest_version) update_date_item = QStandardItem(addon.last_update_date) logging.debug("Parent item: {0}".format(addon.name)) model.appendRow([parent_item, col1]) url_item_identifier = QStandardItem("Addon Link: ") curr_ver_item_identifier = QStandardItem("Current Version: ") latest_ver_item_identifier = QStandardItem("Latest Version: ") remove_addon_item_identifier = QStandardItem("Remove Addon: ") update_date_item_identifier = QStandardItem("Date Updated: ") force_update_identifier = QStandardItem("Update: ") # Append URL, Current Version, and Latest Version to the parent parent_item.appendRow([url_item_identifier, url_item]) # Append URL as a QLabel to allow the hyperlink to be clicked on. self.window.ui.tviewAddons.setIndexWidget(url_item.index(), url_label) parent_item.appendRow([curr_ver_item_identifier, curr_ver_item]) parent_item.appendRow([latest_ver_item_identifier, latest_ver_item]) parent_item.appendRow([update_date_item_identifier, update_date_item]) parent_item.appendRow( [remove_addon_item_identifier, remove_addon_item]) self.window.ui.tviewAddons.setIndexWidget(remove_addon_item.index(), remove_addon_button) parent_item.appendRow([force_update_identifier, update_addon_item]) self.window.ui.tviewAddons.setIndexWidget(update_addon_item.index(), update_addon_button) # self.window.ui.tviewAddons.setColumnWidth(0, self.window.ui.tviewAddons.columnWidth(0) + 50) # Comment out header as I don't like the look at the moment. self.model.setHeaderData(0, 0x01, "Addons") self.model.setHeaderData(1, 0x01, "") @pyqtSlot(str) def remove_addon(self, addon_name): if addon_name not in self.settings.addons['addons']: return message_box = QMessageBox.question( None, "Confirmation", "Are you sure you want to remove {0}?".format(addon_name), QMessageBox.Yes, QMessageBox.No) if message_box == QMessageBox.Yes: del self.settings.addons['addons'][addon_name] # print(self.settings.data.pop(key, None)) self.settings.save_addons() self.settings.load_addons() index = self.window.ui.tviewAddons.selectionModel().selectedRows( )[0] parent = index.parent() self.window.ui.tviewAddons.selectionModel().setCurrentIndex( self.window.ui.tviewAddons.indexAbove(index), QItemSelectionModel.ClearAndSelect) self.model.removeRow(parent.row()) @pyqtSlot(Addon) def force_update_addon(self, addon): if addon.name not in self.settings.addons['addons']: return message_box = QMessageBox.question( None, "Confirmation", "Are you sure you want to force update {0}?".format(addon.name), QMessageBox.Yes, QMessageBox.No) if message_box == QMessageBox.Yes: addon.addon_source = addon.get_website_type() self.settings.files_to_update = [addon] # self.DownloadStart.emit() self.download_worker.start() @pyqtSlot() def update_tree_view(self): tree_data = self.settings.addons['addons'] logging.debug("Tree view data: {0}".format(tree_data)) # No addons are loaded. # TODO: Insert blank row indicating there are no addons. if len(tree_data) == 0: pass if self.model.hasChildren(): self.model.removeRows(0, self.model.rowCount()) for parent_name in tree_data: current_addon = Addon(tree_data[parent_name]['url'], tree_data[parent_name]['name'], tree_data[parent_name]['current_version'], tree_data[parent_name]['latest_version'], tree_data[parent_name]['last_update_date']) self.AddAddon.emit(current_addon, self.tree_model, self.model) self.window.ui.tviewAddons.sortByColumn(0, 0x00) @pyqtSlot(str, str, str) def show_message_box(self, message='', inform='', message_type='warn'): message_box = QMessageBox() if message_type == 'inform': message_box.setIcon(QMessageBox.Information) elif message_type == 'warn': message_box.setIcon(QMessageBox.Warning) else: message_box.setIcon(QMessageBox.Critical) message_box.setStandardButtons(QMessageBox.Ok) message_box.setWindowTitle("Hey Listen!") message_box.setText(message) message_box.setInformativeText(inform) message_box.setWindowIcon(QIcon(":/app/icon.ico")) message_box.exec() def execute_check_updates(self): if not self.settings.check_for_wow_directory(self): return if len(self.settings.addons['addons']) == 0: self.MessageBox.emit( "No AddOns have been specified.", "To add an addon, press 'Addon' -> 'Add Addon' and enter the URL of the addon.", "warn") return self.window.ui.btnCheckForUpdates.setText( "Checking {0} Addons For Updates...".format( len(self.settings.addons['addons']))) self.window.ui.btnCheckForUpdates.setDisabled(True) self.OutputUpdater.emit("Checking for updates...") updater = UpdateChecker(self) self.LoadingGifSignal.emit(True) update_list = updater.check_for_updates() self.LoadingGifSignal.emit(False) self.window.ui.btnCheckForUpdates.setText("Check For Updates") self.window.ui.btnCheckForUpdates.setEnabled(True) # if update_list: self.PromptUpdate.emit(update_list) @pyqtSlot(list) def prompt_to_update(self, update_list): if update_list: self.window.ui.btnDownloadUpdates.setVisible(True) self.OutputUpdater.emit("AddOns out of date:") for addon in update_list: self.OutputUpdater.emit( "\n{0}: \n\tCurrent version: {1} \n\tNew version: {2}\n" .format(addon.name, addon.current_version, addon.latest_version)) message_box = QMessageBox.question( None, "Updates found", "{0} update{1} found. Would you like to download {2} now?". format(len(update_list), "s" if len(update_list) > 1 else "", "them" if len(update_list) > 1 else "it"), QMessageBox.Yes, QMessageBox.No) if message_box == QMessageBox.Yes: self.download_worker.start() else: # message_box = QMessageBox() # message_box.setWindowTitle("Checker") # message_box.setText("All AddOns are up to date!") self.OutputUpdater.emit( "\nAll AddOns are currently up to to date.") # message_box.setStandardButtons(QMessageBox.Ok) # # message_box.exec() @pyqtSlot() def execute_download(self): d = Downloader(self) to_update = self.settings.files_to_update for addon in to_update: print(addon) logging.info("Update list: {0}".format(to_update)) self.LoadingGifSignal.emit(True) current_download_val = 1 download_failed_list = [] self.window.ui.btnDownloadUpdates.setVisible(False) for addon in to_update: self.SetUpdateCount.emit(current_download_val, len(to_update)) if not d.update_addon(addon): download_failed_list.append(addon.name) self.SetUpdateCount.emit(current_download_val, len(to_update)) current_download_val += 1 self.LoadingGifSignal.emit(False) # If downloads failed, tell the user. if download_failed_list: update_text = "The following AddOns failed to download: {0}".format( download_failed_list) self.MessageBox.emit("Status", update_text, "warn") else: update_text = "All AddOns were downloaded and extracted successfully to AddOns folder." self.MessageBox.emit("Complete", update_text, "inform") self.OutputUpdater.emit(update_text)