diff --git a/.claude/settings.local.json b/.claude/settings.local.json index fc3cf9e..7e1c1b0 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -4,7 +4,9 @@ "Bash(python3 -c ' *)", "Bash(sqlite3 data.db \"PRAGMA table_info\\(task_instruction\\);\")", "Bash(venv/Scripts/python.exe -c ' *)", - "Bash(venv/Scripts/python.exe -c \"import py_compile; py_compile.compile\\('taskForm.py', doraise=True\\)\")" + "Bash(venv/Scripts/python.exe -c \"import py_compile; py_compile.compile\\('taskForm.py', doraise=True\\)\")", + "Bash(venv/Scripts/pip list *)", + "Bash(venv/Scripts/pip install *)" ] } } diff --git a/common.py b/common.py index 80db2c4..be61703 100644 --- a/common.py +++ b/common.py @@ -432,11 +432,12 @@ class Common(QObject): self.scanfResult = msg @pyqtSlot(str,str,str,str, result=bool) + @pyqtSlot(str, str, str, str, result=bool) def initAppScript(self, appId, instancName, scriptFile, plusFile="plus"): try: if dog.isDogAlive() == 1 or suDog.isDogAlive() == 1: scriptPath = os.path.join(self.appPath,"qml",plusFile,appId,"script", scriptFile) - with open(scriptPath, "r") as f: + with open(scriptPath, "r") as f: script = f.read() exec(script, self.qscript) if self.qscript != {}: @@ -447,6 +448,10 @@ class Common(QObject): print(e) return False + @pyqtSlot(str, result=QVariant) + def getScript(self, name): + return self.qscript.get(name, None) + @pyqtSlot(str,str,str,str,str, result=bool) def registAppQml(self, appId, instancName, className, scriptFile, plusFile="plus"): try: @@ -915,6 +920,24 @@ class Common(QObject): # export_table_structure_and_data('data.db', 'task_group') # export_table_structure_and_data('data.db', 'task_instruction') # 导入表结构和数据 + @pyqtSlot(str, result=str) + def readLogFile(self, file_path): + try: + with open(file_path, 'r', encoding='utf-8') as f: + return f.read() + except Exception as e: + return f"Error reading file {file_path}: {e}" + + @pyqtSlot(str, result=list) + def getTxtFiles(self, directory): + try: + path = directory.replace("file:///", "") + if not os.path.exists(path): + return [] + return [os.path.join(path, f) for f in sorted(os.listdir(path)) if os.path.isfile(os.path.join(path, f))] + except Exception: + return [] + def migrate_sql_to_database(self, db_path, table_name): import stat try: diff --git a/interfaceSession/sessionSerial.py b/interfaceSession/sessionSerial.py index b9d2d2b..9a6164a 100644 --- a/interfaceSession/sessionSerial.py +++ b/interfaceSession/sessionSerial.py @@ -94,7 +94,7 @@ class SessionSerial(SessionAbstract): self.thread = QThread() self.worker.moveToThread(self.thread) self.thread.started.connect(self.worker.start) - self.worker.newDataArrive.connect(self.onNewDataArrive) + self.worker.newDataArrive.connect(self.onNewDataArrive, type=Qt.ConnectionType.DirectConnection) self.worker.connectSuccess.connect(self.onConnectSuccess) self.worker.connectClosed.connect(self.onConnectClosed) self.worker.connectFailed.connect(self.onConnectFailed) diff --git a/interfaceSession/sessionTbus.py b/interfaceSession/sessionTbus.py index 6e45dfe..4feffa6 100644 --- a/interfaceSession/sessionTbus.py +++ b/interfaceSession/sessionTbus.py @@ -81,7 +81,7 @@ class SessionTbus(SessionAbstract): self.thread = QThread() self.worker.moveToThread(self.thread) self.thread.started.connect(self.worker.start) - self.worker.newDataArrive.connect(self.onNewDataArrive) + self.worker.newDataArrive.connect(self.onNewDataArrive, type=Qt.ConnectionType.DirectConnection) self.worker.connectSuccess.connect(self.onConnectSuccess) self.worker.connectClosed.connect(self.onConnectClosed) self.worker.connectFailed.connect(self.onConnectFailed) diff --git a/interfaceSession/sessionTbusNs.py b/interfaceSession/sessionTbusNs.py index 82ec789..10f8314 100644 --- a/interfaceSession/sessionTbusNs.py +++ b/interfaceSession/sessionTbusNs.py @@ -82,7 +82,7 @@ class SessionTbusNs(SessionAbstract): self.thread = QThread() self.worker.moveToThread(self.thread) self.thread.started.connect(self.worker.start) - self.worker.newDataArrive.connect(self.onNewDataArrive) + self.worker.newDataArrive.connect(self.onNewDataArrive, type=Qt.ConnectionType.DirectConnection) self.worker.connectSuccess.connect(self.onConnectSuccess) self.worker.connectClosed.connect(self.onConnectClosed) self.worker.connectFailed.connect(self.onConnectFailed) diff --git a/mainWindow.py b/mainWindow.py index f00bb90..ee0733b 100644 --- a/mainWindow.py +++ b/mainWindow.py @@ -104,7 +104,7 @@ class MainWindow(QMainWindow): self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.tbDefault.setVisible(False) - self.isDebugQML = False #DEBUG + self.isDebugQML = True #DEBUG self.setWindowTitle("TG-TestingPlatform "+app_version.data["version"]) self.ui.lbtitle.setText("TG-TestingPlatform "+app_version.data["version"]) self.ui.logWidget.setVisible(True) @@ -234,7 +234,6 @@ class MainWindow(QMainWindow): self.ui.quickTaskbar.setSource(QUrl(common.firstPath()+"/TaskBar.qml")) self.ui.quickStackView.setSource(QUrl(common.firstPath()+"/main.qml")) self.ui.quickSetting.setSource(QUrl(common.firstPath()+"/SettingView.qml")) - if self.isDebugQML: self.setDebugQML() @@ -249,7 +248,7 @@ class MainWindow(QMainWindow): self.ui.quickDebug.rootContext().setContextProperty("taskManager", taskManager) self.ui.quickDebug.rootContext().setContextProperty("interfaceManager", interfaceManager) self.ui.quickDebug.rootContext().setContextProperty("common", common) - self.ui.quickDebug.setSource(QUrl(common.firstPath()+"/debug/gviewer/main.qml")) + self.ui.quickDebug.setSource(QUrl(common.firstPath()+"/debug/calibrate/main.qml")) def clearContextProperties(self): # 清除QML引擎缓存 diff --git a/qml/debug/calibrate/main.qml b/qml/debug/calibrate/main.qml index 2063654..aac863c 100644 --- a/qml/debug/calibrate/main.qml +++ b/qml/debug/calibrate/main.qml @@ -15,6 +15,7 @@ Rectangle property var g_sn: "8441" property var appPath: "/"+plusFile+"/"+appId+"/" property var firstPath: "" + property var calibrate: null property var startDate: new Date() property var startTime: startDate.getTime() property var endTime: startDate.getTime() @@ -54,6 +55,7 @@ Rectangle firstPath = common.firstPath() g_user = common.getCurrentUser() common.initAppScript(appId, "calibrate", "calibrate.py", plusFile) + calibrate = common.getScript("calibrate") setConfigInfo() calibrate.startShima(firstPath + appPath) } diff --git a/qml/debug/gviewer/Explorer.qml b/qml/debug/gviewer/Explorer.qml index 5a4a51d..5ac30f6 100644 --- a/qml/debug/gviewer/Explorer.qml +++ b/qml/debug/gviewer/Explorer.qml @@ -5,8 +5,6 @@ import QtQuick.Dialogs // import QtQuick.Controls. import "./app.js" as App import "./common" -import Qt.labs.folderlistmodel - Rectangle { id: root radius: 9 diff --git a/qml/debug/logviewer/main.qml b/qml/debug/logviewer/main.qml index f8eca54..43de76f 100644 --- a/qml/debug/logviewer/main.qml +++ b/qml/debug/logviewer/main.qml @@ -5,7 +5,6 @@ import QtQuick.Dialogs // import QtQuick.Controls. import "./app.js" as App import "./common" -import Qt.labs.folderlistmodel Rectangle { id: root @@ -55,6 +54,24 @@ Rectangle { GradientStop { position: 1.0; color: "#b7d6fb" } orientation: Gradient.Horizontal } + ListModel { id: fileListModel } + + function refreshFileList() { + fileListModel.clear() + if (logDirectory) { + var files = common.getTxtFiles(logDirectory) + for (var i = 0; i < files.length; i++) { + var fp = files[i].toString() + fileListModel.append({ + "fileName": fp.substring(fp.lastIndexOf('/') + 1).replace(/\\/g, '/').split('/').pop(), + "filePath": fp + }) + } + } + } + + onLogDirectoryChanged: { refreshFileList() } + ListModel{ id: logModel } @@ -138,24 +155,16 @@ Rectangle { boundsBehavior: Flickable.StopAtBounds ScrollBar.vertical: ScrollBar{} anchors.margins: 5 - model: FolderListModel { - id: folderModel - rootFolder: logDirectory - showDirsFirst: true - showFiles: true - folder: logDirectory // 显式设置初始路径 - } + model: fileListModel delegate: ItemDelegate { text: model.fileName icon.source: "resource/file.svg" background: Rectangle { color: fileSystemView.currentIndex == index ? "#D9D9D9" : "white" - // 代理项的其他内容... } onClicked: { - console.info("Clicked on: " + model.fileName) currentLogfilePath = model.filePath - currentLogResult = log_processor.read_file(currentLogfilePath) + currentLogResult = common.readLogFile(currentLogfilePath) updateLogDisplay() fileSystemView.currentIndex = index } diff --git a/taskForm.py b/taskForm.py index a1a5e59..e6d4d39 100644 --- a/taskForm.py +++ b/taskForm.py @@ -565,8 +565,8 @@ class TaskForm(QWidget): row_id_index = self.taskGroupModel.index(row, 0, QModelIndex()) group_id = self.taskGroupModel.data(row_id_index, Qt.ItemDataRole.DisplayRole) self.current_groupId = group_id - self.loadTask() self.clearChildrenModel() + QTimer.singleShot(0, self.loadTask) #任务列表选中 def handleTaskSelection(self, index): @@ -955,6 +955,9 @@ class TaskForm(QWidget): if str(taskId) == str(obj.get('task_instruction_id')): self.taskModelBlockSignals = True item.setData({'value': value, 'maximum': maxValue, 'task_instruction_id': obj.get('task_instruction_id'), 'name': obj.get('name')}, Qt.ItemDataRole.UserRole) + self.taskModelBlockSignals = False + self.ui.tableViewTask.viewport().update() + return def _onTaskStop(self, taskId): """任务停止时清理缓存和进度显示""" taskId = str(taskId) diff --git a/taskModel/taskActuator.py b/taskModel/taskActuator.py index 94d9844..084c55c 100644 --- a/taskModel/taskActuator.py +++ b/taskModel/taskActuator.py @@ -87,7 +87,7 @@ class TaskActuator(QThread): loop = int(self.taskInfo.get("loop", 1)) delay = float(int(self.taskInfo.get("delay",0))/1000) taskIndex = 0 - total = instructionListLength * loop + total = instructionListLength * loop if loop > 0 else instructionListLength self.updateProgress.emit(self.id, 0, total) self.updateDetails.emit(self.id, 0, total) self.updateDetails.emit(self.parentId, 0, total) diff --git a/taskModel/taskActuatorManager.py b/taskModel/taskActuatorManager.py index a3df92a..cc0b333 100644 --- a/taskModel/taskActuatorManager.py +++ b/taskModel/taskActuatorManager.py @@ -11,6 +11,30 @@ from taskInstructionModel.taskInstructionManager import taskInstructionManager from taskModel.taskManager import taskManager from common import common +class _KillThread(QThread): + done = pyqtSignal(object) # 传 actuator 回主线程处理 + + def __init__(self, actuator): + super().__init__() + self.actuator = actuator + self.finished.connect(self.deleteLater) + + def run(self): + a = self.actuator + if not a.wait(500): + for item in a.instructionList: + try: + iface = item.get("interface") + if iface: + from interfaceSession.interfaceManager import interfaceManager + interfaceManager.getSession(iface).unlock() + except Exception: + pass + a.terminate() + a.wait(200) + self.done.emit(a) + + class TaskActuatorManager(QObject): logMsg = pyqtSignal(dict) taskStart = pyqtSignal(str) @@ -100,7 +124,6 @@ class TaskActuatorManager(QObject): a for tid, a in list(self.taskActuatorDict.items()) if a.isRunning() and not (excludeTaskId and str(tid) == str(excludeTaskId)) ] - # 先全部设置停止标志 for a in actuators: a.running_lock.lock() a.running = False @@ -111,25 +134,20 @@ class TaskActuatorManager(QObject): a.subTaskActuator.running_lock.lock() a.subTaskActuator.running = False a.subTaskActuator.running_lock.unlock() - # 再统一等待/强杀 - for a in actuators: a.quit() - if not a.wait(500): - for item in a.instructionList: - try: - iface = item.get("interface") - if iface: - from interfaceSession.interfaceManager import interfaceManager - interfaceManager.getSession(iface).unlock() - except Exception: - pass - a.terminate() - a.wait(200) - a.updateDetails.emit(a.id, -1, -1) - a.taskStop.emit(a.id) - log.info(f"任务 {a.id} 已停止") + kt = _KillThread(a) + kt.done.connect(self._onKillDone) + self._killThreads = getattr(self, '_killThreads', []) + self._killThreads.append(kt) + kt.start() return len(actuators) + def _onKillDone(self, a): + a.updateDetails.emit(a.id, -1, -1) + a.taskStop.emit(a.id) + log.info(f"任务 {a.id} 已停止") + self._killThreads = [t for t in getattr(self, '_killThreads', []) if t.isRunning()] + def requestStopAll(self, requesterTaskId=None): """请求停止所有任务(由主程序处理)""" print(f"[DEBUG] requestStopAll 被调用,请求者: {requesterTaskId}")