class MainWindow(QMainWindow): errorSignal = pyqtSignal(str, str) hintSignal = pyqtSignal(str, str) updateProgressSignal = pyqtSignal(str, int, int, str) updateProgressPrintSignal = pyqtSignal(str) showSerialComboboxSignal = pyqtSignal() downloadResultSignal = pyqtSignal(bool, str) DataPath = "./" app = None firmware_start_bytes = [b'\x21\xa8', b'\xef\xbe', b'\xad\xde'] def __init__(self, app): super().__init__() self.app = app self.programStartGetSavedParameters() self.initVar() self.initWindow() self.initEvent() self.updateFrameParams() def __del__(self): pass def initVar(self): self.burning = False self.isDetectSerialPort = False self.DataPath = parameters.dataPath self.kflash = KFlash(print_callback=self.kflash_py_printCallback) self.saveKfpkDir = "" self.packing = False self.zipTempFiles = [] self.fileSelectWidgets = [] def setWindowSize(self, w=520, h=550): self.resize(w, h) def setFileSelectItemLayout(self, item, isKfpkg): if isKfpkg: item[4].hide() item[2].setStretch(0, 1) item[2].setStretch(1, 12) item[2].setStretch(3, 4) item[2].setStretch(4, 1) else: item[4].show() item[2].setStretch(0, 1) item[2].setStretch(1, 8) item[2].setStretch(2, 4) item[2].setStretch(3, 4) item[2].setStretch(4, 1) def addFileSelectionItem(self): enableCheckbox = QCheckBox() filePathWidget = QLineEdit() fileBurnAddrWidget = QLineEdit("0x00000") openFileButton = QPushButton(tr("OpenFile")) removeButton = QPushButton() removeButton.setProperty("class", "remove_file_selection") oneFilePathWidget = QWidget() oneFilePathWidgetLayout = QHBoxLayout() oneFilePathWidget.setLayout(oneFilePathWidgetLayout) oneFilePathWidgetLayout.addWidget(enableCheckbox) oneFilePathWidgetLayout.addWidget(filePathWidget) oneFilePathWidgetLayout.addWidget(fileBurnAddrWidget) oneFilePathWidgetLayout.addWidget(openFileButton) oneFilePathWidgetLayout.addWidget(removeButton) filesItemLen = len(self.fileSelectWidgets) if filesItemLen != 0 and self.fileSelectWidgets[filesItemLen - 1][4].isHidden(): fileBurnAddrWidget.hide() else: fileBurnAddrWidget.show() if filesItemLen == 0: removeButton.hide() elif filesItemLen == 1: self.fileSelectWidgets[0][7].show() # 0 1 2 3 4 5 6 7 8 item = [ "kfpkg", oneFilePathWidget, oneFilePathWidgetLayout, filePathWidget, fileBurnAddrWidget, openFileButton, False, removeButton, enableCheckbox ] # for "bin": ["bin", oneFilePathWidget, oneFilePathWidgetLayout, filePathWidget, fileBurnAddrWidget, openFileButton, isFirmware, removeButton, enableCheckbox] self.fileSelectWidgets.append(item) self.setFileSelectItemLayout(item, True) openFileButton.clicked.connect(lambda: self.selectFile(item)) removeButton.clicked.connect( lambda: self.removeFileSelectionItem(item)) self.fileSelectLayout.addWidget(oneFilePathWidget) return item def removeFileSelectionItem(self, item): if self.packing: self.hintSignal.emit(tr("Busy"), tr("Please wait, packing ...")) return if len(self.fileSelectWidgets) <= 1: return item[5].clicked.disconnect() item[7].clicked.disconnect() item[1].setParent(None) self.fileSelectWidgets.remove(item) self.downloadWidget.resize(self.downloadWidget.width(), 58) self.setWindowSize(self.width()) def initWindow(self): QToolTip.setFont(QFont('SansSerif', 10)) # main layout self.frameWidget = QWidget() mainWidget = QSplitter(Qt.Horizontal) self.frameLayout = QVBoxLayout() self.settingWidget = QWidget() settingLayout = QVBoxLayout() self.settingWidget.setProperty("class", "settingWidget") mainLayout = QVBoxLayout() self.settingWidget.setLayout(settingLayout) mainLayout.addWidget(self.settingWidget) mainLayout.setStretch(0, 2) menuLayout = QHBoxLayout() self.progressHint = QLabel() self.progressHint.hide() self.progressbarRootWidget = QWidget() progressbarLayout = QVBoxLayout() self.progressbarRootWidget.setProperty("class", "progressbarWidget") self.progressbarRootWidget.setLayout(progressbarLayout) self.downloadWidget = QWidget() downloadLayout = QVBoxLayout() self.downloadWidget.setProperty("class", "downloadWidget") self.downloadWidget.setLayout(downloadLayout) mainWidget.setLayout(mainLayout) # menu # ----- # settings and others # ----- # progress bar # ----- # download button # ----- # status bar self.frameLayout.addLayout(menuLayout) self.frameLayout.addWidget(mainWidget) self.frameLayout.addWidget(self.progressHint) self.frameLayout.addWidget(self.progressbarRootWidget) self.frameLayout.addWidget(self.downloadWidget) self.frameWidget.setLayout(self.frameLayout) self.setCentralWidget(self.frameWidget) self.setFrameStrentch(1) # option layout self.langButton = QPushButton() self.skinButton = QPushButton() self.aboutButton = QPushButton() self.langButton.setProperty("class", "menuItemLang") self.skinButton.setProperty("class", "menuItem2") self.aboutButton.setProperty("class", "menuItem3") self.langButton.setObjectName("menuItem") self.skinButton.setObjectName("menuItem") self.aboutButton.setObjectName("menuItem") menuLayout.addWidget(self.langButton) menuLayout.addWidget(self.skinButton) menuLayout.addWidget(self.aboutButton) menuLayout.addStretch(0) # widgets file select self.fileSelectGroupBox = QGroupBox(tr("SelectFile")) # container settingLayout.addWidget(self.fileSelectGroupBox) self.fileSelectContainerLayout = QVBoxLayout() self.fileSelectGroupBox.setLayout(self.fileSelectContainerLayout) # file selection self.fileSelecWidget = QWidget() self.fileSelectLayout = QVBoxLayout() self.fileSelecWidget.setLayout(self.fileSelectLayout) self.fileSelectContainerLayout.addWidget(self.fileSelecWidget) # add file selection item self.addFileSelectionItem() # add fileselection functions mergeBinWidget = QWidget() mergeBinWidgetLayout = QHBoxLayout() mergeBinWidget.setLayout(mergeBinWidgetLayout) self.addFileButton = QPushButton(tr("Add File")) self.packFilesButton = QPushButton(tr("Pack to kfpkg")) self.mergeBinButton = QPushButton(tr("Merge to .bin")) mergeBinWidgetLayout.addWidget(self.addFileButton) mergeBinWidgetLayout.addWidget(self.packFilesButton) mergeBinWidgetLayout.addWidget(self.mergeBinButton) self.fileSelectContainerLayout.addWidget(mergeBinWidget) # widgets board select boardSettingsGroupBox = QGroupBox(tr("BoardSettings")) settingLayout.addWidget(boardSettingsGroupBox) boardSettingsLayout = QGridLayout() boardSettingsGroupBox.setLayout(boardSettingsLayout) self.boardLabel = QLabel(tr("Board")) self.boardCombobox = ComboBox() self.boardCombobox.addItem(parameters.SipeedMaixDock) self.boardCombobox.addItem(parameters.SipeedMaixBit) self.boardCombobox.addItem(parameters.SipeedMaixBitMic) self.boardCombobox.addItem(parameters.SipeedMaixduino) self.boardCombobox.addItem(parameters.SipeedMaixGo) self.boardCombobox.addItem(parameters.SipeedMaixGoD) self.boardCombobox.addItem(parameters.M5StickV) self.boardCombobox.addItem(parameters.KendryteKd233) self.boardCombobox.addItem(parameters.kendryteTrainer) self.boardCombobox.addItem(parameters.Auto) self.burnPositionLabel = QLabel(tr("BurnTo")) self.burnPositionCombobox = ComboBox() self.burnPositionCombobox.addItem(tr("Flash")) self.burnPositionCombobox.addItem(tr("SRAM")) boardSettingsLayout.addWidget(self.boardLabel, 0, 0) boardSettingsLayout.addWidget(self.boardCombobox, 0, 1) boardSettingsLayout.addWidget(self.burnPositionLabel, 1, 0) boardSettingsLayout.addWidget(self.burnPositionCombobox, 1, 1) # widgets serial settings serialSettingsGroupBox = QGroupBox(tr("SerialSettings")) serialSettingsLayout = QGridLayout() serialPortLabek = QLabel(tr("SerialPort")) serailBaudrateLabel = QLabel(tr("SerialBaudrate")) slowModeLabel = QLabel(tr("Speed mode")) self.serialPortCombobox = ComboBox() self.serailBaudrateCombobox = ComboBox() self.serailBaudrateCombobox.addItem("115200") self.serailBaudrateCombobox.addItem("921600") self.serailBaudrateCombobox.addItem("1500000") self.serailBaudrateCombobox.addItem("2000000") self.serailBaudrateCombobox.addItem("3500000") self.serailBaudrateCombobox.addItem("4000000") self.serailBaudrateCombobox.addItem("4500000") self.serailBaudrateCombobox.setCurrentIndex(1) self.serailBaudrateCombobox.setEditable(True) self.slowModeCombobox = ComboBox() self.slowModeCombobox.addItem(tr("Slow mode")) self.slowModeCombobox.addItem(tr("Fast mode")) slowModeLabel.setToolTip(tr("slow mode tips")) self.slowModeCombobox.setToolTip(tr("slow mode tips")) serialSettingsLayout.addWidget(serialPortLabek, 0, 0) serialSettingsLayout.addWidget(serailBaudrateLabel, 1, 0) serialSettingsLayout.addWidget(slowModeLabel, 2, 0) serialSettingsLayout.addWidget(self.serialPortCombobox, 0, 1) serialSettingsLayout.addWidget(self.serailBaudrateCombobox, 1, 1) serialSettingsLayout.addWidget(self.slowModeCombobox, 2, 1) serialSettingsGroupBox.setLayout(serialSettingsLayout) settingLayout.addWidget(serialSettingsGroupBox) # set stretch settingLayout.setStretch(0, 1) settingLayout.setStretch(1, 1) settingLayout.setStretch(2, 2) # widgets progress bar self.progressbar = QProgressBar(self.progressbarRootWidget) self.progressbar.setValue(0) self.progressbarRootWidget.hide() # widgets download area self.downloadButton = QPushButton(tr("Download")) downloadLayout.addWidget(self.downloadButton) # main window self.statusBarStauts = QLabel() self.statusBarStauts.setMinimumWidth(80) self.statusBarStauts.setText("<font color=%s>%s</font>" % ("#1aac2d", tr("DownloadHint"))) self.statusBar().addWidget(self.statusBarStauts) self.setWindowSize() self.MoveToCenter() self.setWindowTitle(parameters.appName + " V" + str(helpAbout.versionMajor) + "." + str(helpAbout.versionMinor)) icon = QIcon() print("icon path:" + self.DataPath + "/" + parameters.appIcon) icon.addPixmap(QPixmap(self.DataPath + "/" + parameters.appIcon), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) if sys.platform == "win32": ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( parameters.appName) self.show() self.progressbar.setGeometry(10, 0, self.downloadWidget.width() - 25, 40) print("config file path:", parameters.configFilePath) def initEvent(self): self.serialPortCombobox.clicked.connect(self.portComboboxClicked) self.errorSignal.connect(self.errorHint) self.hintSignal.connect(self.hint) self.downloadResultSignal.connect(self.downloadResult) self.showSerialComboboxSignal.connect(self.showCombobox) self.updateProgressSignal.connect(self.updateProgress) self.updateProgressPrintSignal.connect(self.updateProgressPrint) self.langButton.clicked.connect(self.langChange) self.skinButton.clicked.connect(self.skinChange) self.aboutButton.clicked.connect(self.showAbout) self.downloadButton.clicked.connect(self.download) self.addFileButton.clicked.connect( lambda: self.fileSelectLayout.addWidget(self.addFileSelectionItem( )[1])) self.packFilesButton.clicked.connect(self.packFiles) self.mergeBinButton.clicked.connect(self.mergeBin) self.myObject = MyClass(self) slotLambda = lambda: self.indexChanged_lambda(self.myObject) self.serialPortCombobox.currentIndexChanged.connect(slotLambda) def setFrameStrentch(self, mode): if mode == 0: self.frameLayout.setStretch(0, 1) self.frameLayout.setStretch(1, 3) self.frameLayout.setStretch(2, 3) self.frameLayout.setStretch(3, 1) self.frameLayout.setStretch(4, 1) self.frameLayout.setStretch(5, 1) else: self.frameLayout.setStretch(0, 0) self.frameLayout.setStretch(1, 0) self.frameLayout.setStretch(2, 1) self.frameLayout.setStretch(3, 1) self.frameLayout.setStretch(4, 1) self.frameLayout.setStretch(5, 1) # @QtCore.pyqtSlot(str) def indexChanged_lambda(self, obj): mainObj = obj.arg self.serialPortCombobox.setToolTip( mainObj.serialPortCombobox.currentText()) def portComboboxClicked(self): self.detectSerialPort() def MoveToCenter(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def highlightFirmwarePath(self, item, firmware): if firmware: item[3].setProperty("class", "qLineEditHighlight") item[4].setText("0x00000") else: item[3].setProperty("class", "qLineEditNormal") self.frameWidget.style().unpolish(item[3]) self.frameWidget.style().polish(item[3]) self.frameWidget.update() def fileSelectShow(self, item, name, addr=None, firmware=None, enable=True, loadFirst=False): isKfpkg = False if self.isKfpkg(name): isKfpkg = True if not item: # add item from param if loadFirst: item = self.fileSelectWidgets[0] else: item = self.addFileSelectionItem() if isKfpkg: self.highlightFirmwarePath(item, False) self.setFileSelectItemLayout(item, True) else: item[4].setText("0x%06x" % (addr)) self.setFileSelectItemLayout(item, False) if self.isFileFirmware(name): self.highlightFirmwarePath(item, True) item[6] = True else: self.highlightFirmwarePath(item, False) item[6] = False item[3].setText(name) item[8].setChecked(enable) return if isKfpkg: self.setFileSelectItemLayout(item, True) self.highlightFirmwarePath(item, False) # disable other items for i in self.fileSelectWidgets: i[8].setChecked(False) # only enable this kfpkg item[8].setChecked(True) else: self.setFileSelectItemLayout(item, False) if self.isFileFirmware(name): self.highlightFirmwarePath(item, True) item[4].setText("0x00000") else: self.highlightFirmwarePath(item, False) # disable kfpkg file for i in self.fileSelectWidgets: if self.isKfpkg(i[3].text()): i[8].setChecked(False) # enable this bin file item[8].setChecked(True) item[3].setText(name) # return: ("bin", [(file path, burn addr, add prefix, enable),...]) # or ("kfpkg", file path) # or (None, msg) def getBurnFilesInfo(self): files = [] fileType = "" for item in self.fileSelectWidgets: path = item[3].text().strip() enable = item[8].isChecked() try: addr = int(item[4].text(), 16) except Exception: addr = 0 if not enable: continue if path == "" or not os.path.exists(path): return (None, tr("Line {}: ").format( self.fileSelectWidgets.index(item) + 1) + tr("File path error") + ":" + path) if self.isKfpkg(path): if fileType == "bin": return ( None, tr("Can not select kfpkg and bin files at the time")) fileType = "kfpkg" if len(files) != 0: return (None, tr("Only support one kfpkg file")) files = path else: if fileType == "kfpkg": return ( None, tr("Can not select kfpkg and bin files at the time")) fileType = "bin" files.append((path, addr, item[6], enable)) return (fileType, files) class KFPKG(): def __init__(self): self.fileInfo = {"version": "0.1.0", "files": []} self.filePath = {} self.burnAddr = [] def addFile(self, addr, path, prefix=False): if not os.path.exists(path): raise ValueError(tr("FilePathError")) if addr in self.burnAddr: raise ValueError( tr("Burn dddr duplicate") + ":0x%06x" % (addr)) f = {} f_name = os.path.split(path)[1] f["address"] = addr f["bin"] = f_name f["sha256Prefix"] = prefix self.fileInfo["files"].append(f) self.filePath[f_name] = path self.burnAddr.append(addr) def listDumps(self): kfpkg_json = json.dumps(self.fileInfo, indent=4) return kfpkg_json def listDump(self, path): with open(path, "w") as f: f.write(json.dumps(self.fileInfo, indent=4)) def listLoads(self, kfpkgJson): self.fileInfo = json.loads(kfpkgJson) def listLload(self, path): with open(path) as f: self.fileInfo = json.load(f) def save(self, path): listName = os.path.join(tempfile.gettempdir(), "kflash_gui_tmp_list.json") self.listDump(listName) try: with zipfile.ZipFile(path, "w") as zip: for name, path in self.filePath.items(): zip.write(path, arcname=name, compress_type=zipfile.ZIP_DEFLATED) zip.write(listName, arcname="flash-list.json", compress_type=zipfile.ZIP_DEFLATED) zip.close() except Exception as e: os.remove(listName) raise e os.remove(listName) def checkFilesAddrValid(self, fileType, files): if fileType == "bin": files.sort(key=lambda file: file[1]) startAddr = -1 fileSize = 0 fileShortLast = "" count = 0 for file, addr, firmware, enable in files: if not enable: continue fileShort = ".../" + "/".join(file.split("/")[-2:]) if startAddr + fileSize > addr: return ( False, tr("File address error") + ": {} {} 0x{:X}, {} {} {} [0x{:X},0x{:X}]".format( fileShort, tr("start from"), addr, tr("but file"), fileShortLast, tr("address range is"), startAddr, startAddr + fileSize)) fileSize = os.path.getsize(file) startAddr = addr fileShortLast = fileShort count += 1 if count == 0: return (False, tr("No file selected")) return (True, "") def packFiles(self): if self.packing: self.hintSignal.emit(tr("Busy"), tr("Please wait, packing ...")) return self.packing = True fileType, files = self.getBurnFilesInfo() if not fileType: self.errorSignal.emit(tr("Error"), files) self.packing = False return if fileType == "kfpkg": self.errorSignal.emit(tr("Error"), tr("Can not pack kfpkg")) self.packing = False return ok, msg = self.checkFilesAddrValid(fileType, files) if not ok: self.errorSignal.emit(tr("Error"), msg) self.packing = False return # select saving path if not os.path.exists(self.saveKfpkDir): self.saveKfpkDir = os.getcwd() fileName_choose, filetype = QFileDialog.getSaveFileName( self, tr("Save File"), self.saveKfpkDir, "k210 packages (*.kfpkg)") if fileName_choose == "": # self.errorSignal.emit(tr("Error"), tr("File path error")) self.packing = False return if not self.isKfpkg(fileName_choose): fileName_choose += ".kfpkg" self.saveKfpkDir = os.path.split(fileName_choose)[0] # pack and save t = threading.Thread(target=self.packFileProccess, args=( files, fileName_choose, )) t.setDaemon(True) t.start() def packFileProccess(self, files, fileSaveName): # generate flash-list.json kfpkg = self.KFPKG() try: for path, addr, prefix, enable in files: if enable: kfpkg.addFile(addr, path, prefix) except Exception as e: self.errorSignal.emit(tr("Error"), tr("Pack kfpkg fail") + ":" + str(e)) self.packing = False return # write kfpkg file try: kfpkg.save(fileSaveName) except Exception as e: self.errorSignal.emit(tr("Error"), tr("Pack kfpkg fail") + ":" + str(e)) self.packing = False return self.hintSignal.emit(tr("Success"), tr("Save kfpkg success")) self.packing = False def getBurnFilesInfoFromKfpkg(self, kfpkg): tempDir = tempfile.gettempdir() listFileName = "flash-list.json" try: zip = zipfile.ZipFile(kfpkg, mode="r") zip.extract(listFileName, tempDir) with open(tempDir + "/" + listFileName) as f: info = json.load(f) filesInfo = {} for fileInfo in info["files"]: filesInfo[fileInfo["bin"]] = [ fileInfo["address"], fileInfo["sha256Prefix"] ] print(filesInfo, zip.namelist()) binFiles = zip.namelist() binFiles.remove(listFileName) for file in binFiles: zip.extract(file, tempDir) self.zipTempFiles.append( (tempDir + "/" + file, filesInfo[file][0], filesInfo[file][1], True)) zip.close() except Exception as e: return (None, str(e)) return (self.zipTempFiles, "") def cleanKfpkgTempFiles(self): tempDir = tempfile.gettempdir() try: for file in self.zipTempFiles: os.remove(file[0]) except Exception: pass self.zipTempFiles = [] def mergeBin(self): if self.packing: self.hintSignal.emit(tr("Busy"), tr("Please wait, packing ...")) return self.packing = True fileType, files = self.getBurnFilesInfo() if not fileType: self.errorSignal.emit(tr("Error"), files) self.cleanKfpkgTempFiles() self.packing = False return if fileType == "kfpkg": files, msg = self.getBurnFilesInfoFromKfpkg(files) fileType = "bin" if not files: self.errorSignal.emit(tr("Error"), msg) self.cleanKfpkgTempFiles() self.packing = False return ok, msg = self.checkFilesAddrValid(fileType, files) if not ok: self.errorSignal.emit(tr("Error"), msg) self.packing = False self.cleanKfpkgTempFiles() return # select saving path if not os.path.exists(self.saveKfpkDir): self.saveKfpkDir = os.getcwd() fileName_choose, filetype = QFileDialog.getSaveFileName( self, tr("Save File"), self.saveKfpkDir, "Binary file (*.bin)") if fileName_choose == "": # self.errorSignal.emit(tr("Error"), tr("File path error")) self.packing = False self.cleanKfpkgTempFiles() return if not fileName_choose.endswith(".bin"): fileName_choose += ".bin" self.saveKfpkDir = os.path.split(fileName_choose)[0] # pack and save t = threading.Thread(target=self.mergeBinProccess, args=( files, fileName_choose, )) t.setDaemon(True) t.start() def mergeBinProccess(self, files, fileSaveName): self.updateProgressPrintSignal.emit(tr("Merging, please wait ...")) files.sort(key=lambda file: file[1]) bin = b'' aesFlag = b'\x00' startAddrLast = files[0][1] fileSizeLast = 0 if files[0][2]: # firmware name = files[0][0] size = os.path.getsize(name) f = open(name, "rb") firmware = f.read() f.close() bin += aesFlag # add aes key flag bin += struct.pack('I', size) # add firmware length bin += firmware # add firmware content sha256Hash = hashlib.sha256(bin).digest() bin += sha256Hash # add parity startAddrLast = 0 fileSizeLast = len(bin) files.remove(files[0]) for file, addr, firmware, enable in files: if not enable: continue fillLen = addr - (startAddrLast + fileSizeLast) if fillLen > 0: # fill 0xFF fill = bytearray([0xFF for i in range(fillLen)]) bin += fill with open(file, "rb") as f: # add bin file content bin += f.read() startAddrLast = addr fileSizeLast = os.path.getsize(file) with open(fileSaveName, "wb") as f: f.write(bin) self.updateProgressPrintSignal.emit(tr("Save merged bin file success")) self.hintSignal.emit(tr("Success"), tr("Save merged bin file success")) self.packing = False self.cleanKfpkgTempFiles() def selectFile(self, item): if self.packing: self.hintSignal.emit(tr("Busy"), tr("Please wait, packing ...")) return index = self.fileSelectWidgets.index(item) oldPath = item[3].text() if oldPath == "" and index > 0: oldPath = self.fileSelectWidgets[index - 1][3].text() if oldPath == "": oldPath = os.getcwd() fileName_choose, filetype = QFileDialog.getOpenFileName( self, tr("SelectFile"), oldPath, "All Files (*);;bin Files (*.bin);;k210 packages (*.kfpkg);;kmodel (*.kmodel);;encrypted kmodle(*.smodel)" ) # 设置文件扩展名过滤,用双分号间隔 if fileName_choose == "": return if not self.isFileValid(fileName_choose): self.errorSignal.emit(tr("Error"), tr("File path error")) return self.fileSelectShow(item, fileName_choose) def errorHint(self, title, str): QMessageBox.critical(self, title, str) def hint(self, title, str): QMessageBox.information(self, title, str) def findSerialPort(self): self.port_list = list(serial.tools.list_ports.comports()) return self.port_list def portChanged(self): self.serialPortCombobox.setCurrentIndex(0) self.serialPortCombobox.setToolTip(str(self.portList[0])) def detectSerialPort(self): if not self.isDetectSerialPort: self.isDetectSerialPort = True t = threading.Thread(target=self.detectSerialPortProcess) t.setDaemon(True) t.start() def showCombobox(self): self.serialPortCombobox.showPopup() def isKfpkg(self, name): if name.endswith(".kfpkg"): return True return False def isFileFirmware(self, name): isFirmware = False if name.endswith(".bin"): f = open(name, "rb") start_bytes = f.read(6) f.close() for flags in self.firmware_start_bytes: if flags in start_bytes: isFirmware = True break return isFirmware def isFileValid(self, name): if not os.path.exists(name): return False return True def detectSerialPortProcess(self): while (1): portList = self.findSerialPort() if len(portList) > 0: currText = self.serialPortCombobox.currentText() self.serialPortCombobox.clear() for i in portList: showStr = str(i[0]) + " (" + str(i[1]) + ")" self.serialPortCombobox.addItem(showStr) index = self.serialPortCombobox.findText(currText) if index >= 0: self.serialPortCombobox.setCurrentIndex(index) else: self.serialPortCombobox.setCurrentIndex(0) break time.sleep(1) self.showSerialComboboxSignal.emit() self.isDetectSerialPort = False def programExitSaveParameters(self): paramObj = paremeters_save.ParametersToSave() paramObj.board = self.boardCombobox.currentText() paramObj.burnPosition = self.burnPositionCombobox.currentText() paramObj.baudRate = self.serailBaudrateCombobox.currentIndex() paramObj.skin = self.param.skin paramObj.language = translation.current_lang for item in self.fileSelectWidgets: path = item[3].text() try: addr = int(item[4].text(), 16) except Exception: addr = 0 fileInfo = (path, addr, item[6], item[8].isChecked()) paramObj.files.append(fileInfo) if self.slowModeCombobox.currentIndex() == 0: paramObj.slowMode = True else: paramObj.slowMode = False paramObj.save(parameters.configFilePath) def programStartGetSavedParameters(self): paramObj = paremeters_save.ParametersToSave() paramObj.load(parameters.configFilePath) translation.setLanguage(paramObj.language) self.param = paramObj def updateFrameParams(self): pathLen = len(self.param.files) if pathLen != 0: if len(self.param.files[0] ) != 4: # [ (path, addr, prefix, enable), ...] return count = 0 for path, addr, firmware, enable in self.param.files: firmware = None if (not firmware) else True if count == 0: self.fileSelectShow(None, path, addr, firmware, enable=enable, loadFirst=True) else: self.fileSelectShow(None, path, addr, firmware, enable=enable, loadFirst=False) count += 1 self.boardCombobox.setCurrentText(self.param.board) self.burnPositionCombobox.setCurrentText(self.param.burnPosition) self.serailBaudrateCombobox.setCurrentIndex(self.param.baudRate) if self.param.slowMode: self.slowModeCombobox.setCurrentIndex(0) else: self.slowModeCombobox.setCurrentIndex(1) def closeEvent(self, event): try: self.programExitSaveParameters() finally: event.accept() def langChange(self): if self.param.language == translation.language_en: translation.setLanguage(translation.language_zh) lang = tr("Chinese language") else: translation.setLanguage(translation.language_en) lang = tr("English language") self.hint( tr("Hint"), tr("Language Changed to ") + lang + "\n" + tr("Reboot to take effect")) self.frameWidget.style().unpolish(self.downloadButton) self.frameWidget.style().polish(self.downloadButton) self.frameWidget.update() def skinChange(self): if self.param.skin == 1: # light file = open(self.DataPath + '/assets/qss/style-dark.qss', "r") self.param.skin = 2 else: # elif self.param.skin == 2: # dark file = open(self.DataPath + '/assets/qss/style.qss', "r") self.param.skin = 1 self.app.setStyleSheet(file.read().replace("$DataPath", self.DataPath)) file.close() def showAbout(self): QMessageBox.information( self, tr("About"), "<h1 style='color:#f75a5a';margin=10px;>" + parameters.appName + '</h1><br><b style="color:#08c7a1;margin = 5px;">V' + str(helpAbout.versionMajor) + "." + str(helpAbout.versionMinor) + "." + str(helpAbout.versionDev) + "</b><br><br>" + helpAbout.date + "<br><br>" + tr("help str") + "<br><br>" + helpAbout.strAbout()) def autoUpdateDetect(self): auto = autoUpdate.AutoUpdate() if auto.detectNewVersion(): self.hintSignal.emit( tr("Upgrade"), tr("Upgrade available, please download new release in release page" )) auto.OpenBrowser() def openDevManagement(self): os.system('start devmgmt.msc') def updateProgress(self, fileTypeStr, current, total, speedStr): currBurnPos = self.burnPositionCombobox.currentText() if currBurnPos == tr("SRAM") or currBurnPos == tr_en("SRAM"): fileTypeStr = tr("ToSRAM") percent = current / float(total) * 100 hint = "<font color=%s>%s %s:</font> <font color=%s> %.2f%%</font> <font color=%s> %s</font>" % ( "#ff7575", tr("Downloading"), fileTypeStr, "#2985ff", percent, "#1aac2d", speedStr) self.progressHint.setText(hint) self.progressbar.setValue(percent) def updateProgressPrint(self, str): self.statusBarStauts.setText(str) def kflash_py_printCallback(self, *args, **kwargs): # end = kwargs.pop('end', "\n") msg = "" for i in args: msg += str(i) msg.replace("\n", " ") self.updateProgressPrintSignal.emit(msg) def progress(self, fileTypeStr, current, total, speedStr): self.updateProgressSignal.emit(fileTypeStr, current, total, speedStr) def download(self): if self.packing: self.hintSignal.emit(tr("Busy"), tr("Please wait, packing ...")) return if self.burning: self.terminateBurn() return fileType, filesInfo = self.getBurnFilesInfo() if not fileType or not filesInfo: self.errorSignal.emit(tr("Error"), tr("File path error")) return self.burning = True # if not self.checkFileName(filename): # self.errorSignal.emit(tr("Error"), tr("FilePathError")) # self.burning = False # return color = False board = "dan" boardText = self.boardCombobox.currentText() if boardText == parameters.SipeedMaixGo: board = "goE" elif boardText == parameters.SipeedMaixGoD: board = "goD" elif boardText == parameters.SipeedMaixduino: board = "maixduino" elif boardText == parameters.SipeedMaixBit: board = "bit" elif boardText == parameters.SipeedMaixBitMic: board = "bit_mic" elif boardText == parameters.KendryteKd233: board = "kd233" elif boardText == parameters.kendryteTrainer: board = "trainer" elif boardText == parameters.M5StickV: board = "goE" elif boardText == parameters.Auto: board = None sram = False if self.burnPositionCombobox.currentText()==tr("SRAM") or \ self.burnPositionCombobox.currentText()==tr_en("SRAM"): sram = True try: baud = int(self.serailBaudrateCombobox.currentText()) except Exception: self.errorSignal.emit(tr("Error"), tr("BaudrateError")) self.burning = False return dev = "" try: dev = self.serialPortCombobox.currentText().split()[0] except Exception: pass if dev == "": self.errorSignal.emit(tr("Error"), tr("PleaseSelectSerialPort")) self.burning = False return slow = self.slowModeCombobox.currentIndex() == 0 # hide setting widgets self.setFrameStrentch(1) self.settingWidget.hide() self.progressbar.setValue(0) self.progressbar.setGeometry(10, 0, self.downloadWidget.width() - 25, 40) self.progressbarRootWidget.show() self.progressHint.show() self.downloadButton.setText(tr("Cancel")) self.downloadButton.setProperty("class", "redbutton") self.downloadButton.style().unpolish(self.downloadButton) self.downloadButton.style().polish(self.downloadButton) self.downloadButton.update() self.statusBarStauts.setText("<font color=%s>%s ...</font>" % ("#1aac2d", tr("Downloading"))) hint = "<font color=%s>%s</font>" % ("#ff0d0d", tr("DownloadStart")) self.progressHint.setText(hint) # download self.burnThread = threading.Thread(target=self.flashBurnProcess, args=(dev, baud, board, sram, fileType, filesInfo, self.progress, color, slow)) self.burnThread.setDaemon(True) self.burnThread.start() def flashBurnProcess(self, dev, baud, board, sram, fileType, files, callback, color, slow): success = True errMsg = "" tmpFile = "" if fileType == "kfpkg": if sram: errMsg = tr("only support bin file when Download to SRAM") success = False else: filename = files else: #generate kfpkg if sram: filename = files[0][0] else: tmpFile = os.path.join(tempfile.gettempdir(), "kflash_gui_tmp.kfpkg") kfpkg = self.KFPKG() try: for path, addr, prefix, enable in files: if enable: kfpkg.addFile(addr, path, prefix) kfpkg.save(tmpFile) filename = os.path.abspath(tmpFile) except Exception as e: try: os.remove(tmpFile) except Exception: print("can not delete temp file:", tmpFile) errMsg = tr("Pack kfpkg fail") + ":" + str(e) success = False if success: try: if board: self.kflash.process(terminal=False, dev=dev, baudrate=baud, board=board, sram=sram, file=filename, callback=callback, noansi=not color, slow_mode=slow) else: self.kflash.process(terminal=False, dev=dev, baudrate=baud, sram=sram, file=filename, callback=callback, noansi=not color, slow_mode=slow) except Exception as e: errMsg = tr2(str(e)) if str(e) != "Burn SRAM OK": success = False if tmpFile != "": try: os.remove(filename) except Exception: print("Can not delete tmp file:", filename) if success: self.downloadResultSignal.emit(True, errMsg) else: self.downloadResultSignal.emit(False, errMsg) self.burning = False def downloadResult(self, success, msg): if success: self.hintSignal.emit(tr("Success"), tr("DownloadSuccess")) self.statusBarStauts.setText("<font color=%s>%s</font>" % ("#1aac2d", tr("DownloadSuccess"))) else: if msg == tr("Cancel"): self.statusBarStauts.setText( "<font color=%s>%s</font>" % ("#ff1d1d", tr("DownloadCanceled"))) else: msg = tr("ErrorSettingHint") + "\n\n" + msg self.errorSignal.emit(tr("Error"), msg) self.statusBarStauts.setText("<font color=%s>%s</font>" % ("#ff1d1d", tr("DownloadFail"))) self.progressHint.setText("") self.downloadButton.setText(tr("Download")) self.downloadButton.setProperty("class", "normalbutton") self.downloadButton.style().unpolish(self.downloadButton) self.downloadButton.style().polish(self.downloadButton) self.downloadButton.update() self.setFrameStrentch(0) self.progressbarRootWidget.hide() self.progressHint.hide() self.settingWidget.show() self.burning = False def terminateBurn(self): hint = "<font color=%s>%s</font>" % ("#ff0d0d", tr("DownloadCanceling")) self.progressHint.setText(hint) self.kflash.kill()
class MainWindow(QMainWindow): errorSignal = pyqtSignal(str, str) hintSignal = pyqtSignal(str, str) updateProgressSignal = pyqtSignal(str, int, int, str) updateProgressPrintSignal = pyqtSignal(str) showSerialComboboxSignal = pyqtSignal() downloadResultSignal = pyqtSignal(bool, str) DataPath = "./" app = None def __init__(self,app): super().__init__() self.app = app self.programStartGetSavedParameters() self.initVar() self.initWindow() self.initEvent() self.updateFrameParams() def __del__(self): pass def initVar(self): self.burning = False self.isDetectSerialPort = False self.DataPath = parameters.dataPath self.kflash = KFlash(print_callback=self.kflash_py_printCallback) self.saveKfpkDir = "" def setWindowSize(self, w=520, h=550): self.resize(w, h) def initWindow(self): QToolTip.setFont(QFont('SansSerif', 10)) # main layout self.frameWidget = QWidget() mainWidget = QSplitter(Qt.Horizontal) self.frameLayout = QVBoxLayout() self.settingWidget = QWidget() settingLayout = QVBoxLayout() self.settingWidget.setProperty("class","settingWidget") mainLayout = QVBoxLayout() self.settingWidget.setLayout(settingLayout) mainLayout.addWidget(self.settingWidget) mainLayout.setStretch(0,2) menuLayout = QHBoxLayout() self.progressHint = QLabel() self.progressHint.hide() self.progressbarRootWidget = QWidget() progressbarLayout = QVBoxLayout() self.progressbarRootWidget.setProperty("class","progressbarWidget") self.progressbarRootWidget.setLayout(progressbarLayout) self.downloadWidget = QWidget() downloadLayout = QVBoxLayout() self.downloadWidget.setProperty("class","downloadWidget") self.downloadWidget.setLayout(downloadLayout) mainWidget.setLayout(mainLayout) # menu # ----- # settings and others # ----- # progress bar # ----- # download button # ----- # status bar self.frameLayout.addLayout(menuLayout) self.frameLayout.addWidget(mainWidget) self.frameLayout.addWidget(self.progressHint) self.frameLayout.addWidget(self.progressbarRootWidget) self.frameLayout.addWidget(self.downloadWidget) self.frameWidget.setLayout(self.frameLayout) self.setCentralWidget(self.frameWidget) self.setFrameStrentch(1) # option layout self.langButton = QPushButton() self.skinButton = QPushButton() self.aboutButton = QPushButton() self.langButton.setProperty("class", "menuItemLang") self.skinButton.setProperty("class", "menuItem2") self.aboutButton.setProperty("class", "menuItem3") self.langButton.setObjectName("menuItem") self.skinButton.setObjectName("menuItem") self.aboutButton.setObjectName("menuItem") menuLayout.addWidget(self.langButton) menuLayout.addWidget(self.skinButton) menuLayout.addWidget(self.aboutButton) menuLayout.addStretch(0) # widgets file select self.fileSelectGroupBox = QGroupBox(tr("SelectFile")) settingLayout.addWidget(self.fileSelectGroupBox) self.fileSelectLayout = QVBoxLayout() self.fileSelectGroupBox.setLayout(self.fileSelectLayout) oneFilePathWidget = QWidget() oneFilePathWidgetLayout = QHBoxLayout() oneFilePathWidget.setLayout(oneFilePathWidgetLayout) filePathWidget = QLineEdit() openFileButton = QPushButton(tr("OpenFile")) oneFilePathWidgetLayout.addWidget(filePathWidget) oneFilePathWidgetLayout.addWidget(openFileButton) oneFilePathWidgetLayout.setStretch(0, 3) oneFilePathWidgetLayout.setStretch(1, 1) self.fileSelectLayout.addWidget(oneFilePathWidget) self.fileSelectWidgets = [["kfpkg", oneFilePathWidget, oneFilePathWidgetLayout, filePathWidget, None, openFileButton]] # for "button": ["button", addoneWidget, addoneWidgetLayout, addFileButton, packFileButton] # for "bin": ["bin", oneFilePathWidget, oneFilePathWidgetLayout, filePathWidget, fileBurnAddrWidget, openFileButton, fileBurnEncCheckbox] # widgets board select boardSettingsGroupBox = QGroupBox(tr("BoardSettings")) settingLayout.addWidget(boardSettingsGroupBox) boardSettingsLayout = QGridLayout() boardSettingsGroupBox.setLayout(boardSettingsLayout) self.boardLabel = QLabel(tr("Board")) self.boardCombobox = ComboBox() self.boardCombobox.addItem(parameters.SipeedMaixDock) self.boardCombobox.addItem(parameters.SipeedMaixBit) self.boardCombobox.addItem(parameters.SipeedMaixBitMic) self.boardCombobox.addItem(parameters.SipeedMaixduino) self.boardCombobox.addItem(parameters.SipeedMaixGo) self.boardCombobox.addItem(parameters.SipeedMaixGoD) self.boardCombobox.addItem(parameters.KendryteKd233) self.boardCombobox.addItem(parameters.kendryteTrainer) self.boardCombobox.addItem(parameters.Auto) self.burnPositionLabel = QLabel(tr("BurnTo")) self.burnPositionCombobox = ComboBox() self.burnPositionCombobox.addItem(tr("Flash")) self.burnPositionCombobox.addItem(tr("SRAM")) boardSettingsLayout.addWidget(self.boardLabel, 0, 0) boardSettingsLayout.addWidget(self.boardCombobox, 0, 1) boardSettingsLayout.addWidget(self.burnPositionLabel, 1, 0) boardSettingsLayout.addWidget(self.burnPositionCombobox, 1, 1) # widgets serial settings serialSettingsGroupBox = QGroupBox(tr("SerialSettings")) serialSettingsLayout = QGridLayout() serialPortLabek = QLabel(tr("SerialPort")) serailBaudrateLabel = QLabel(tr("SerialBaudrate")) slowModeLabel = QLabel(tr("Speed mode")) self.serialPortCombobox = ComboBox() self.serailBaudrateCombobox = ComboBox() self.serailBaudrateCombobox.addItem("115200") self.serailBaudrateCombobox.addItem("921600") self.serailBaudrateCombobox.addItem("1500000") self.serailBaudrateCombobox.addItem("2000000") self.serailBaudrateCombobox.addItem("3500000") self.serailBaudrateCombobox.addItem("4000000") self.serailBaudrateCombobox.addItem("4500000") self.serailBaudrateCombobox.setCurrentIndex(1) self.serailBaudrateCombobox.setEditable(True) self.slowModeCombobox = ComboBox() self.slowModeCombobox.addItem(tr("Slow mode")) self.slowModeCombobox.addItem(tr("Fast mode")) serialSettingsLayout.addWidget(serialPortLabek,0,0) serialSettingsLayout.addWidget(serailBaudrateLabel, 1, 0) serialSettingsLayout.addWidget(slowModeLabel, 2, 0) serialSettingsLayout.addWidget(self.serialPortCombobox, 0, 1) serialSettingsLayout.addWidget(self.serailBaudrateCombobox, 1, 1) serialSettingsLayout.addWidget(self.slowModeCombobox, 2, 1) serialSettingsGroupBox.setLayout(serialSettingsLayout) settingLayout.addWidget(serialSettingsGroupBox) # set stretch settingLayout.setStretch(0,1) settingLayout.setStretch(1,1) settingLayout.setStretch(2,2) # widgets progress bar self.progressbar = QProgressBar(self.progressbarRootWidget) self.progressbar.setValue(0) self.progressbarRootWidget.hide() # widgets download area self.downloadButton = QPushButton(tr("Download")) downloadLayout.addWidget(self.downloadButton) # main window self.statusBarStauts = QLabel() self.statusBarStauts.setMinimumWidth(80) self.statusBarStauts.setText("<font color=%s>%s</font>" %("#1aac2d", tr("DownloadHint"))) self.statusBar().addWidget(self.statusBarStauts) self.setWindowSize() self.MoveToCenter() self.setWindowTitle(parameters.appName+" V"+str(helpAbout.versionMajor)+"."+str(helpAbout.versionMinor)) icon = QIcon() print("icon path:"+self.DataPath+"/"+parameters.appIcon) icon.addPixmap(QPixmap(self.DataPath+"/"+parameters.appIcon), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) if sys.platform == "win32": ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(parameters.appName) self.show() self.progressbar.setGeometry(10, 0, self.downloadWidget.width()-25, 40) print("config file path:", parameters.configFilePath) def initEvent(self): self.serialPortCombobox.clicked.connect(self.portComboboxClicked) self.errorSignal.connect(self.errorHint) self.hintSignal.connect(self.hint) self.downloadResultSignal.connect(self.downloadResult) self.showSerialComboboxSignal.connect(self.showCombobox) self.updateProgressSignal.connect(self.updateProgress) self.updateProgressPrintSignal.connect(self.updateProgressPrint) self.langButton.clicked.connect(self.langChange) self.skinButton.clicked.connect(self.skinChange) self.aboutButton.clicked.connect(self.showAbout) self.downloadButton.clicked.connect(self.download) self.fileSelectWidget_Button(0).clicked.connect(lambda:self.selectFile(self.fileSelectWidget_Path(0))) self.myObject=MyClass(self) slotLambda = lambda: self.indexChanged_lambda(self.myObject) self.serialPortCombobox.currentIndexChanged.connect(slotLambda) def setFrameStrentch(self, mode): if mode == 0: self.frameLayout.setStretch(0,1) self.frameLayout.setStretch(1,3) self.frameLayout.setStretch(2,3) self.frameLayout.setStretch(3,1) self.frameLayout.setStretch(4,1) self.frameLayout.setStretch(5,1) else: self.frameLayout.setStretch(0,0) self.frameLayout.setStretch(1,0) self.frameLayout.setStretch(2,1) self.frameLayout.setStretch(3,1) self.frameLayout.setStretch(4,1) self.frameLayout.setStretch(5,1) def fileSelectWidget_Type(self, index): return self.fileSelectWidgets[index][0] def fileSelectWidget_Widget(self, index): return self.fileSelectWidgets[index][1] def fileSelectWidget_Layout(self, index): return self.fileSelectWidgets[index][2] def fileSelectWidget_Path(self, index): return self.fileSelectWidgets[index][3] def fileSelectWidget_Addr(self, index): return self.fileSelectWidgets[index][4] def fileSelectWidget_Button(self, index): return self.fileSelectWidgets[index][5] def fileSelectWidget_Prefix(self, index): return self.fileSelectWidgets[index][6] def fileSelectWidget_Close(self, index): return self.fileSelectWidgets[index][7] # @QtCore.pyqtSlot(str) def indexChanged_lambda(self, obj): mainObj = obj.arg self.serialPortCombobox.setToolTip(mainObj.serialPortCombobox.currentText()) def portComboboxClicked(self): self.detectSerialPort() def MoveToCenter(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def removeFileSelection(self, button): index = -1 for i in range(len(self.fileSelectWidgets)): if len(self.fileSelectWidgets[i]) >= 8: if self.fileSelectWidget_Close(i) == button: index = i print(index) if index == -1: return if len(self.fileSelectWidgets) > 2: self.fileSelectWidget_Button(index).clicked.disconnect() self.fileSelectWidget_Close(index).clicked.disconnect() self.fileSelectWidget_Widget(index).setParent(None) self.fileSelectWidgets.remove(self.fileSelectWidgets[index]) if len(self.fileSelectWidgets) == 2: self.fileSelectWidget_Close(0).clicked.disconnect() self.fileSelectWidget_Close(0).setParent(None) self.fileSelectWidgets[0].remove(self.fileSelectWidget_Close(0)) self.downloadWidget.resize(self.downloadWidget.width(), 58) self.setWindowSize(self.width()) def addAddFileWidget(self): if len(self.fileSelectWidgets) == 2: removeButton0 = QPushButton() removeButton0.setProperty("class", "remove_file_selection") self.fileSelectWidgets[0][2].addWidget(removeButton0) self.fileSelectWidgets[0].append(removeButton0) removeButton0.clicked.connect(lambda:self.removeFileSelection(removeButton0)) oneFilePathWidget = QWidget() oneFilePathWidgetLayout = QHBoxLayout() oneFilePathWidget.setLayout(oneFilePathWidgetLayout) filePathWidget = QLineEdit() fileBurnAddrWidget = QLineEdit("0x00000") fileBurnEncCheckbox = QCheckBox(tr("Prefix")) openFileButton = QPushButton(tr("OpenFile")) removeButton = QPushButton() removeButton.setProperty("class", "remove_file_selection") oneFilePathWidgetLayout.addWidget(filePathWidget) oneFilePathWidgetLayout.addWidget(fileBurnAddrWidget) oneFilePathWidgetLayout.addWidget(fileBurnEncCheckbox) oneFilePathWidgetLayout.addWidget(openFileButton) oneFilePathWidgetLayout.addWidget(removeButton) oneFilePathWidgetLayout.setStretch(0, 4) oneFilePathWidgetLayout.setStretch(1, 2) oneFilePathWidgetLayout.setStretch(2, 1) oneFilePathWidgetLayout.setStretch(3, 2) # oneFilePathWidgetLayout.setStretch(4, 1) index = len(self.fileSelectWidgets)-1 self.fileSelectWidgets.insert(index, ["bin", oneFilePathWidget, oneFilePathWidgetLayout, filePathWidget, fileBurnAddrWidget, openFileButton, fileBurnEncCheckbox, removeButton]) self.fileSelectLayout.insertWidget(index, oneFilePathWidget) openFileButton.clicked.connect(lambda:self.selectFile(filePathWidget)) removeButton.clicked.connect(lambda:self.removeFileSelection(removeButton)) def fileSelectShowKfpkg(self, index, name): if index==0 and self.fileSelectWidget_Type(0) == "kfpkg": #only one kgpkg before self.fileSelectWidget_Path(index).setText(name) else:# have bin file before, remove all and add one for kfpkg for i in range(len(self.fileSelectWidgets)): if self.fileSelectWidget_Type(i)=="button": self.fileSelectWidgets[i][3].clicked.disconnect() self.fileSelectWidgets[i][4].clicked.disconnect() else: self.fileSelectWidget_Button(i).clicked.disconnect() # self.fileSelectLayout.removeWidget(self.fileSelectWidget_Widget(i)) self.fileSelectWidget_Widget(i).setParent(None) self.fileSelectWidgets.clear() oneFilePathWidget = QWidget() oneFilePathWidgetLayout = QHBoxLayout() oneFilePathWidget.setLayout(oneFilePathWidgetLayout) filePathWidget = QLineEdit() openFileButton = QPushButton(tr("OpenFile")) oneFilePathWidgetLayout.addWidget(filePathWidget) oneFilePathWidgetLayout.addWidget(openFileButton) oneFilePathWidgetLayout.setStretch(0, 3) oneFilePathWidgetLayout.setStretch(1, 1) self.fileSelectLayout.addWidget(oneFilePathWidget) self.fileSelectWidgets.append(["kfpkg", oneFilePathWidget, oneFilePathWidgetLayout, filePathWidget, None, openFileButton]) openFileButton.clicked.connect(lambda:self.selectFile(filePathWidget)) filePathWidget.setText(name) # TODO: resize window def fileSelectShowBin(self, index, name, addr=None, prefix=None, prefixAuto=False, closeButton=False ): if index==0 and self.fileSelectWidget_Type(0) == "kfpkg": #only one kgpkg before self.fileSelectWidget_Button(index).clicked.disconnect() # self.fileSelectLayout.removeWidget(self.fileSelectWidget_Widget(index)) self.fileSelectWidget_Widget(index).setParent(None) self.fileSelectWidgets.clear() oneFilePathWidget = QWidget() oneFilePathWidgetLayout = QHBoxLayout() oneFilePathWidget.setLayout(oneFilePathWidgetLayout) filePathWidget = QLineEdit() fileBurnAddrWidget = QLineEdit("0x00000") fileBurnEncCheckbox = QCheckBox(tr("Prefix")) openFileButton = QPushButton(tr("OpenFile")) if closeButton: removeButton = QPushButton() removeButton.setProperty("class", "remove_file_selection") oneFilePathWidgetLayout.addWidget(filePathWidget) oneFilePathWidgetLayout.addWidget(fileBurnAddrWidget) oneFilePathWidgetLayout.addWidget(fileBurnEncCheckbox) oneFilePathWidgetLayout.addWidget(openFileButton) if closeButton: oneFilePathWidgetLayout.addWidget(removeButton) oneFilePathWidgetLayout.setStretch(0, 4) oneFilePathWidgetLayout.setStretch(1, 2) oneFilePathWidgetLayout.setStretch(2, 1) oneFilePathWidgetLayout.setStretch(3, 2) # oneFilePathWidgetLayout.setStretch(4, 1) self.fileSelectLayout.addWidget(oneFilePathWidget) openFileButton.clicked.connect(lambda:self.selectFile(filePathWidget)) if closeButton: self.fileSelectWidgets.append(["bin", oneFilePathWidget, oneFilePathWidgetLayout, filePathWidget, fileBurnAddrWidget, openFileButton, fileBurnEncCheckbox, removeButton]) removeButton.clicked.connect(lambda:self.removeFileSelection(removeButton)) print(removeButton) else: self.fileSelectWidgets.append(["bin", oneFilePathWidget, oneFilePathWidgetLayout, filePathWidget, fileBurnAddrWidget, openFileButton, fileBurnEncCheckbox]) # add ADD button addoneWidget = QWidget() addoneWidgetLayout = QHBoxLayout() addoneWidget.setLayout(addoneWidgetLayout) addFileButton = QPushButton(tr("Add File")) packFileButton = QPushButton(tr("Pack to kfpkg")) addoneWidgetLayout.addWidget(addFileButton) addoneWidgetLayout.addWidget(packFileButton) self.fileSelectLayout.addWidget(addoneWidget) self.fileSelectWidgets.append(["button", addoneWidget, addoneWidgetLayout, addFileButton, packFileButton]) addFileButton.clicked.connect(self.addAddFileWidget) packFileButton.clicked.connect(self.packFile) self.fileSelectWidget_Path(index).setText(name) if prefixAuto: if name.endswith(".bin"): self.fileSelectWidget_Prefix(index).setChecked(True) else: self.fileSelectWidget_Prefix(index).setChecked(False) elif prefix: self.fileSelectWidget_Prefix(index).setChecked(True) if addr: self.fileSelectWidget_Addr(index).setText("0x%06x" %(addr)) # return: ("kfpkg", [(file path, burn addr, add prefix),...]) # or ("bin", file path) # or (None, None) def getBurnFilesInfo(self): files = [] if self.fileSelectWidgets[0][0] == "kfpkg": path = self.fileSelectWidget_Path(0).text().strip() if path=="" or not os.path.exists(path): self.errorSignal.emit(tr("Error"), tr("Line {}: ").format(i+1)+tr("File path error")+":"+path) return (None, None) return ("kfpkg", path) for i in range(len(self.fileSelectWidgets)): if self.fileSelectWidgets[i][0] == "bin": path = self.fileSelectWidget_Path(i).text().strip() if path=="": continue if not os.path.exists(path): self.errorSignal.emit(tr("Error"), tr("Line {}: ").format(i+1)+tr("File path error")+":"+path) return (None, None) try: addr = int(self.fileSelectWidgets[i][4].text(), 16) except Exception: self.errorSignal.emit(tr("Error"), tr("Line {}: ").format(i+1)+tr("Address error")+self.fileSelectWidgets[i][4].text()) return (None, None) files.append( (path, addr, self.fileSelectWidgets[i][6].isChecked()) ) return ("bin", files) class KFPKG(): def __init__(self): self.fileInfo = {"version": "0.1.0", "files": []} self.filePath = {} self.burnAddr = [] def addFile(self, addr, path, prefix=False): if not os.path.exists(path): raise ValueError(tr("FilePathError")) if addr in self.burnAddr: raise ValueError(tr("Burn dddr duplicate")+":0x%06x" %(addr)) f = {} f_name = os.path.split(path)[1] f["address"] = addr f["bin"] = f_name f["sha256Prefix"] = prefix self.fileInfo["files"].append(f) self.filePath[f_name] = path self.burnAddr.append(addr) def listDumps(self): kfpkg_json = json.dumps(self.fileInfo, indent=4) return kfpkg_json def listDump(self, path): with open(path, "w") as f: f.write(json.dumps(self.fileInfo, indent=4)) def listLoads(self, kfpkgJson): self.fileInfo = json.loads(kfpkgJson) def listLload(self, path): with open(path) as f: self.fileInfo = json.load(f) def save(self, path): listName = os.path.join(tempfile.gettempdir(), "kflash_gui_tmp_list.json") self.listDump(listName) try: with zipfile.ZipFile(path, "w") as zip: for name,path in self.filePath.items(): zip.write(path, arcname=name, compress_type=zipfile.ZIP_LZMA) zip.write(listName, arcname="flash-list.json", compress_type=zipfile.ZIP_LZMA) zip.close() except Exception as e: os.remove(listName) raise e os.remove(listName) def packFile(self): # generate flash-list.json fileType, files = self.getBurnFilesInfo() if not fileType or not files or fileType=="kfpkg": self.errorSignal.emit(tr("Error"), tr("File path error")) return kfpkg = self.KFPKG() try: for path, addr, prefix in files: kfpkg.addFile(addr, path, prefix) except Exception as e: self.errorSignal.emit(tr("Error"), tr("Pack kfpkg fail")+":"+str(e)) return # select saving path if not os.path.exists(self.saveKfpkDir): self.saveKfpkDir = os.getcwd() fileName_choose, filetype = QFileDialog.getSaveFileName(self, tr("Save File"), self.saveKfpkDir, "k210 packages (*.kfpkg)") if fileName_choose == "": self.errorSignal.emit(tr("Error"), tr("File path error")) return if not fileName_choose.endswith(".kfpkg"): fileName_choose += ".kfpkg" self.saveKfpkDir = os.path.split(fileName_choose)[0] # print("save to ", fileName_choose) # write kfpkg file try: kfpkg.save(fileName_choose) except Exception as e: self.errorSignal.emit(tr("Error"), tr("Pack kfpkg fail")+":"+str(e)) return self.hintSignal.emit(tr("Success"), tr("Save kfpkg success")) def selectFile(self, pathobj): index = -1 for i in range(len(self.fileSelectWidgets)): if len(self.fileSelectWidgets[i]) >= 4: if pathobj == self.fileSelectWidget_Path(i): index = i if index == -1: return tmp = index while tmp>=0: oldPath = self.fileSelectWidget_Path(tmp).text() if oldPath != "": break tmp -= 1 if oldPath=="": oldPath = os.getcwd() fileName_choose, filetype = QFileDialog.getOpenFileName(self, tr("SelectFile"), oldPath, "All Files (*);;bin Files (*.bin);;k210 packages (*.kfpkg);;kmodel (*.kmodel);;encrypted kmodle(*.smodel)") # 设置文件扩展名过滤,用双分号间隔 if fileName_choose == "": return if not self.isFileValid(fileName_choose): self.errorSignal.emit(tr("Error"), tr("File path error")) return if self.isKfpkg(fileName_choose): self.fileSelectShowKfpkg(index, fileName_choose) else: self.fileSelectShowBin(index, fileName_choose, prefixAuto=True, closeButton=False) def errorHint(self, title, str): QMessageBox.critical(self, title, str) def hint(self, title, str): QMessageBox.information(self, title, str) def findSerialPort(self): self.port_list = list(serial.tools.list_ports.comports()) return self.port_list def portChanged(self): self.serialPortCombobox.setCurrentIndex(0) self.serialPortCombobox.setToolTip(str(self.portList[0])) def detectSerialPort(self): if not self.isDetectSerialPort: self.isDetectSerialPort = True t = threading.Thread(target=self.detectSerialPortProcess) t.setDaemon(True) t.start() def showCombobox(self): self.serialPortCombobox.showPopup() def isKfpkg(self, name): if name.endswith(".kfpkg"): return True return False def isFileValid(self, name): if not os.path.exists(name): return False return True def detectSerialPortProcess(self): while(1): portList = self.findSerialPort() if len(portList)>0: currText = self.serialPortCombobox.currentText() self.serialPortCombobox.clear() for i in portList: showStr = str(i[0])+" ("+str(i[1])+")" self.serialPortCombobox.addItem(showStr) index = self.serialPortCombobox.findText(currText) if index>=0: self.serialPortCombobox.setCurrentIndex(index) else: self.serialPortCombobox.setCurrentIndex(0) break time.sleep(1) self.showSerialComboboxSignal.emit() self.isDetectSerialPort = False def programExitSaveParameters(self): paramObj = paremeters_save.ParametersToSave() paramObj.board = self.boardCombobox.currentText() paramObj.burnPosition = self.burnPositionCombobox.currentText() paramObj.baudRate = self.serailBaudrateCombobox.currentIndex() paramObj.skin = self.param.skin paramObj.language = translation.current_lang path = self.fileSelectWidget_Path(0).text() if path.endswith(".kfpkg"): paramObj.files.append(path) else: for i in range(len(self.fileSelectWidgets)): try: addr = int(self.fileSelectWidget_Addr(i).text(),16) except Exception: continue paramObj.files.append( (self.fileSelectWidget_Path(i).text(), addr, self.fileSelectWidget_Prefix(i).isChecked()) ) if self.slowModeCombobox.currentIndex()==0: paramObj.slowMode = True else: paramObj.slowMode = False paramObj.save(parameters.configFilePath) def programStartGetSavedParameters(self): paramObj = paremeters_save.ParametersToSave() paramObj.load(parameters.configFilePath) translation.setLanguage(paramObj.language) self.param = paramObj def updateFrameParams(self): pathLen = len(self.param.files) if pathLen == 1 and type(self.param.files[0])==str and self.param.files[0].endswith(".kfpkg"): self.fileSelectWidget_Path(0).setText(self.param.files[0]) elif pathLen != 0: index = 0 for path, addr, prefix in self.param.files: prefix = None if (not prefix) else True if index!=0: self.addAddFileWidget() if pathLen > 1 and index != 0: closeButton = True else: closeButton = False self.fileSelectShowBin(index, path, addr, prefix, closeButton=closeButton) index += 1 self.boardCombobox.setCurrentText(self.param.board) self.burnPositionCombobox.setCurrentText(self.param.burnPosition) self.serailBaudrateCombobox.setCurrentIndex(self.param.baudRate) if self.param.slowMode: self.slowModeCombobox.setCurrentIndex(0) else: self.slowModeCombobox.setCurrentIndex(1) def closeEvent(self, event): try: self.programExitSaveParameters() finally: event.accept() def langChange(self): if self.param.language == translation.language_en: translation.setLanguage(translation.language_zh) lang = tr("Chinese language") else: translation.setLanguage(translation.language_en) lang = tr("English language") self.hint(tr("Hint"), tr("Language Changed to ") + lang + "\n"+ tr("Reboot to take effect")) self.frameWidget.style().unpolish(self.downloadButton) self.frameWidget.style().polish(self.downloadButton) self.frameWidget.update() def skinChange(self): if self.param.skin == 1: # light file = open(self.DataPath + '/assets/qss/style-dark.qss', "r") self.param.skin = 2 else: # elif self.param.skin == 2: # dark file = open(self.DataPath + '/assets/qss/style.qss', "r") self.param.skin = 1 self.app.setStyleSheet(file.read().replace("$DataPath", self.DataPath)) def showAbout(self): QMessageBox.information(self, tr("About"),"<h1 style='color:#f75a5a';margin=10px;>"+parameters.appName+ '</h1><br><b style="color:#08c7a1;margin = 5px;">V'+str(helpAbout.versionMajor)+"."+ str(helpAbout.versionMinor)+"."+str(helpAbout.versionDev)+ "</b><br><br>"+helpAbout.date+"<br><br>"+helpAbout.strAbout()) def autoUpdateDetect(self): auto = autoUpdate.AutoUpdate() if auto.detectNewVersion(): auto.OpenBrowser() def openDevManagement(self): os.system('start devmgmt.msc') def updateProgress(self, fileTypeStr, current, total, speedStr): currBurnPos = self.burnPositionCombobox.currentText() if currBurnPos == tr("SRAM") or currBurnPos == tr_en("SRAM"): fileTypeStr = tr("ToSRAM") percent = current/float(total)*100 hint = "<font color=%s>%s %s:</font> <font color=%s> %.2f%%</font> <font color=%s> %s</font>" %("#ff7575", tr("Downloading"), fileTypeStr, "#2985ff", percent, "#1aac2d", speedStr) self.progressHint.setText(hint) self.progressbar.setValue(percent) def updateProgressPrint(self, str): self.statusBarStauts.setText(str) def kflash_py_printCallback(self, *args, end = "\n"): msg = "" for i in args: msg += str(i) msg.replace("\n", " ") self.updateProgressPrintSignal.emit(msg) def progress(self, fileTypeStr, current, total, speedStr): self.updateProgressSignal.emit(fileTypeStr, current, total, speedStr) def download(self): if self.burning: self.terminateBurn() return tmpFile = "" fileType, filesInfo = self.getBurnFilesInfo() if not fileType or not filesInfo: self.errorSignal.emit(tr("Error"), tr("File path error")) return if fileType == "kfpkg": filename = filesInfo else:#generate kfpkg tmpFile = os.path.join(tempfile.gettempdir(), "kflash_gui_tmp.kfpkg") kfpkg = self.KFPKG() try: for path, addr, prefix in filesInfo: kfpkg.addFile(addr, path, prefix) kfpkg.save(tmpFile) except Exception as e: os.remove(tmpFile) self.errorSignal.emit(tr("Error"), tr("Pack kfpkg fail")+":"+str(e)) return filename = os.path.abspath(tmpFile) self.burning = True # if not self.checkFileName(filename): # self.errorSignal.emit(tr("Error"), tr("FilePathError")) # self.burning = False # return color = False board = "dan" boardText = self.boardCombobox.currentText() if boardText == parameters.SipeedMaixGo: board = "goE" elif boardText == parameters.SipeedMaixGoD: board = "goD" elif boardText == parameters.SipeedMaixduino: board = "maixduino" elif boardText == parameters.SipeedMaixBit: board = "bit" elif boardText == parameters.SipeedMaixBitMic: board = "bit_mic" elif boardText == parameters.KendryteKd233: board = "kd233" elif boardText == parameters.kendryteTrainer: board = "trainer" elif boardText == parameters.Auto: board = None sram = False if self.burnPositionCombobox.currentText()==tr("SRAM") or \ self.burnPositionCombobox.currentText()==tr_en("SRAM"): sram = True try: baud = int(self.serailBaudrateCombobox.currentText()) except Exception: self.errorSignal.emit(tr("Error"), tr("BaudrateError")) self.burning = False return dev = "" try: dev = self.serialPortCombobox.currentText().split()[0] except Exception: pass if dev=="": self.errorSignal.emit(tr("Error"), tr("PleaseSelectSerialPort")) self.burning = False return slow = self.slowModeCombobox.currentIndex()==0 # hide setting widgets self.setFrameStrentch(1) self.settingWidget.hide() self.progressbar.setValue(0) self.progressbar.setGeometry(10, 0, self.downloadWidget.width()-25, 40) self.progressbarRootWidget.show() self.progressHint.show() self.downloadButton.setText(tr("Cancel")) self.downloadButton.setProperty("class", "redbutton") self.downloadButton.style().unpolish(self.downloadButton) self.downloadButton.style().polish(self.downloadButton) self.downloadButton.update() self.statusBarStauts.setText("<font color=%s>%s ...</font>" %("#1aac2d", tr("Downloading"))) hint = "<font color=%s>%s</font>" %("#ff0d0d", tr("DownloadStart")) self.progressHint.setText(hint) # download self.burnThread = threading.Thread(target=self.flashBurnProcess, args=(dev, baud, board, sram, filename, self.progress, tmpFile!="", color, slow)) self.burnThread.setDaemon(True) self.burnThread.start() def flashBurnProcess(self, dev, baud, board, sram, filename, callback, cleanFile, color, slow): success = True errMsg = "" try: if board: self.kflash.process(terminal=False, dev=dev, baudrate=baud, board=board, sram = sram, file=filename, callback=callback, noansi=not color, slow_mode=slow) else: self.kflash.process(terminal=False, dev=dev, baudrate=baud, sram = sram, file=filename, callback=callback, noansi=not color, slow_mode=slow) except Exception as e: errMsg = str(e) if str(e) != "Burn SRAM OK": success = False if cleanFile: os.remove(filename) if success: self.downloadResultSignal.emit(True, errMsg) else: self.downloadResultSignal.emit(False, errMsg) def downloadResult(self, success, msg): if success: self.hintSignal.emit(tr("Success"), tr("DownloadSuccess")) self.statusBarStauts.setText("<font color=%s>%s</font>" %("#1aac2d", tr("DownloadSuccess"))) else: if msg == "Cancel": self.statusBarStauts.setText("<font color=%s>%s</font>" %("#ff1d1d", tr("DownloadCanceled"))) else: msg = tr("ErrorSettingHint") + "\n\n"+msg self.errorSignal.emit(tr("Error"), msg) self.statusBarStauts.setText("<font color=%s>%s</font>" %("#ff1d1d", tr("DownloadFail"))) self.progressHint.setText("") self.downloadButton.setText(tr("Download")) self.downloadButton.setProperty("class", "normalbutton") self.downloadButton.style().unpolish(self.downloadButton) self.downloadButton.style().polish(self.downloadButton) self.downloadButton.update() self.setFrameStrentch(0) self.progressbarRootWidget.hide() self.progressHint.hide() self.settingWidget.show() self.burning = False def terminateBurn(self): hint = "<font color=%s>%s</font>" %("#ff0d0d", tr("DownloadCanceling")) self.progressHint.setText(hint) self.kflash.kill()
class MainWindow(QMainWindow): receiveUpdateSignal = pyqtSignal(str) errorSignal = pyqtSignal(str) showSerialComboboxSignal = pyqtSignal() setDisableSettingsSignal = pyqtSignal(bool) isDetectSerialPort = False receiveCount = 0 sendCount = 0 isScheduledSending = False DataPath = "./" isHideSettings = False isHideFunctinal = True app = None isWaveOpen = False def __init__(self, app): super().__init__() self.app = app pathDirList = sys.argv[0].replace("\\", "/").split("/") pathDirList.pop() self.DataPath = os.path.abspath("/".join(str(i) for i in pathDirList)) if not os.path.exists(self.DataPath + "/" + parameters.strDataDirName): pathDirList.pop() self.DataPath = os.path.abspath("/".join( str(i) for i in pathDirList)) self.DataPath = (self.DataPath + "/" + parameters.strDataDirName).replace("\\", "/") self.initWindow() self.initTool() self.initEvent() self.programStartGetSavedParameters() def __del__(self): pass def initTool(self): self.com = serial.Serial() def initWindow(self): QToolTip.setFont(QFont('SansSerif', 10)) # main layout frameWidget = QWidget() mainWidget = QSplitter(Qt.Horizontal) frameLayout = QVBoxLayout() self.settingWidget = QWidget() self.settingWidget.setProperty("class", "settingWidget") self.receiveSendWidget = QSplitter(Qt.Vertical) self.functionalWiget = QWidget() settingLayout = QVBoxLayout() sendReceiveLayout = QVBoxLayout() sendFunctionalLayout = QVBoxLayout() mainLayout = QHBoxLayout() self.settingWidget.setLayout(settingLayout) self.receiveSendWidget.setLayout(sendReceiveLayout) self.functionalWiget.setLayout(sendFunctionalLayout) mainLayout.addWidget(self.settingWidget) mainLayout.addWidget(self.receiveSendWidget) mainLayout.addWidget(self.functionalWiget) mainLayout.setStretch(0, 2) mainLayout.setStretch(1, 6) mainLayout.setStretch(2, 2) menuLayout = QHBoxLayout() mainWidget.setLayout(mainLayout) frameLayout.addLayout(menuLayout) frameLayout.addWidget(mainWidget) frameWidget.setLayout(frameLayout) self.setCentralWidget(frameWidget) # option layout self.settingsButton = QPushButton() self.skinButton = QPushButton("") # self.waveButton = QPushButton("") self.aboutButton = QPushButton() self.functionalButton = QPushButton() self.encodingCombobox = ComboBox() self.encodingCombobox.addItem("ASCII") self.encodingCombobox.addItem("UTF-8") self.encodingCombobox.addItem("UTF-16") self.encodingCombobox.addItem("GBK") self.encodingCombobox.addItem("GB2312") self.encodingCombobox.addItem("GB18030") self.settingsButton.setProperty("class", "menuItem1") self.skinButton.setProperty("class", "menuItem2") self.aboutButton.setProperty("class", "menuItem3") self.functionalButton.setProperty("class", "menuItem4") # self.waveButton.setProperty("class", "menuItem5") self.settingsButton.setObjectName("menuItem") self.skinButton.setObjectName("menuItem") self.aboutButton.setObjectName("menuItem") self.functionalButton.setObjectName("menuItem") # self.waveButton.setObjectName("menuItem") menuLayout.addWidget(self.settingsButton) menuLayout.addWidget(self.skinButton) # menuLayout.addWidget(self.waveButton) menuLayout.addWidget(self.aboutButton) menuLayout.addStretch(0) menuLayout.addWidget(self.encodingCombobox) menuLayout.addWidget(self.functionalButton) # widgets receive and send area self.receiveArea = QTextEdit() self.sendArea = QTextEdit() self.clearReceiveButtion = QPushButton(parameters.strClearReceive) self.sendButtion = QPushButton(parameters.strSend) self.sendHistory = ComboBox() sendWidget = QWidget() sendAreaWidgetsLayout = QHBoxLayout() sendWidget.setLayout(sendAreaWidgetsLayout) buttonLayout = QVBoxLayout() buttonLayout.addWidget(self.clearReceiveButtion) buttonLayout.addStretch(1) buttonLayout.addWidget(self.sendButtion) sendAreaWidgetsLayout.addWidget(self.sendArea) sendAreaWidgetsLayout.addLayout(buttonLayout) sendReceiveLayout.addWidget(self.receiveArea) sendReceiveLayout.addWidget(sendWidget) sendReceiveLayout.addWidget(self.sendHistory) sendReceiveLayout.setStretch(0, 7) sendReceiveLayout.setStretch(1, 2) sendReceiveLayout.setStretch(2, 1) # widgets serial settings serialSettingsGroupBox = QGroupBox(parameters.strSerialSettings) serialSettingsLayout = QGridLayout() serialReceiveSettingsLayout = QGridLayout() serialSendSettingsLayout = QGridLayout() serialPortLabek = QLabel(parameters.strSerialPort) serailBaudrateLabel = QLabel(parameters.strSerialBaudrate) serailBytesLabel = QLabel(parameters.strSerialBytes) serailParityLabel = QLabel(parameters.strSerialParity) serailStopbitsLabel = QLabel(parameters.strSerialStopbits) self.serialPortCombobox = ComboBox() self.serailBaudrateCombobox = ComboBox() self.serailBaudrateCombobox.addItem("9600") self.serailBaudrateCombobox.addItem("19200") self.serailBaudrateCombobox.addItem("38400") self.serailBaudrateCombobox.addItem("57600") self.serailBaudrateCombobox.addItem("115200") self.serailBaudrateCombobox.setCurrentIndex(4) self.serailBaudrateCombobox.setEditable(True) self.serailBytesCombobox = ComboBox() self.serailBytesCombobox.addItem("5") self.serailBytesCombobox.addItem("6") self.serailBytesCombobox.addItem("7") self.serailBytesCombobox.addItem("8") self.serailBytesCombobox.setCurrentIndex(3) self.serailParityCombobox = ComboBox() self.serailParityCombobox.addItem("None") self.serailParityCombobox.addItem("Odd") self.serailParityCombobox.addItem("Even") self.serailParityCombobox.addItem("Mark") self.serailParityCombobox.addItem("Space") self.serailParityCombobox.setCurrentIndex(0) self.serailStopbitsCombobox = ComboBox() self.serailStopbitsCombobox.addItem("1") self.serailStopbitsCombobox.addItem("1.5") self.serailStopbitsCombobox.addItem("2") self.serailStopbitsCombobox.setCurrentIndex(0) self.checkBoxRts = QCheckBox("rts") self.checkBoxDtr = QCheckBox("dtr") self.serialOpenCloseButton = QPushButton(parameters.strOpen) serialSettingsLayout.addWidget(serialPortLabek, 0, 0) serialSettingsLayout.addWidget(serailBaudrateLabel, 1, 0) serialSettingsLayout.addWidget(serailBytesLabel, 2, 0) serialSettingsLayout.addWidget(serailParityLabel, 3, 0) serialSettingsLayout.addWidget(serailStopbitsLabel, 4, 0) serialSettingsLayout.addWidget(self.serialPortCombobox, 0, 1) serialSettingsLayout.addWidget(self.serailBaudrateCombobox, 1, 1) serialSettingsLayout.addWidget(self.serailBytesCombobox, 2, 1) serialSettingsLayout.addWidget(self.serailParityCombobox, 3, 1) serialSettingsLayout.addWidget(self.serailStopbitsCombobox, 4, 1) serialSettingsLayout.addWidget(self.checkBoxRts, 5, 0, 1, 1) serialSettingsLayout.addWidget(self.checkBoxDtr, 5, 1, 1, 1) serialSettingsLayout.addWidget(self.serialOpenCloseButton, 6, 0, 1, 2) serialSettingsGroupBox.setLayout(serialSettingsLayout) settingLayout.addWidget(serialSettingsGroupBox) # serial receive settings serialReceiveSettingsGroupBox = QGroupBox( parameters.strSerialReceiveSettings) self.receiveSettingsAscii = QRadioButton(parameters.strAscii) self.receiveSettingsHex = QRadioButton(parameters.strHex) self.receiveSettingsAscii.setChecked(True) self.receiveSettingsAutoLinefeed = QCheckBox( parameters.strAutoLinefeed) self.receiveSettingsAutoLinefeedTime = QLineEdit( parameters.strAutoLinefeedTime) self.receiveSettingsAutoLinefeed.setMaximumWidth(75) self.receiveSettingsAutoLinefeedTime.setMaximumWidth(75) serialReceiveSettingsLayout.addWidget(self.receiveSettingsAscii, 1, 0, 1, 1) serialReceiveSettingsLayout.addWidget(self.receiveSettingsHex, 1, 1, 1, 1) serialReceiveSettingsLayout.addWidget(self.receiveSettingsAutoLinefeed, 2, 0, 1, 1) serialReceiveSettingsLayout.addWidget( self.receiveSettingsAutoLinefeedTime, 2, 1, 1, 1) serialReceiveSettingsGroupBox.setLayout(serialReceiveSettingsLayout) settingLayout.addWidget(serialReceiveSettingsGroupBox) # serial send settings serialSendSettingsGroupBox = QGroupBox( parameters.strSerialSendSettings) self.sendSettingsAscii = QRadioButton(parameters.strAscii) self.sendSettingsHex = QRadioButton(parameters.strHex) self.sendSettingsAscii.setChecked(True) self.sendSettingsScheduledCheckBox = QCheckBox(parameters.strScheduled) self.sendSettingsScheduled = QLineEdit(parameters.strScheduledTime) self.sendSettingsScheduledCheckBox.setMaximumWidth(75) self.sendSettingsScheduled.setMaximumWidth(75) self.sendSettingsCFLF = QCheckBox(parameters.strCRLF) self.sendSettingsCFLF.setChecked(False) serialSendSettingsLayout.addWidget(self.sendSettingsAscii, 1, 0, 1, 1) serialSendSettingsLayout.addWidget(self.sendSettingsHex, 1, 1, 1, 1) serialSendSettingsLayout.addWidget(self.sendSettingsScheduledCheckBox, 2, 0, 1, 1) serialSendSettingsLayout.addWidget(self.sendSettingsScheduled, 2, 1, 1, 1) serialSendSettingsLayout.addWidget(self.sendSettingsCFLF, 3, 0, 1, 2) serialSendSettingsGroupBox.setLayout(serialSendSettingsLayout) settingLayout.addWidget(serialSendSettingsGroupBox) settingLayout.setStretch(0, 5) settingLayout.setStretch(1, 2) settingLayout.setStretch(2, 2) # right functional layout self.filePathWidget = QLineEdit() self.openFileButton = QPushButton("Open File") self.sendFileButton = QPushButton("Send File") self.clearHistoryButton = QPushButton("Clear History") self.addButton = QPushButton(parameters.strAdd) fileSendGroupBox = QGroupBox(parameters.strSendFile) fileSendGridLayout = QGridLayout() fileSendGridLayout.addWidget(self.filePathWidget, 0, 0, 1, 1) fileSendGridLayout.addWidget(self.openFileButton, 0, 1, 1, 1) fileSendGridLayout.addWidget(self.sendFileButton, 1, 0, 1, 2) fileSendGroupBox.setLayout(fileSendGridLayout) sendFunctionalLayout.addWidget(fileSendGroupBox) sendFunctionalLayout.addWidget(self.clearHistoryButton) sendFunctionalLayout.addWidget(self.addButton) sendFunctionalLayout.addStretch(1) self.isHideFunctinal = True self.hideFunctional() # main window self.statusBarStauts = QLabel() self.statusBarStauts.setMinimumWidth(80) self.statusBarStauts.setText("<font color=%s>%s</font>" % ("#008200", parameters.strReady)) self.statusBarSendCount = QLabel(parameters.strSend + "(bytes): " + "0") self.statusBarReceiveCount = QLabel(parameters.strReceive + "(bytes): " + "0") self.statusBar().addWidget(self.statusBarStauts) self.statusBar().addWidget(self.statusBarSendCount, 2) self.statusBar().addWidget(self.statusBarReceiveCount, 3) # self.statusBar() self.resize(800, 500) self.MoveToCenter() self.setWindowTitle(parameters.appName + " V" + str(helpAbout.versionMajor) + "." + str(helpAbout.versionMinor)) icon = QIcon() print("icon path:" + self.DataPath + "/" + parameters.appIcon) icon.addPixmap(QPixmap(self.DataPath + "/" + parameters.appIcon), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) if sys.platform == "win32": ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( "comtool") self.show() print("config file path:", parameters.configFilePath) def initEvent(self): self.serialOpenCloseButton.clicked.connect(self.openCloseSerial) self.sendButtion.clicked.connect(self.sendData) self.receiveUpdateSignal.connect(self.updateReceivedDataDisplay) self.clearReceiveButtion.clicked.connect(self.clearReceiveBuffer) self.serialPortCombobox.clicked.connect(self.portComboboxClicked) self.sendSettingsHex.clicked.connect(self.onSendSettingsHexClicked) self.sendSettingsAscii.clicked.connect(self.onSendSettingsAsciiClicked) self.errorSignal.connect(self.errorHint) self.showSerialComboboxSignal.connect(self.showCombobox) # self.showBaudComboboxSignal.connect(self.showBaudCombobox) self.setDisableSettingsSignal.connect(self.setDisableSettings) self.sendHistory.currentIndexChanged.connect( self.sendHistoryIndexChanged) self.settingsButton.clicked.connect(self.showHideSettings) self.skinButton.clicked.connect(self.skinChange) self.aboutButton.clicked.connect(self.showAbout) self.openFileButton.clicked.connect(self.selectFile) self.sendFileButton.clicked.connect(self.sendFile) self.clearHistoryButton.clicked.connect(self.clearHistory) self.addButton.clicked.connect(self.functionAdd) self.functionalButton.clicked.connect(self.showHideFunctional) self.sendArea.currentCharFormatChanged.connect( self.sendAreaFontChanged) # self.waveButton.clicked.connect(self.openWaveDisplay) self.checkBoxRts.clicked.connect(self.rtsChanged) self.checkBoxDtr.clicked.connect(self.dtrChanged) self.myObject = MyClass(self) slotLambda = lambda: self.indexChanged_lambda(self.myObject) self.serialPortCombobox.currentIndexChanged.connect(slotLambda) # @QtCore.pyqtSlot(str) def indexChanged_lambda(self, obj): mainObj = obj.arg # print("item changed:",mainObj.serialPortCombobox.currentText()) self.serialPortCombobox.setToolTip( mainObj.serialPortCombobox.currentText()) def openCloseSerialProcess(self): try: if self.com.is_open: self.receiveProgressStop = True self.com.close() self.setDisableSettingsSignal.emit(False) else: try: self.com.baudrate = int( self.serailBaudrateCombobox.currentText()) self.com.port = self.serialPortCombobox.currentText( ).split(" ")[0] self.com.bytesize = int( self.serailBytesCombobox.currentText()) self.com.parity = self.serailParityCombobox.currentText( )[0] self.com.stopbits = float( self.serailStopbitsCombobox.currentText()) self.com.timeout = None if self.checkBoxRts.isChecked(): self.com.rts = False else: self.com.rts = True if self.checkBoxDtr.isChecked(): self.com.dtr = False else: self.com.dtr = True self.com.open() # print("open success") # print(self.com) self.setDisableSettingsSignal.emit(True) self.receiveProcess = threading.Thread( target=self.receiveData) self.receiveProcess.setDaemon(True) self.receiveProcess.start() except Exception as e: self.com.close() self.receiveProgressStop = True self.errorSignal.emit(parameters.strOpenFailed + "\n" + str(e)) self.setDisableSettingsSignal.emit(False) except Exception as e: print(e) def setDisableSettings(self, disable): if disable: self.serialOpenCloseButton.setText(parameters.strClose) self.statusBarStauts.setText("<font color=%s>%s</font>" % ("#008200", parameters.strReady)) self.serialPortCombobox.setDisabled(True) self.serailBaudrateCombobox.setDisabled(True) self.serailParityCombobox.setDisabled(True) self.serailStopbitsCombobox.setDisabled(True) self.serailBytesCombobox.setDisabled(True) self.serialOpenCloseButton.setDisabled(False) else: self.serialOpenCloseButton.setText(parameters.strOpen) self.statusBarStauts.setText("<font color=%s>%s</font>" % ("#f31414", parameters.strClosed)) self.serialPortCombobox.setDisabled(False) self.serailBaudrateCombobox.setDisabled(False) self.serailParityCombobox.setDisabled(False) self.serailStopbitsCombobox.setDisabled(False) self.serailBytesCombobox.setDisabled(False) self.programExitSaveParameters() def openCloseSerial(self): t = threading.Thread(target=self.openCloseSerialProcess) t.setDaemon(True) t.start() def rtsChanged(self): if self.checkBoxRts.isChecked(): self.com.setRTS(False) else: self.com.setRTS(True) def dtrChanged(self): if self.checkBoxDtr.isChecked(): self.com.setDTR(False) else: self.com.setDTR(True) def portComboboxClicked(self): self.detectSerialPort() def getSendData(self): data = self.sendArea.toPlainText() if self.sendSettingsCFLF.isChecked(): data = data.replace("\n", "\r\n") if self.sendSettingsHex.isChecked(): if self.sendSettingsCFLF.isChecked(): data = data.replace("\r\n", " ") else: data = data.replace("\n", " ") data = self.hexStringB2Hex(data) if data == -1: self.errorSignal.emit(parameters.strWriteFormatError) return -1 else: data = data.encode(self.encodingCombobox.currentText(), "ignore") return data def sendData(self): try: if self.com.is_open: data = self.getSendData() if data == -1: return # print(self.sendArea.toPlainText()) # print("send:",data) self.sendCount += len(data) self.com.write(data) data = self.sendArea.toPlainText() self.sendHistoryFindDelete(data) self.sendHistory.insertItem(0, data) self.sendHistory.setCurrentIndex(0) self.receiveUpdateSignal.emit("") # scheduled send if self.sendSettingsScheduledCheckBox.isChecked(): if not self.isScheduledSending: t = threading.Thread(target=self.scheduledSend) t.setDaemon(True) t.start() except Exception as e: self.errorSignal.emit(parameters.strWriteError) # print(e) def scheduledSend(self): self.isScheduledSending = True while self.sendSettingsScheduledCheckBox.isChecked(): self.sendData() try: time.sleep( int(self.sendSettingsScheduled.text().strip()) / 1000) except Exception: self.errorSignal.emit(parameters.strTimeFormatError) self.isScheduledSending = False def receiveData(self): self.receiveProgressStop = False self.timeLastReceive = 0 while (not self.receiveProgressStop): try: # length = self.com.in_waiting length = max(1, min(2048, self.com.in_waiting)) bytes = self.com.read(length) if bytes != None: # if self.isWaveOpen: # self.wave.displayData(bytes) self.receiveCount += len(bytes) if self.receiveSettingsAutoLinefeed.isChecked(): if time.time() - self.timeLastReceive > int( self.receiveSettingsAutoLinefeedTime.text( )) / 1000: if self.sendSettingsCFLF.isChecked(): self.receiveUpdateSignal.emit("\r\n") else: self.receiveUpdateSignal.emit("\n") self.timeLastReceive = time.time() if self.receiveSettingsHex.isChecked(): strReceived = self.asciiB2HexString(bytes) self.receiveUpdateSignal.emit(strReceived) else: self.receiveUpdateSignal.emit( bytes.decode(self.encodingCombobox.currentText(), "ignore")) except Exception as e: # print("receiveData error") # if self.com.is_open and not self.serialPortCombobox.isEnabled(): # self.openCloseSerial() # self.serialPortCombobox.clear() # self.detectSerialPort() if 'multiple access' in str(e): self.errorSignal.emit( "device disconnected or multiple access on port?") break # time.sleep(0.009) def updateReceivedDataDisplay(self, str): if str != "": curScrollValue = self.receiveArea.verticalScrollBar().value() self.receiveArea.moveCursor(QTextCursor.End) endScrollValue = self.receiveArea.verticalScrollBar().value() self.receiveArea.insertPlainText(str) if curScrollValue < endScrollValue: self.receiveArea.verticalScrollBar().setValue(curScrollValue) else: self.receiveArea.moveCursor(QTextCursor.End) self.statusBarSendCount.setText("%s(bytes):%d" % (parameters.strSend, self.sendCount)) self.statusBarReceiveCount.setText( "%s(bytes):%d" % (parameters.strReceive, self.receiveCount)) def onSendSettingsHexClicked(self): data = self.sendArea.toPlainText().replace("\n", "\r\n") data = self.asciiB2HexString(data.encode()) self.sendArea.clear() self.sendArea.insertPlainText(data) def onSendSettingsAsciiClicked(self): try: data = self.sendArea.toPlainText().replace("\n", " ").strip() self.sendArea.clear() if data != "": data = self.hexStringB2Hex(data).decode( self.encodingCombobox.currentText(), 'ignore') self.sendArea.insertPlainText(data) except Exception as e: # QMessageBox.information(self,parameters.strWriteFormatError,parameters.strWriteFormatError) print("format error") def sendHistoryIndexChanged(self): self.sendArea.clear() self.sendArea.insertPlainText(self.sendHistory.currentText()) def clearReceiveBuffer(self): self.receiveArea.clear() self.receiveCount = 0 self.sendCount = 0 self.receiveUpdateSignal.emit(None) def MoveToCenter(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def errorHint(self, str): QMessageBox.information(self, str, str) def closeEvent(self, event): reply = QMessageBox.question(self, 'Sure To Quit?', "Are you sure to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self.com.close() self.receiveProgressStop = True self.programExitSaveParameters() event.accept() else: event.ignore() def findSerialPort(self): self.port_list = list(serial.tools.list_ports.comports()) return self.port_list def portChanged(self): self.serialPortCombobox.setCurrentIndex(0) self.serialPortCombobox.setToolTip(str(self.portList[0])) def detectSerialPort(self): if not self.isDetectSerialPort: self.isDetectSerialPort = True t = threading.Thread(target=self.detectSerialPortProcess) t.setDaemon(True) t.start() def showCombobox(self): self.serialPortCombobox.showPopup() def detectSerialPortProcess(self): while (1): portList = self.findSerialPort() if len(portList) > 0: currText = self.serialPortCombobox.currentText() self.serialPortCombobox.clear() for i in portList: showStr = str(i[0]) + " " + str(i[1]) if i[0].startswith("/dev/cu.Bluetooth-Incoming-Port"): continue self.serialPortCombobox.addItem(showStr) index = self.serialPortCombobox.findText(currText) if index >= 0: self.serialPortCombobox.setCurrentIndex(index) else: self.serialPortCombobox.setCurrentIndex(0) break time.sleep(1) self.showSerialComboboxSignal.emit() self.isDetectSerialPort = False def sendHistoryFindDelete(self, str): self.sendHistory.removeItem(self.sendHistory.findText(str)) def asciiB2HexString(self, strB): strHex = binascii.b2a_hex(strB).upper() return re.sub(r"(?<=\w)(?=(?:\w\w)+$)", " ", strHex.decode()) + " " def hexStringB2Hex(self, hexString): dataList = hexString.split(" ") j = 0 for i in dataList: if len(i) > 2: return -1 elif len(i) == 1: dataList[j] = "0" + i j += 1 data = "".join(dataList) try: data = bytes.fromhex(data) except Exception: return -1 # print(data) return data def programExitSaveParameters(self): paramObj = parameters.ParametersToSave() paramObj.baudRate = self.serailBaudrateCombobox.currentIndex() paramObj.dataBytes = self.serailBytesCombobox.currentIndex() paramObj.parity = self.serailParityCombobox.currentIndex() paramObj.stopBits = self.serailStopbitsCombobox.currentIndex() paramObj.skin = self.param.skin if self.receiveSettingsHex.isChecked(): paramObj.receiveAscii = False if not self.receiveSettingsAutoLinefeed.isChecked(): paramObj.receiveAutoLinefeed = False else: paramObj.receiveAutoLinefeed = True paramObj.receiveAutoLindefeedTime = self.receiveSettingsAutoLinefeedTime.text( ) if self.sendSettingsHex.isChecked(): paramObj.sendAscii = False if not self.sendSettingsScheduledCheckBox.isChecked(): paramObj.sendScheduled = False paramObj.sendScheduledTime = self.sendSettingsScheduled.text() if not self.sendSettingsCFLF.isChecked(): paramObj.useCRLF = False paramObj.sendHistoryList.clear() for i in range(0, self.sendHistory.count()): paramObj.sendHistoryList.append(self.sendHistory.itemText(i)) if self.checkBoxRts.isChecked(): paramObj.rts = 1 else: paramObj.rts = 0 if self.checkBoxDtr.isChecked(): paramObj.dtr = 1 else: paramObj.dtr = 0 paramObj.encodingIndex = self.encodingCombobox.currentIndex() f = open(parameters.configFilePath, "wb") f.truncate() pickle.dump(paramObj, f) pickle.dump(paramObj.sendHistoryList, f) f.close() def programStartGetSavedParameters(self): paramObj = parameters.ParametersToSave() try: f = open(parameters.configFilePath, "rb") paramObj = pickle.load(f) paramObj.sendHistoryList = pickle.load(f) f.close() except Exception as e: f = open(parameters.configFilePath, "wb") f.close() self.serailBaudrateCombobox.setCurrentIndex(paramObj.baudRate) self.serailBytesCombobox.setCurrentIndex(paramObj.dataBytes) self.serailParityCombobox.setCurrentIndex(paramObj.parity) self.serailStopbitsCombobox.setCurrentIndex(paramObj.stopBits) if paramObj.receiveAscii == False: self.receiveSettingsHex.setChecked(True) if paramObj.receiveAutoLinefeed == False: self.receiveSettingsAutoLinefeed.setChecked(False) else: self.receiveSettingsAutoLinefeed.setChecked(True) self.receiveSettingsAutoLinefeedTime.setText( paramObj.receiveAutoLindefeedTime) if paramObj.sendAscii == False: self.sendSettingsHex.setChecked(True) if paramObj.sendScheduled == False: self.sendSettingsScheduledCheckBox.setChecked(False) else: self.sendSettingsScheduledCheckBox.setChecked(True) self.sendSettingsScheduled.setText(paramObj.sendScheduledTime) if paramObj.useCRLF == False: self.sendSettingsCFLF.setChecked(False) else: self.sendSettingsCFLF.setChecked(True) for i in range(0, len(paramObj.sendHistoryList)): str = paramObj.sendHistoryList[i] self.sendHistory.addItem(str) if paramObj.rts == 0: self.checkBoxRts.setChecked(False) else: self.checkBoxRts.setChecked(True) if paramObj.dtr == 0: self.checkBoxDtr.setChecked(False) else: self.checkBoxDtr.setChecked(True) self.encodingCombobox.setCurrentIndex(paramObj.encodingIndex) self.param = paramObj def keyPressEvent(self, event): if event.key() == Qt.Key_Control: self.keyControlPressed = True elif event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: if self.keyControlPressed: self.sendData() elif event.key() == Qt.Key_L: if self.keyControlPressed: self.sendArea.clear() elif event.key() == Qt.Key_K: if self.keyControlPressed: self.receiveArea.clear() def keyReleaseEvent(self, event): if event.key() == Qt.Key_Control: self.keyControlPressed = False def sendAreaFontChanged(self, font): print("font changed") def functionAdd(self): QMessageBox.information(self, "On the way", "On the way") def showHideSettings(self): if self.isHideSettings: self.showSettings() self.isHideSettings = False else: self.hideSettings() self.isHideSettings = True def showSettings(self): self.settingWidget.show() self.settingsButton.setStyleSheet( parameters.strStyleShowHideButtonLeft.replace( "$DataPath", self.DataPath)) def hideSettings(self): self.settingWidget.hide() self.settingsButton.setStyleSheet( parameters.strStyleShowHideButtonRight.replace( "$DataPath", self.DataPath)) def showHideFunctional(self): if self.isHideFunctinal: self.showFunctional() self.isHideFunctinal = False else: self.hideFunctional() self.isHideFunctinal = True def showFunctional(self): self.functionalWiget.show() self.functionalButton.setStyleSheet( parameters.strStyleShowHideButtonRight.replace( "$DataPath", self.DataPath)) def hideFunctional(self): self.functionalWiget.hide() self.functionalButton.setStyleSheet( parameters.strStyleShowHideButtonLeft.replace( "$DataPath", self.DataPath)) def skinChange(self): if self.param.skin == 1: # light file = open(self.DataPath + '/assets/qss/style-dark.qss', "r") self.param.skin = 2 else: # elif self.param.skin == 2: # dark file = open(self.DataPath + '/assets/qss/style.qss', "r") self.param.skin = 1 self.app.setStyleSheet(file.read().replace("$DataPath", self.DataPath)) def showAbout(self): QMessageBox.information( self, "About", "<h1 style='color:#f75a5a';margin=10px;>" + parameters.appName + '</h1><br><b style="color:#08c7a1;margin = 5px;">V' + str(helpAbout.versionMajor) + "." + str(helpAbout.versionMinor) + "." + str(helpAbout.versionDev) + "</b><br><br>" + helpAbout.date + "<br><br>" + helpAbout.strAbout()) def selectFile(self): oldPath = self.filePathWidget.text() if oldPath == "": oldPath = os.getcwd() fileName_choose, filetype = QFileDialog.getOpenFileName( self, "SelectFile", oldPath, "All Files (*);;") if fileName_choose == "": return self.filePathWidget.setText(fileName_choose) def sendFile(self): filename = self.filePathWidget.text() if not os.path.exists(filename): self.errorSignal.emit("File path error\npath:%s" % (filename)) return try: f = open(filename, "rb") except Exception as e: self.errorSignal.emit("Open file %s failed! \n %s" % (filename, str(e))) return self.com.write(f.read()) #TODO: optimize send in new thread f.close() def clearHistory(self): self.param.sendHistoryList.clear() self.sendHistory.clear() self.errorSignal.emit("History cleared!") def autoUpdateDetect(self): auto = autoUpdate.AutoUpdate() if auto.detectNewVersion(): auto.OpenBrowser() def openDevManagement(self): os.system('start devmgmt.msc')
class MainWindow(QMainWindow): errorSignal = pyqtSignal(str, str) hintSignal = pyqtSignal(str, str) updateProgressSignal = pyqtSignal(str, int, int, str) updateProgressPrintSignal = pyqtSignal(str) showSerialComboboxSignal = pyqtSignal() downloadResultSignal = pyqtSignal(bool, str) DataPath = "./" app = None def __init__(self, app): super().__init__() self.app = app self.programStartGetSavedParameters() self.initVar() self.initWindow() self.updateFrameParams() self.initEvent() def __del__(self): pass def initVar(self): self.burning = False self.isDetectSerialPort = False self.DataPath = parameters.dataPath self.kflash = KFlash(print_callback=self.kflash_py_printCallback) def initWindow(self): QToolTip.setFont(QFont('SansSerif', 10)) # main layout self.frameWidget = QWidget() mainWidget = QSplitter(Qt.Horizontal) self.frameLayout = QVBoxLayout() self.settingWidget = QWidget() settingLayout = QVBoxLayout() self.settingWidget.setProperty("class", "settingWidget") mainLayout = QVBoxLayout() self.settingWidget.setLayout(settingLayout) mainLayout.addWidget(self.settingWidget) mainLayout.setStretch(0, 2) menuLayout = QHBoxLayout() self.progressHint = QLabel() self.progressHint.hide() self.progressbarRootWidget = QWidget() progressbarLayout = QVBoxLayout() self.progressbarRootWidget.setProperty("class", "progressbarWidget") self.progressbarRootWidget.setLayout(progressbarLayout) self.downloadWidget = QWidget() downloadLayout = QVBoxLayout() self.downloadWidget.setProperty("class", "downloadWidget") self.downloadWidget.setLayout(downloadLayout) mainWidget.setLayout(mainLayout) # menu # ----- # settings and others # ----- # progress bar # ----- # download button # ----- # status bar self.frameLayout.addLayout(menuLayout) self.frameLayout.addWidget(mainWidget) self.frameLayout.addWidget(self.progressHint) self.frameLayout.addWidget(self.progressbarRootWidget) self.frameLayout.addWidget(self.downloadWidget) self.frameWidget.setLayout(self.frameLayout) self.setCentralWidget(self.frameWidget) self.setFrameStrentch(0) # option layout self.langButton = QPushButton() self.skinButton = QPushButton() self.aboutButton = QPushButton() self.langButton.setProperty("class", "menuItemLang") self.skinButton.setProperty("class", "menuItem2") self.aboutButton.setProperty("class", "menuItem3") self.langButton.setObjectName("menuItem") self.skinButton.setObjectName("menuItem") self.aboutButton.setObjectName("menuItem") menuLayout.addWidget(self.langButton) menuLayout.addWidget(self.skinButton) menuLayout.addWidget(self.aboutButton) menuLayout.addStretch(0) # widgets file select fileSelectGroupBox = QGroupBox(tr("SelectFile")) settingLayout.addWidget(fileSelectGroupBox) fileSelectLayout = QHBoxLayout() fileSelectGroupBox.setLayout(fileSelectLayout) self.filePathWidget = QLineEdit() self.openFileButton = QPushButton(tr("OpenFile")) fileSelectLayout.addWidget(self.filePathWidget) fileSelectLayout.addWidget(self.openFileButton) # widgets board select boardSettingsGroupBox = QGroupBox(tr("BoardSettings")) settingLayout.addWidget(boardSettingsGroupBox) boardSettingsLayout = QGridLayout() boardSettingsGroupBox.setLayout(boardSettingsLayout) self.boardLabel = QLabel(tr("Board")) self.boardCombobox = ComboBox() self.boardCombobox.addItem(parameters.SipeedMaixDock) self.boardCombobox.addItem(parameters.SipeedMaixBit) self.boardCombobox.addItem(parameters.SipeedMaixBitMic) self.boardCombobox.addItem(parameters.SipeedMaixduino) self.boardCombobox.addItem(parameters.SipeedMaixGo) self.boardCombobox.addItem(parameters.SipeedMaixGoD) self.boardCombobox.addItem(parameters.KendryteKd233) self.boardCombobox.addItem(parameters.kendryteTrainer) self.boardCombobox.addItem(parameters.Auto) self.burnPositionLabel = QLabel(tr("BurnTo")) self.burnPositionCombobox = ComboBox() self.burnPositionCombobox.addItem(tr("Flash")) self.burnPositionCombobox.addItem(tr("SRAM")) boardSettingsLayout.addWidget(self.boardLabel, 0, 0) boardSettingsLayout.addWidget(self.boardCombobox, 0, 1) boardSettingsLayout.addWidget(self.burnPositionLabel, 1, 0) boardSettingsLayout.addWidget(self.burnPositionCombobox, 1, 1) # widgets serial settings serialSettingsGroupBox = QGroupBox(tr("SerialSettings")) serialSettingsLayout = QGridLayout() serialPortLabek = QLabel(tr("SerialPort")) serailBaudrateLabel = QLabel(tr("SerialBaudrate")) self.serialPortCombobox = ComboBox() self.serailBaudrateCombobox = ComboBox() self.serailBaudrateCombobox.addItem("115200") self.serailBaudrateCombobox.addItem("921600") self.serailBaudrateCombobox.addItem("1500000") self.serailBaudrateCombobox.addItem("2000000") self.serailBaudrateCombobox.addItem("3500000") self.serailBaudrateCombobox.addItem("4000000") self.serailBaudrateCombobox.addItem("4500000") self.serailBaudrateCombobox.setCurrentIndex(1) self.serailBaudrateCombobox.setEditable(True) serialSettingsLayout.addWidget(serialPortLabek, 0, 0) serialSettingsLayout.addWidget(serailBaudrateLabel, 1, 0) serialSettingsLayout.addWidget(self.serialPortCombobox, 0, 1) serialSettingsLayout.addWidget(self.serailBaudrateCombobox, 1, 1) serialSettingsGroupBox.setLayout(serialSettingsLayout) settingLayout.addWidget(serialSettingsGroupBox) # set stretch settingLayout.setStretch(0, 1) settingLayout.setStretch(1, 1) settingLayout.setStretch(2, 2) # widgets progress bar self.progressbar = QProgressBar(self.progressbarRootWidget) self.progressbar.setGeometry(10, 0, 400, 40) self.progressbar.setValue(0) self.progressbarRootWidget.hide() # widgets download area self.downloadButton = QPushButton(tr("Download")) downloadLayout.addWidget(self.downloadButton) # main window self.statusBarStauts = QLabel() self.statusBarStauts.setMinimumWidth(80) self.statusBarStauts.setText("<font color=%s>%s</font>" % ("#1aac2d", tr("DownloadHint"))) self.statusBar().addWidget(self.statusBarStauts) self.resize(400, 550) self.MoveToCenter() self.setWindowTitle(parameters.appName + " V" + str(helpAbout.versionMajor) + "." + str(helpAbout.versionMinor)) icon = QIcon() print("icon path:" + self.DataPath + "/" + parameters.appIcon) icon.addPixmap(QPixmap(self.DataPath + "/" + parameters.appIcon), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) if sys.platform == "win32": ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( parameters.appName) self.show() print("config file path:", os.getcwd() + "/" + parameters.configFilePath) def initEvent(self): self.serialPortCombobox.clicked.connect(self.portComboboxClicked) self.errorSignal.connect(self.errorHint) self.hintSignal.connect(self.hint) self.downloadResultSignal.connect(self.downloadResult) self.showSerialComboboxSignal.connect(self.showCombobox) self.updateProgressSignal.connect(self.updateProgress) self.updateProgressPrintSignal.connect(self.updateProgressPrint) self.langButton.clicked.connect(self.langChange) self.skinButton.clicked.connect(self.skinChange) self.aboutButton.clicked.connect(self.showAbout) self.openFileButton.clicked.connect(self.selectFile) self.downloadButton.clicked.connect(self.download) self.myObject = MyClass(self) slotLambda = lambda: self.indexChanged_lambda(self.myObject) self.serialPortCombobox.currentIndexChanged.connect(slotLambda) def setFrameStrentch(self, mode): if mode == 0: self.frameLayout.setStretch(0, 1) self.frameLayout.setStretch(1, 3) self.frameLayout.setStretch(2, 3) self.frameLayout.setStretch(3, 1) self.frameLayout.setStretch(4, 1) self.frameLayout.setStretch(5, 1) else: self.frameLayout.setStretch(0, 0) self.frameLayout.setStretch(1, 0) self.frameLayout.setStretch(2, 1) self.frameLayout.setStretch(3, 1) self.frameLayout.setStretch(4, 1) self.frameLayout.setStretch(5, 1) # @QtCore.pyqtSlot(str) def indexChanged_lambda(self, obj): mainObj = obj.arg self.serialPortCombobox.setToolTip( mainObj.serialPortCombobox.currentText()) def portComboboxClicked(self): self.detectSerialPort() def MoveToCenter(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def selectFile(self): oldPath = self.filePathWidget.text() if oldPath == "": oldPath = os.getcwd() fileName_choose, filetype = QFileDialog.getOpenFileName( self, tr("SelectFile"), oldPath, "All Files (*);;bin Files (*.bin);;k210 packages (*.kfpkg)" ) # 设置文件扩展名过滤,用双分号间隔 if fileName_choose == "": return if not self.checkFileName(fileName_choose): self.errorSignal.emit(tr("Error"), tr("FileTypeError")) return self.filePathWidget.setText(fileName_choose) def errorHint(self, title, str): QMessageBox.critical(self, title, str) def hint(self, title, str): QMessageBox.information(self, title, str) def findSerialPort(self): self.port_list = list(serial.tools.list_ports.comports()) return self.port_list def portChanged(self): self.serialPortCombobox.setCurrentIndex(0) self.serialPortCombobox.setToolTip(str(self.portList[0])) def detectSerialPort(self): if not self.isDetectSerialPort: self.isDetectSerialPort = True t = threading.Thread(target=self.detectSerialPortProcess) t.setDaemon(True) t.start() def showCombobox(self): self.serialPortCombobox.showPopup() def checkFileName(self, name): if not name.endswith(".bin") and not name.endswith(".kfpkg"): return False if not os.path.exists(name): return False return True def detectSerialPortProcess(self): while (1): portList = self.findSerialPort() if len(portList) > 0: currText = self.serialPortCombobox.currentText() self.serialPortCombobox.clear() for i in portList: showStr = str(i[0]) + " (" + str(i[1]) + ")" self.serialPortCombobox.addItem(showStr) index = self.serialPortCombobox.findText(currText) if index >= 0: self.serialPortCombobox.setCurrentIndex(index) else: self.serialPortCombobox.setCurrentIndex(0) break time.sleep(1) self.showSerialComboboxSignal.emit() self.isDetectSerialPort = False def programExitSaveParameters(self): paramObj = paremeters_save.ParametersToSave() paramObj.filePath = self.filePathWidget.text() paramObj.board = self.boardCombobox.currentText() paramObj.burnPosition = self.burnPositionCombobox.currentText() paramObj.baudRate = self.serailBaudrateCombobox.currentIndex() paramObj.skin = self.param.skin paramObj.language = translation.current_lang f = open(parameters.configFilePath, "wb") f.truncate() pickle.dump(paramObj, f) f.close() def programStartGetSavedParameters(self): paramObj = paremeters_save.ParametersToSave() try: f = open(parameters.configFilePath, "rb") paramObj = pickle.load(f) f.close() except Exception as e: f = open(parameters.configFilePath, "wb") f.close() translation.setLanguage(paramObj.language) self.param = paramObj def updateFrameParams(self): self.filePathWidget.setText(self.param.filePath) self.boardCombobox.setCurrentText(self.param.board) self.burnPositionCombobox.setCurrentText(self.param.burnPosition) self.serailBaudrateCombobox.setCurrentIndex(self.param.baudRate) def closeEvent(self, event): self.programExitSaveParameters() def langChange(self): if self.param.language == translation.language_en: translation.setLanguage(translation.language_zh) lang = tr("Chinese language") else: translation.setLanguage(translation.language_en) lang = tr("English language") self.hint( tr("Hint"), tr("Language Changed to ") + lang + "\n" + tr("Reboot to take effect")) self.frameWidget.style().unpolish(self.downloadButton) self.frameWidget.style().polish(self.downloadButton) self.frameWidget.update() def skinChange(self): if self.param.skin == 1: # light file = open(self.DataPath + '/assets/qss/style-dark.qss', "r") self.param.skin = 2 else: # elif self.param.skin == 2: # dark file = open(self.DataPath + '/assets/qss/style.qss', "r") self.param.skin = 1 self.app.setStyleSheet(file.read().replace("$DataPath", self.DataPath)) def showAbout(self): QMessageBox.information( self, tr("About"), "<h1 style='color:#f75a5a';margin=10px;>" + parameters.appName + '</h1><br><b style="color:#08c7a1;margin = 5px;">V' + str(helpAbout.versionMajor) + "." + str(helpAbout.versionMinor) + "." + str(helpAbout.versionDev) + "</b><br><br>" + helpAbout.date + "<br><br>" + helpAbout.strAbout()) def autoUpdateDetect(self): auto = autoUpdate.AutoUpdate() if auto.detectNewVersion(): auto.OpenBrowser() def openDevManagement(self): os.system('start devmgmt.msc') def updateProgress(self, fileTypeStr, current, total, speedStr): currBurnPos = self.burnPositionCombobox.currentText() if currBurnPos == tr("SRAM") or currBurnPos == tr_en("SRAM"): fileTypeStr = tr("ToSRAM") percent = current / float(total) * 100 hint = "<font color=%s>%s %s:</font> <font color=%s> %.2f%%</font> <font color=%s> %s</font>" % ( "#ff7575", tr("Downloading"), fileTypeStr, "#2985ff", percent, "#1aac2d", speedStr) self.progressHint.setText(hint) self.progressbar.setValue(percent) def updateProgressPrint(self, str): self.statusBarStauts.setText(str) def kflash_py_printCallback(self, *args, end="\n"): msg = "" for i in args: msg += str(i) msg.replace("\n", " ") self.updateProgressPrintSignal.emit(msg) def progress(self, fileTypeStr, current, total, speedStr): self.updateProgressSignal.emit(fileTypeStr, current, total, speedStr) def download(self): if self.burning: self.terminateBurn() return self.burning = True filename = self.filePathWidget.text() if not self.checkFileName(filename): self.errorSignal.emit(tr("Error"), tr("FilePathError")) self.burning = False return color = False board = "dan" boardText = self.boardCombobox.currentText() if boardText == parameters.SipeedMaixGo: board = "goE" elif boardText == parameters.SipeedMaixGoD: board = "goD" elif boardText == parameters.SipeedMaixduino: board = "maixduino" elif boardText == parameters.SipeedMaixBit: board = "bit" elif boardText == parameters.SipeedMaixBitMic: board = "bit_mic" elif boardText == parameters.KendryteKd233: board = "kd233" elif boardText == parameters.kendryteTrainer: board = "trainer" elif boardText == parameters.Auto: board = None sram = False if self.burnPositionCombobox.currentText()==tr("SRAM") or \ self.burnPositionCombobox.currentText()==tr_en("SRAM"): sram = True try: baud = int(self.serailBaudrateCombobox.currentText()) except Exception: self.errorSignal.emit(tr("Error"), tr("BaudrateError")) self.burning = False return dev = self.serialPortCombobox.currentText().split()[0] if dev == "": self.errorSignal.emit(tr("Error"), tr("PleaseSelectSerialPort")) self.burning = False return # hide setting widgets self.setFrameStrentch(1) self.settingWidget.hide() self.progressbar.setValue(0) self.progressbarRootWidget.show() self.progressHint.show() self.downloadButton.setText(tr("Cancel")) self.downloadButton.setProperty("class", "redbutton") self.downloadButton.style().unpolish(self.downloadButton) self.downloadButton.style().polish(self.downloadButton) self.downloadButton.update() self.statusBarStauts.setText("<font color=%s>%s ...</font>" % ("#1aac2d", tr("Downloading"))) hint = "<font color=%s>%s</font>" % ("#ff0d0d", tr("DownloadStart")) self.progressHint.setText(hint) # download self.burnThread = threading.Thread(target=self.flashBurnProcess, args=( dev, baud, board, sram, filename, self.progress, color, )) self.burnThread.setDaemon(True) self.burnThread.start() def flashBurnProcess(self, dev, baud, board, sram, filename, callback, color): success = True errMsg = "" try: if board: self.kflash.process(terminal=False, dev=dev, baudrate=baud, board=board, sram=sram, file=filename, callback=callback, noansi=not color) else: self.kflash.process(terminal=False, dev=dev, baudrate=baud, sram=sram, file=filename, callback=callback, noansi=not color) except Exception as e: errMsg = str(e) if str(e) != "Burn SRAM OK": success = False if success: self.downloadResultSignal.emit(True, errMsg) else: self.downloadResultSignal.emit(False, errMsg) def downloadResult(self, success, msg): if success: self.hintSignal.emit(tr("Success"), tr("DownloadSuccess")) self.statusBarStauts.setText("<font color=%s>%s</font>" % ("#1aac2d", tr("DownloadSuccess"))) else: if msg == "Cancel": self.statusBarStauts.setText( "<font color=%s>%s</font>" % ("#ff1d1d", tr("DownloadCanceled"))) else: msg = tr("ErrorSettingHint") + "\n\n" + msg self.errorSignal.emit(tr("Error"), msg) self.statusBarStauts.setText("<font color=%s>%s</font>" % ("#ff1d1d", tr("DownloadFail"))) self.progressHint.setText("") self.downloadButton.setText(tr("Download")) self.downloadButton.setProperty("class", "normalbutton") self.downloadButton.style().unpolish(self.downloadButton) self.downloadButton.style().polish(self.downloadButton) self.downloadButton.update() self.setFrameStrentch(0) self.progressbarRootWidget.hide() self.progressHint.hide() self.settingWidget.show() self.burning = False def terminateBurn(self): hint = "<font color=%s>%s</font>" % ("#ff0d0d", tr("DownloadCanceling")) self.progressHint.setText(hint) self.kflash.kill()