import sys import os import re import json import base64 import time from PyQt6 import * from PyQt6.QtCore import * from PyQt6.QtGui import * from PyQt6.QtWidgets import * from PyQt6.QtQuick import * from PyQt6.QtQml import * from projectForm import ProjectForm from devModelForm import DevModelForm from deviceForm import DeviceForm from taskForm import TaskForm from dataSyncForm import DataSyncForm from userForm import UserForm from logForm import LogForm from config import config from logs import log from influxDB import influxdb from grafana import grafana from taskListForm import TaskListForm from loginForm import LoginForm from interfaceSession.interfaceManager import interfaceManager from influxdbModel.influxdbManager import influxdbManager from deviceModel.deviceManager import deviceManager from taskModel.taskActuatorManager import taskActuatorManager from taskGroupModel.taskGroupManager import taskGroupManager from taskModel.taskManager import taskManager from instructionModel.instructionManager import instructionManager from projectModel.projectManager import projectManager from userModel.userManager import userManager from common import common from models import Session from version import app_version from ui.Ui_mainForm import Ui_MainWindow from dog import dog from dog import suDog import ui.res_rc class LogModel(QAbstractListModel): @pyqtSlot(str, str) def filter_logs(self, level, tag): self.beginResetModel() self.filtered_data = [log for log in self.all_logs if (not level or log["level"] == level) and (not tag or log["tag"] == tag)] self.endResetModel() class AsyncLoader(QThread): loaded = pyqtSignal() def run(self): self.loaded.emit() class MainWindow(QMainWindow): windowClose = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.initdataDb() self.loginForm = LoginForm() self.loginForm.show() self.setConfig() self._run() self.initWindow() def initdataDb(self): dataDB = os.path.join(os.path.dirname(__file__),'data.db') trigger_path = os.path.join(os.path.dirname(__file__),'sqls','trigger') dev_model_path = os.path.join(os.path.dirname(__file__),'sqls','dev_model') device_path = os.path.join(os.path.dirname(__file__),'sqls','device') device_group_path = os.path.join(os.path.dirname(__file__),'sqls','device_group') device_model_group_path = os.path.join(os.path.dirname(__file__),'sqls','device_model_group') public_path = os.path.join(os.path.dirname(__file__),'sqls','public') common.import_sql_to_database(dataDB, trigger_path) common.import_sql_to_database(dataDB, dev_model_path) common.import_sql_to_database(dataDB, device_path) common.import_sql_to_database(dataDB, device_group_path) common.import_sql_to_database(dataDB, device_model_group_path) common.import_sql_to_database(dataDB, public_path) Session.checkId() def closeEvent(self, event): self.windowClose.emit() self._exit() event.accept() # 接受关闭事件 def initWindow(self): self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.tbDefault.setVisible(False) self.isDebugQML = False #DEBUG self.setWindowTitle("TG-TestingPlatform "+app_version.data["version"]) self.ui.lbtitle.setText("TG-TestingPlatform "+app_version.data["version"]) self.ui.logWidget.setVisible(True) self.projectModel = QStandardItemModel() self.projectHistoryModel = QStandardItemModel() self.projectForm = ProjectForm() self.devModelWidget = DevModelForm() self.deviceWidget = DeviceForm() self.taskWidget = TaskForm() self.dataSyncForm = DataSyncForm() self.userWidget = UserForm() self.logWidget = LogForm() self.setWindowFlags(Qt.WindowType.FramelessWindowHint) # self.setWindowFlags(Qt.WindowType.WindowCloseButtonHint) self.setMinimumSize(800, 600) self.setMaximumSize(1920, 1080) # 设置最大大小 self.windowClose.connect(common.onWindowClose) self.ui.tbStopAll.clicked.connect(self.onStopAllClicked) # 连接 stopAllRequested 信号 taskActuatorManager.stopAllRequested.connect(self.onStopAllRequested) self.ui.verticalLayout_devModel.addWidget(self.devModelWidget) self.ui.verticalLayout_device.addWidget(self.deviceWidget) self.ui.verticalLayout_task.addWidget(self.taskWidget) self.ui.verticalLayout_dataSync.addWidget(self.dataSyncForm) self.ui.verticalLayout_pro.addWidget(self.projectForm) self.ui.verticalLayout_user.addWidget(self.userWidget) self.ui.verticalLayout_log.addWidget(self.logWidget) self.async_loader = AsyncLoader() self.async_loader.loaded.connect(self.setQtQuick) common.appReady.connect(self.appReady) common.registQml.connect(self.registQml) # 创建一个悬浮按钮 self.hide_button = QToolButton(self) self.taskListForm = TaskListForm(self) self.ui.toolBar.mousePressEvent = self.toolbar_mousePressEvent self.ui.toolBar.mouseMoveEvent = self.toolbar_mouseMoveEvent self.ui.toolBar.mouseReleaseEvent = self.toolbar_mouseReleaseEvent # 设置按钮的绝对位置 self.hide_button.setGeometry(95, int((self.height() - 30) /2), 30, 30) self.taskListForm.setGeometry(500, 420, 600, 500) self.taskListForm.setVisible(False) self.hide_button.setIcon(QIcon(":/resource/close.svg")) self.hide_button.setStyleSheet("border-radius: 12px;") self.hide_button.setIconSize(QSize(30, 30)) self.hide_button.setVisible(True) self.createMenu() self.connectBtnHandle() self.dogIndex = 1 self.dogErrorIndex = 0 self.timer = QTimer(self) self.timer.timeout.connect(self.updateDateTime) self.ui.pbMax.setVisible(False) self.timer.start(1000) # 设置定时器的时间间隔为1秒(1000毫秒) # 在窗口大小改变事件中设置TaskListForm的位置 self.installEventFilter(self) self.async_loader.start() #DEBUG if self.isDebugQML: pass else: self.ui.tbDebug.setVisible(False) def toolbar_mousePressEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: self.oldPos = event.globalPosition() def toolbar_mouseMoveEvent(self, event): if self.oldPos: delta = event.globalPosition() - self.oldPos new_pos = self.pos() + QPoint(delta.toPoint()) self.move(new_pos) self.oldPos = event.globalPosition() def toolbar_mouseReleaseEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: self.oldPos = None def eventFilter(self, obj, event): if event.type() == QEvent.Type.Resize: mainWinWidth = self.width() mainWinHeight = self.height() taskListFormHeight = self.taskListForm.height() # 确保列表不会超出主窗口底部 maxHeight = mainWinHeight - 100 # 留出顶部空间 if taskListFormHeight > maxHeight: taskListFormHeight = maxHeight self.taskListForm.setMinimumHeight(maxHeight) self.taskListForm.setGeometry( int((mainWinWidth - self.taskListForm.width()) / 2), # 水平居中 int(mainWinHeight - taskListFormHeight - 40), # 设置在底部 int(self.taskListForm.width()), int(taskListFormHeight) ) self.hide_button.setGeometry( self.hide_button.pos().x(), # 水平居中 int((mainWinHeight - self.hide_button.height()) /2), # 设置在底部 int(self.hide_button.width()), int(self.hide_button.height()) ) return super().eventFilter(obj, event) def appReady(self, instanceName): self.ui.quickStackView.rootContext().setContextProperty(instanceName, common.qscript[instanceName]) self.ui.quickDebug.rootContext().setContextProperty(instanceName, common.qscript[instanceName]) def registQml(self,instanceName, className): instance = common.qscript[instanceName] qmlRegisterType(instance.__class__, className, 1, 0, className) #qml资源加载 def setQtQuick(self): self.ui.quickSetting.rootContext().setContextProperty("common", common) self.ui.quickStackView.rootContext().setContextProperty("projectManager", projectManager) self.ui.quickStackView.rootContext().setContextProperty("taskActuatorManager", taskActuatorManager) self.ui.quickStackView.rootContext().setContextProperty("instructionManager", instructionManager) self.ui.quickStackView.rootContext().setContextProperty("deviceManager", deviceManager) self.ui.quickStackView.rootContext().setContextProperty("taskGroupManager", taskGroupManager) self.ui.quickStackView.rootContext().setContextProperty("taskManager", taskManager) self.ui.quickStackView.rootContext().setContextProperty("interfaceManager", interfaceManager) self.ui.quickStackView.rootContext().setContextProperty("common", common) self.ui.quickTaskbar.rootContext().setContextProperty("common", common) self.ui.quickSetting.rootContext().setContextProperty("interfaceManager", interfaceManager) self.ui.quickSetting.rootContext().setContextProperty("influxdbManager", influxdbManager) 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() def setDebugQML(self): self.ui.quickDebug.engine().clearComponentCache() self.ui.quickDebug.rootContext().setContextProperty("logModel", LogModel()) self.ui.quickDebug.rootContext().setContextProperty("projectManager", projectManager) self.ui.quickDebug.rootContext().setContextProperty("taskActuatorManager", taskActuatorManager) self.ui.quickDebug.rootContext().setContextProperty("instructionManager", instructionManager) self.ui.quickDebug.rootContext().setContextProperty("deviceManager", deviceManager) self.ui.quickDebug.rootContext().setContextProperty("taskGroupManager", taskGroupManager) 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")) def clearContextProperties(self): # 清除QML引擎缓存 self.ui.quickSetting.engine().clearComponentCache() self.ui.quickStackView.engine().clearComponentCache() # self.ui.quickSetting.rootContext().setContextProperty("common", None) # self.ui.quickStackView.rootContext().setContextProperty("projectManager", None) # self.ui.quickStackView.rootContext().setContextProperty("taskActuatorManager", None) # self.ui.quickStackView.rootContext().setContextProperty("instructionManager", None) # self.ui.quickStackView.rootContext().setContextProperty("deviceManager", None) # self.ui.quickStackView.rootContext().setContextProperty("interfaceManager", None) # self.ui.quickStackView.rootContext().setContextProperty("common", None) # self.ui.quickTaskbar.rootContext().setContextProperty("common", None) # self.ui.quickSetting.rootContext().setContextProperty("interfaceManager", None) # self.ui.quickSetting.rootContext().setContextProperty("influxdbManager", None) # self.ui.quickTaskbar.setSource(QUrl("")) # self.ui.quickStackView.setSource(QUrl("")) # self.ui.quickSetting.setSource(QUrl("")) # if self.isDebugQML: # self.ui.quickDebug.rootContext().setContextProperty("projectManager", None) # self.ui.quickDebug.rootContext().setContextProperty("taskActuatorManager", None) # self.ui.quickDebug.rootContext().setContextProperty("instructionManager", None) # self.ui.quickDebug.rootContext().setContextProperty("deviceManager", None) # self.ui.quickDebug.rootContext().setContextProperty("interfaceManager", None) # self.ui.quickDebug.rootContext().setContextProperty("common", None) # self.ui.quickDebug.setSource(QUrl("")) #连接按钮信号 def connectBtnHandle(self): self.ui.tbModel.clicked.connect(self.showDevModelForm) self.ui.tbDev.clicked.connect(self.showDeviceForm) self.ui.tbTask.clicked.connect(self.showTaskForm) self.ui.tbDataSync.clicked.connect(self.showDataSyncForm) self.ui.tbAppStore.clicked.connect(self.showAppStore) self.ui.tbProject.clicked.connect(self.showProject) self.ui.tbUser.clicked.connect(self.showUser) self.ui.tbSetting.clicked.connect(self.showSetting) self.loginForm.loginSuccess.connect(self.login) self.loginForm.loginClose.connect(self.loginClose) self.ui.tbDebug.clicked.connect(self.showDebug) projectManager.sig_projectChanged.connect(self.changeProject) common.sig_changeQml.connect(self.onChangeQml) self.hide_button.clicked.connect(self.showHide) self.ui.tbLog.clicked.connect(self.logControl ) self.ui.tbTaskList.clicked.connect(self.showTaskListForm) self.ui.tblogOut.clicked.connect(self.logOut) self.ui.pbClose.clicked.connect(self.closeWind) self.ui.pbMin.clicked.connect(self.showMin) self.ui.pbMax.clicked.connect(self.showMax) self.ui.pbRestore.clicked.connect(self.showNormal) interfaceManager.connectFailed.connect(self.connectFailed) def showNormal(self): self.setWindowState(Qt.WindowState.WindowNoState) self.ui.pbMax.setVisible(True) self.ui.pbRestore.setVisible(False) def showMin(self): self.showMinimized() def showMax(self): self.setWindowState(Qt.WindowState.WindowMaximized) self.ui.pbMax.setVisible(False) self.ui.pbRestore.setVisible(True) def closeWind(self): self.dataSyncForm.cleanup() self.close() def loginClose(self): self.windowClose.emit() self._exit() def onChangeQml(self, qml): self.ui.stackedWidget.setCurrentIndex(4) self.ui.tbDefault.setChecked(True) def showDebug(self): self.ui.stackedWidget.setCurrentIndex(8) self.setDebugQML() common.sig_unChecked.emit() def showDevModelForm(self): self.ui.stackedWidget.setCurrentIndex(0) self.devModelWidget.refreshSelect() common.sig_unChecked.emit() def showDeviceForm(self): self.ui.stackedWidget.setCurrentIndex(1) self.deviceWidget.refreshSelect() common.sig_unChecked.emit() def showTaskForm(self): self.ui.stackedWidget.setCurrentIndex(2) self.taskWidget.refreshSelect() common.sig_unChecked.emit() def showDataSyncForm(self): self.ui.stackedWidget.setCurrentIndex(3) common.sig_unChecked.emit() def showAppStore(self): common.sig_changeMainQml.emit("./AppStore.qml") self.ui.stackedWidget.setCurrentIndex(4) common.sig_unChecked.emit() def showSetting(self): self.ui.stackedWidget.setCurrentIndex(5) common.sig_unChecked.emit() def showProject(self): self.ui.stackedWidget.setCurrentIndex(6) self.projectForm.refreshSelect() common.sig_unChecked.emit() def showUser(self): self.ui.stackedWidget.setCurrentIndex(7) self.userWidget.refreshSelect() common.sig_unChecked.emit() # 创建菜单 def createMenu(self): menu = QMenu(self) # 创建菜单项 # action1 = QAction("系统设置", self) action1 = QAction("切换用户", self) action2 = QAction("帮助文档", self) action3 = QAction("关于", self) # 连接QAction的triggered信号到槽函数 # action1.triggered.connect(self.showSetting) action3.triggered.connect(self.showAboutDialog) # 将菜单项添加到菜单中 # menu.addAction(action1) menu.addAction(action1) menu.addAction(action2) menu.addAction(action3) # 设置工具按钮的触发事件(点击时显示菜单) # lambda: menu.exec(self.ui.tbMore.mapToGlobal(QPoint(40, -30))) self.ui.tbAbout.clicked.connect(self.showAboutDialog) def logOut(self): userManager.logOut() self.hide() self.loginForm.showNormal() def changeProject(self, proId): if proId == common.emputyProId: self.ui.lbCurrentPro.setText("") return info = projectManager.getInfo(proId) self.ui.lbCurrentPro.setText(info["name"]) def showHide(self): self.ui.leftFrame.setVisible( not self.ui.leftFrame.isVisible()) if self.ui.leftFrame.isVisible(): self.hide_button.setGeometry(95, int((self.height() - 30) /2), 30, 30) self.hide_button.setIcon(QIcon(":/resource/close.svg")) else: self.hide_button.setGeometry(0, int((self.height() - 30) /2), 30, 30) self.hide_button.setIcon(QIcon(":/resource/open.svg")) def connectFailed(self): reply = QMessageBox.critical(None, "错误", "连接失败", QMessageBox.StandardButton.Ok) def updateDateTime(self): self.dogIndex = self.dogIndex + 1 if self.dogIndex >= 10: self.dogIndex = 1 if dog.isDogAlive() != 1 : if suDog.isDogAlive() != 1: self.dogErrorIndex += 1 log.error("error index: %d" % self.dogErrorIndex) else: self.dogErrorIndex = 0 else: self.dogErrorIndex = 0 if self.dogErrorIndex >= 3: self.timer.stop() reply = QMessageBox.critical(None, "Error", "请插入加密狗", QMessageBox.StandardButton.Ok) if reply == QMessageBox.StandardButton.Ok: sys.exit(1) self.ui.lbDateTime.setText(QDateTime.currentDateTime().toString("yyyy-MM-dd HH:mm:ss") ) def login(self): loginData = userManager.getCurrentUser() if "name" not in loginData: return self.ui.lbCurrentUserName.setText(loginData["name"]) if "role" in loginData: self.setRoleEnable(loginData["role"]) else: self.setRoleEnable(1) self.showMaximized() #DEBUG if self.isDebugQML: self.ui.stackedWidget.setCurrentIndex(8) def setRoleEnable(self, role): if role == 0: self.ui.tbModel.setVisible(True) self.ui.tbAppStore.setVisible(True) self.ui.tbUser.setVisible(True) self.ui.tbDev.setVisible(True) self.ui.tbTask.setVisible(True) self.ui.tbDataSync.setVisible(True) self.ui.tbProject.setVisible(True) self.ui.tbAbout.setVisible(True) self.ui.tbSetting.setVisible(True) self.ui.tbModel.setChecked(True) self.showDevModelForm() elif role == 1: self.ui.tbModel.setVisible(False) self.ui.tbAppStore.setVisible(False) self.ui.tbUser.setVisible(False) self.ui.tbDev.setVisible(True) self.ui.tbDev.setChecked(True) self.showDeviceForm() elif role == 2: self.ui.tbModel.setVisible(False) self.ui.tbAppStore.setVisible(False) self.ui.tbUser.setVisible(False) self.ui.tbDev.setVisible(False) self.ui.tbTask.setVisible(False) self.ui.tbDataSync.setVisible(False) self.ui.tbProject.setChecked(False) self.showProject() def onStopAllClicked(self): """结束所有任务按钮点击处理""" # 立即禁用按钮,防止重复点击 self.ui.tbStopAll.setEnabled(False) self.ui.tbStopAll.setToolTip("正在停止任务...") try: # 执行停止 stopped_count = taskActuatorManager.stopAll() # 显示反馈 if stopped_count > 0: log.info(f"已停止 {stopped_count} 个运行中的任务") else: log.info("没有运行中的任务") except Exception as e: log.error(f"停止任务时出错: {e}") finally: # 恢复按钮(确保无论成功与否都恢复) self.ui.tbStopAll.setEnabled(True) self.ui.tbStopAll.setToolTip("结束所有任务") def onStopAllRequested(self, requesterTaskId=None): """处理脚本请求的 stopAll(由主程序执行)""" print(f"[DEBUG] 收到 stopAll 请求,请求者任务ID: {requesterTaskId}") log.info(f"收到 stopAll 请求,请求者任务ID: {requesterTaskId}") try: print(f"[DEBUG] 当前任务字典: {list(taskActuatorManager.taskActuatorDict.keys())}") stopped_count = taskActuatorManager.stopAll(excludeTaskId=requesterTaskId) print(f"[DEBUG] 已停止 {stopped_count} 个其他任务") log.info(f"已停止 {stopped_count} 个其他任务") # 最后停止请求者自身 if requesterTaskId: print(f"[DEBUG] 停止请求者任务 {requesterTaskId}") taskActuatorManager.stop(requesterTaskId) print(f"[DEBUG] 已停止请求者任务 {requesterTaskId}") log.info(f"已停止请求者任务 {requesterTaskId}") except Exception as e: print(f"[DEBUG] 处理 stopAll 请求时出错: {e}") log.error(f"处理 stopAll 请求时出错: {e}") import traceback traceback.print_exc() def showTaskListForm(self): if self.taskListForm.isVisible(): self.taskListForm.setVisible(False) return self.taskListForm.setVisible(True) def alertDialog(self, msg, title = ""): msgBox = QMessageBox() reply = msgBox.question(self, title, f"{msg}", QMessageBox.StandardButton.Yes ) return reply == QMessageBox.StandardButton.Yes def removeDialog(self, msg, title = "删除指令"): msgBox = QMessageBox() reply = msgBox.question(self, title, f"是否删除 {msg}?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) return reply == QMessageBox.StandardButton.Yes def askDialog(self, msg, title = ""): msgBox = QMessageBox() reply = msgBox.question(self, title, msg, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) return reply == QMessageBox.StandardButton.Yes #关于 def showAboutDialog(self): QMessageBox.about(self, "关于", f"{app_version.data['name']} "+"\n\n版本号: "+app_version.data["version"]+"\n\nID: "+app_version.data["proId"]+"\n\n发布时间: "+app_version.data["releaseTime"]) #日志开关 def logControl(self): self.ui.logWidget.setVisible(not self.ui.logWidget.isVisible()) def setConfig(self): log.setConfig(config.data["log"]) grafana.setConfig(config.data["grafana"]) def _run(self): # influxdb.run() grafana.run() def _exit(self): influxdb.close() grafana.close()