TG-PlatformPlus/mainWindow.py

557 lines
24 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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()