PyQt6完整工程源码分享

PyQt6完整工程源码分享

一、源码分享

1、效果展示

2、工程结构

3、mainWindow.py

python 复制代码
import json
import logging
from enum import Enum

from PyQt6 import QtWidgets, QtCore
from PyQt6.QtCore import pyqtSlot, QDir, QFileInfo, QFile, QIODevice, QSize, QTimer, QPropertyAnimation, QEasingCurve, \
    QAbstractAnimation, QPoint
from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel, QMessageBox, QFileDialog, QListWidgetItem, QWidget
import mainWindowUI
import paraSettingWidget
import resources
from paraSettingWidget import ParaSettingsWidget
from projectPara import ProjectPara, equipmentToZh, equipment, arrivedPosToZh, arrivedPos, executeActionToZh, \
    executeAction
from createTaskWidget import CreateTaskWidget
from threadScheduleControl import ThreadScheduleControl

logger = logging.getLogger(ProjectPara.PROJECT)

class ListWidgetItem(QWidget):
    class Status(Enum):
        UnRunning = 0
        Running = 1
        Finished = 2
        Error = 3
    def __init__(self):
        super().__init__()
        self.valueInit()
        self.controlInit()

    def valueInit(self):
        pass

    def controlInit(self):
        self.setObjectName("ListWidgetItem")
        self.resize(512, 211)
        self.setStyleSheet("#ListWidgetItem{\n"
                           "    background-color: rgba(0, 255, 255, 100);\n"
                           "}")
        self.gridLayout = QtWidgets.QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.labelStatus = QtWidgets.QLabel(parent=self)
        # font = QtGui.QFont()
        # font.setFamily("Microsoft YaHei UI")
        # font.setPointSize(18)
        # font.setBold(True)
        # font.setItalic(False)
        # self.labelStatus.setFont(font)
        self.labelStatus.setStyleSheet("font: 700 15pt \"Microsoft YaHei UI\";\n"
                                       "background-color: rgb(255, 255, 0);\n"
                                       "border-radius:10px;")
        self.labelStatus.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.labelStatus.setWordWrap(True)
        self.labelStatus.setObjectName("labelStatus")
        self.verticalLayout.addWidget(self.labelStatus)
        self.labelIndex = QtWidgets.QLabel(parent=self)
        self.labelIndex.setStyleSheet("color: rgb(255, 0, 0);\n"
                                      "font: 25pt \"Microsoft YaHei UI\";"
                                      "background-color: rgba(0,255, 255, 100);\n"
                                      "border-radius:10px;")
        self.labelIndex.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.labelIndex.setObjectName("labelIndex")
        self.verticalLayout.addWidget(self.labelIndex)
        self.horizontalLayout.addLayout(self.verticalLayout)
        self.textBrowser = QtWidgets.QTextBrowser(parent=self)
        self.textBrowser.setObjectName("textBrowser")
        self.textBrowser.setStyleSheet("background-color: rgba(255, 255, 255, 0);")
        self.horizontalLayout.addWidget(self.textBrowser)
        self.horizontalLayout.setStretch(0, 1)
        self.horizontalLayout.setStretch(1, 4)
        self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
    def setIndex(self, index):
        self.labelIndex.setText("%02d"%index)
    def setStatus(self, status:Status):
        match status:
            case self.Status.UnRunning:
                self.labelStatus.setText("暂未\n运行")
                self.labelStatus.setStyleSheet("font: 700 15pt \"Microsoft YaHei UI\";\n"
                                               "background-color: rgb(255, 255, 0);\n"
                                               "border-radius:10px;")
            case self.Status.Running:
                self.labelStatus.setText("正在\n运行")
                self.labelStatus.setStyleSheet("font: 700 15pt \"Microsoft YaHei UI\";\n"
                                               "background-color: rgb(0, 255, 0);\n"
                                               "border-radius:10px;")
            case self.Status.Finished:
                self.labelStatus.setText("运行\n完成")
                self.labelStatus.setStyleSheet("font: 700 15pt \"Microsoft YaHei UI\";\n"
                                               "background-color: rgb(138, 159, 164);\n"
                                               "border-radius:10px;")
            case self.Status.Error:
                self.labelStatus.setText("出现\n故障")
                self.labelStatus.setStyleSheet("font: 700 15pt \"Microsoft YaHei UI\";\n"
                                               "background-color: rgb(255, 0, 0);\n"
                                               "border-radius:10px;")
    def setTaskInfo(self,info):
        write = ""
        for i in  range(len(info)):
            if equipment(int(info[i]["equipment"])) == equipment.NoEquip:
                continue
            str = "%s->%s->%s"%(equipmentToZh(equipment(int(info[i]["equipment"])))
                                            ,arrivedPosToZh(arrivedPos(int(info[i]["arrivedPos"])))
                                            ,executeActionToZh(executeAction(int(info[i]["executeAction"]))))
            # write += equipmentToZh(equipment(int(info[i]["equipment"]))) + "->"
            # write += arrivedPosToZh(arrivedPos(int(info[i]["arrivedPos"]))) + "->"
            # write += executeActionToZh(executeAction(int(info[i]["executeAction"]))) + "\n"
            write += str +"\n"

        self.textBrowser.setText(write)

class MainWindow(QMainWindow, mainWindowUI.Ui_MainWindow):

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)
        self.dirInit()
        self.valueInit()
        self.controlInit()
    def dirInit(self):
        logDir = QDir(ProjectPara.LOG_PATH)
        if not logDir.exists():
            dir = QDir()
            dir.mkpath(ProjectPara.LOG_PATH)

        taskDir = QDir(ProjectPara.TASK_PATH)
        if not taskDir.exists():
            dir = QDir()
            dir.mkpath(ProjectPara.TASK_PATH)


        self.updateTaskListSlot()
    def valueInit(self):
        #创建目录
        self.controller = ThreadScheduleControl()
        self.controller.taskRunOverSignal.connect(self.taskRunOverSlot)
        self.controller.taskRunErrorSignal.connect(self.taskRunErrorSlot)
        self.controller.taskRunningNumberSignal.connect(self.taskRunningNumberSlot)
        self.controller.taskFinishedNumberSignal.connect(self.taskFinishedNumberSlot)
        self.controller.equipmentErrorSignal.connect(self.equipmentErrorSlot)
        self.controller.updateTrafficPosSignal.connect(self.updateTrafficPosSlot)
        self.controller.start()

        #动画初始化
        self.animAirplane1 = QPropertyAnimation(self.labelAirplane1, b"pos")
        self.animAirplane1.setDuration(300)  # 动画持续时间1秒
        self.animAirplane1.setEasingCurve(QEasingCurve.Type.InOutQuad)  # 缓动曲线

        self.animAirplane2 = QPropertyAnimation(self.labelAirplane2, b"pos")
        self.animAirplane2.setDuration(300)  # 动画持续时间1秒
        self.animAirplane2.setEasingCurve(QEasingCurve.Type.InOutQuad)  # 缓动曲线

        self.animAirplane3 = QPropertyAnimation(self.labelAirplane3, b"pos")
        self.animAirplane3.setDuration(300)  # 动画持续时间1秒
        self.animAirplane3.setEasingCurve(QEasingCurve.Type.InOutQuad)  # 缓动曲线

        self.animAirplane4 = QPropertyAnimation(self.labelAirplane4, b"pos")
        self.animAirplane4.setDuration(300)  # 动画持续时间1秒
        self.animAirplane4.setEasingCurve(QEasingCurve.Type.InOutQuad)  # 缓动曲线

        self.animCar1 = QPropertyAnimation(self.labelCar1, b"pos")
        self.animCar1.setDuration(300)  # 动画持续时间1秒
        self.animCar1.setEasingCurve(QEasingCurve.Type.InOutQuad)  # 缓动曲线

        self.animCar2 = QPropertyAnimation(self.labelCar2, b"pos")
        self.animCar2.setDuration(300)  # 动画持续时间1秒
        self.animCar2.setEasingCurve(QEasingCurve.Type.InOutQuad)  # 缓动曲线

        self.animCar3 = QPropertyAnimation(self.labelCar3, b"pos")
        self.animCar3.setDuration(300)  # 动画持续时间1秒
        self.animCar3.setEasingCurve(QEasingCurve.Type.InOutQuad)  # 缓动曲线

        self.animCar4 = QPropertyAnimation(self.labelCar4, b"pos")
        self.animCar4.setDuration(300)  # 动画持续时间1秒
        self.animCar4.setEasingCurve(QEasingCurve.Type.InOutQuad)  # 缓动曲线


        self.airplanePosList = [arrivedPos.StartupPos, arrivedPos.StartupPos, arrivedPos.StartupPos, arrivedPos.StartupPos]
        self.labelAirplaneList = [self.labelAirplane1,self.labelAirplane2,self.labelAirplane3,self.labelAirplane4]
        self.animAirplaneList = [self.animAirplane1,self.animAirplane2,self.animAirplane3,self.animAirplane4]

        self.carPosList = [arrivedPos.StartupPos, arrivedPos.StartupPos, arrivedPos.StartupPos,arrivedPos.StartupPos]
        self.labelCarList = [self.labelCar1,self.labelCar2,self.labelCar3,self.labelCar4]
        self.animCarList = [self.animCar1,self.animCar2,self.animCar3,self.animCar4]
    def controlInit(self):

        title = self.windowTitle()
        title += "_"+ProjectPara.VERSION
        self.setWindowTitle(title)

        self.btnStartTask.setEnabled(False)
        self.btnStopTask.setEnabled(False)

        self.paraSettingsWidget = ParaSettingsWidget()
        self.createTaskWidget = CreateTaskWidget()

        self.createTaskWidget.updateTaskSignal.connect(self.updateTaskListSlot)

        self.actionCreateTask.triggered.connect(self.actionTriggerSlot)
        self.actionParaSettings.triggered.connect(self.actionTriggerSlot)
        self.actionSystemExit.triggered.connect(self.close)
        self.actionConnectDevice.triggered.connect(self.actionTriggerSlot)

        self.btnStartTask.clicked.connect(self.btnClickedSlot)
        self.btnStopTask.clicked.connect(self.btnClickedSlot)

        self.updateEquipmentPosTimer = QTimer()
        self.updateEquipmentPosTimer.timeout.connect(self.updateEquipmentTimeroutSlot)
        self.updateEquipmentPosTimer.start(100)
    def __calAndUpdateTrafficPos(self):
        intervalWidth = self.widgetEquipment.width() / 4
        arrangeWidth = self.widgetEquipment.width() - intervalWidth*2
        arrangeWidth = arrangeWidth - self.labelAirplane1.width() - self.labelAirplane2.width() - self.labelAirplane3.width() - self.labelAirplane4.width()
        arrangeWidth = arrangeWidth / 3

        for i in range(len(self.labelAirplaneList)):
            if self.animAirplaneList[i].state() == QAbstractAnimation.State.Running:
                continue
            x = 0
            y = 0
            match self.airplanePosList[i]:
                case arrivedPos.StartupPos | arrivedPos.StopedPos:
                    x = intervalWidth + i * (self.labelAirplaneList[i].width() + arrangeWidth)
                    y = self.widgetEquipment.height()/2 + self.widgetEquipment.height()/5 
                case arrivedPos.Platform1Pos:
                    geo = self.labelPlatform1.geometry()
                    x = geo.x() + geo.width() / 2 - self.labelAirplaneList[i].width() / 2
                    y = geo.y() - geo.height()
                case arrivedPos.Platform2Pos:
                    geo = self.labelPlatform2.geometry()
                    x = geo.x() + geo.width() / 2 - self.labelAirplaneList[i].width() / 2
                    y = geo.y() - geo.height()
                case arrivedPos.Platform3Pos:
                    geo = self.labelPlatform3.geometry()
                    x = geo.x() + geo.width() / 2 - self.labelAirplaneList[i].width() / 2
                    y = geo.y() - geo.height()
                case arrivedPos.Platform4Pos:
                    geo = self.labelPlatform4.geometry()
                    x = geo.x() + geo.width() / 2 - self.labelAirplaneList[i].width() / 2
                    y = geo.y() - geo.height()
            if x != self.labelAirplaneList[i].x() or y != self.labelAirplaneList[i].y():
                self.animAirplaneList[i].setStartValue(self.labelAirplaneList[i].pos())
                self.animAirplaneList[i].setEndValue(QPoint(int(x),int(y)))
                self.animAirplaneList[i].start()
            #self.labelAirplaneList[i].setGeometry(int(x),int(y),self.labelAirplaneList[i].width(),self.labelAirplaneList[i].height())

        intervalWidth = self.widgetEquipment.width() / 4
        arrangeWidth = self.widgetEquipment.width() - intervalWidth*2
        arrangeWidth = arrangeWidth - self.labelCar1.width() - self.labelCar2.width() - self.labelCar3.width() - self.labelCar4.width()
        arrangeWidth = arrangeWidth / 3
        for i in range(len(self.labelCarList)):
            if self.animCarList[i].state() == QAbstractAnimation.State.Running:
                continue
            x = 0
            y = 0
            match self.carPosList[i]:
                case arrivedPos.StartupPos | arrivedPos.StopedPos:
                    x = intervalWidth + i * (self.labelCarList[i].width() + arrangeWidth)
                    y = self.widgetEquipment.height()/2 - self.widgetEquipment.height()/5 - self.labelCarList[i].height()
                case arrivedPos.Platform1Pos:
                    geo = self.labelPlatform1.geometry()
                    x = geo.x() + geo.width() / 2 - self.labelCarList[i].width() / 2
                    y = geo.y() + geo.height()+5
                case arrivedPos.Platform2Pos:
                    geo = self.labelPlatform2.geometry()
                    x = geo.x() + geo.width() / 2 - self.labelCarList[i].width() / 2
                    y = geo.y() + geo.height()+5
                case arrivedPos.Platform3Pos:
                    geo = self.labelPlatform3.geometry()
                    x = geo.x() + geo.width() / 2 - self.labelCarList[i].width() / 2
                    y = geo.y() + geo.height()+5
                case arrivedPos.Platform4Pos:
                    geo = self.labelPlatform4.geometry()
                    x = geo.x() + geo.width() / 2 - self.labelCarList[i].width() / 2
                    y = geo.y() + geo.height()+5
            if x != self.labelCarList[i].x() or y != self.labelCarList[i].y():
                self.animCarList[i].setStartValue(self.labelCarList[i].pos())
                self.animCarList[i].setEndValue(QPoint(int(x),int(y)))
                self.animCarList[i].start()
            #self.labelCarList[i].setGeometry(int(x),int(y),self.labelCarList[i].width(),self.labelCarList[i].height())

    def setTaskItemStatus(self,number,status:ListWidgetItem.Status):
        item = self.listWidget.item(number)
        widget = self.listWidget.itemWidget(item)
        if widget and isinstance(widget, ListWidgetItem):
            widget.setStatus(status)
    @pyqtSlot()
    def updateTaskListSlot(self):
        self.comboBoxTask.clear()
        logger.info("update task list slot")
        dir = QDir(ProjectPara.TASK_PATH)
        entryDirList = dir.entryInfoList(QDir.Filter.Files)
        for i in entryDirList:
            if i.isFile():
                info = QFileInfo(i)
                self.comboBoxTask.addItem(info.baseName())
    @pyqtSlot()
    def actionTriggerSlot(self):
        action = self.sender()
        if action == self.actionParaSettings:
            self.paraSettingsWidget.show()
        elif action == self.actionCreateTask:
            self.createTaskWidget.show()
        elif action == self.actionConnectDevice:
            res,info = self.controller.connectTo()
            if res:
                QMessageBox.information(self,"连接成功!","设备连接成功!")
                self.btnStartTask.setEnabled(True)
            else:
                QMessageBox.critical(self,"连接失败!",f"失败设备:{info}")
    @pyqtSlot()
    def btnClickedSlot(self):
        btn = self.sender()

        if btn == self.btnStartTask:
            logger.debug("btn start clicked!")
            #判断设备是否正在初始化中
            # if not self.controller.equipmentIsReady():
            #     QMessageBox.warning(self,"设备初始化中!","设备正在初始化,请稍后!")
            #     logger.debug("equipment init!")
            #     return
            if self.controller.equipmentIsError():
                QMessageBox.warning(self,"设备故障!","设备故障,请检查!")
                logger.debug("equipment error!")
                return
            
            taskName = self.comboBoxTask.currentText()
            if not taskName:
                QMessageBox.warning(self,"任务为空!","请选择正确的任务!")
                return

            #开始解析任务
            file = QFile(ProjectPara.TASK_PATH + taskName + ".json")
            if not file.open(QIODevice.OpenModeFlag.ReadOnly | QIODevice.OpenModeFlag.Text):
                QMessageBox.warning(self, "任务文件打开失败!", "任务文件打开失败,请重试!")
                return
            data = file.readAll()
            file.close()
            attitude = 0.0
            try:
                logger.debug(f"start task:{taskName}")
                jsonData = json.loads(data.data().decode('utf-8'))

                attitude = float(jsonData["attitude"])

                self.listWidget.clear()
                insertIndex = 0
                for taskItem in jsonData["taskItem"]:
                    itemWidgt = ListWidgetItem()
                    item = QListWidgetItem()
                    item.setSizeHint(QSize(self.listWidget.width(), 200))

                    self.listWidget.insertItem(insertIndex, item)
                    itemWidgt.setTaskInfo(taskItem)
                    itemWidgt.setStatus(ListWidgetItem.Status.UnRunning)
                    itemWidgt.setIndex(insertIndex)
                    self.listWidget.setItemWidget(self.listWidget.item(insertIndex), itemWidgt)
                    insertIndex += 1
            except json.JSONDecodeError as e:
                logger.warning("task analysis failed!")
                QMessageBox.warning(self,"任务解析错误","任务解析错误!请检查!")
                return
            except :
                logger.warning("task analysis failed!")
                QMessageBox.warning(self, "任务解析错误", "任务解析错误!请检查!")
                return
            if self.controller.startTask(jsonData["taskItem"],attitude):
                self.btnStartTask.setEnabled(False)
                self.btnStopTask.setEnabled(True)
                self.actionCreateTask.setEnabled(False)
            #QMessageBox.information(self,"文件打开成功!","文件打开成功")
        elif btn == self.btnStopTask:
            logger.debug("btn stop clicked!")
            self.controller.stopTask()
            self.btnStartTask.setEnabled(True)
            self.btnStopTask.setEnabled(False)
            self.actionCreateTask.setEnabled(True)
    @pyqtSlot()
    def taskRunOverSlot(self):
        self.btnStartTask.setEnabled(True)
        self.btnStopTask.setEnabled(False)
        self.actionCreateTask.setEnabled(True)
        QMessageBox.information(self,"任务完成!","任务运行完成!")
    @pyqtSlot(int,str)
    def taskRunErrorSlot(self,number,info):
        self.btnStartTask.setEnabled(True)
        self.btnStopTask.setEnabled(False)
        self.actionCreateTask.setEnabled(True)
        logger.debug(f"task run error:{number}")
        self.setTaskItemStatus(number,ListWidgetItem.Status.Error)
        QMessageBox.critical(self, "任务错误!", f"任务运行错误!错误信息:{info}")
    @pyqtSlot(int)
    def taskRunningNumberSlot(self,number):
        #self.taskRunningNumberLabel.setText(f"当前运行任务数:{number}")
        #self.listWidget.item(number).setTaskStatus(ListWidgetItem.Status.Running)
        logger.debug(f"task running number:{number}")
        self.setTaskItemStatus(number,ListWidgetItem.Status.Running)
        item = self.listWidget.item(number)
        if item:
            self.listWidget.scrollToItem(item)
    @pyqtSlot(int)
    def taskFinishedNumberSlot(self,number):
        logger.debug(f"task finished number:{number}")
        self.setTaskItemStatus(number,ListWidgetItem.Status.Finished)
    @pyqtSlot(str,int)
    def equipmentErrorSlot(self,info,number):
        logger.error(f"equipment error equipment{info} number{number}!")
    @pyqtSlot(equipment,arrivedPos)
    def updateTrafficPosSlot(self,traffic:equipment,pos:arrivedPos):
        match traffic:
            case equipment.Airplane1:
                self.airplanePosList[0] = pos
            case equipment.Airplane2:
                self.airplanePosList[1] = pos
            case equipment.Airplane3:
                self.airplanePosList[2] = pos
            case equipment.Airplane4:
                self.airplanePosList[3] = pos
            case equipment.Car1:
                self.carPosList[0] = pos
            case equipment.Car2:
                self.carPosList[1] = pos
            case equipment.Car3:
                self.carPosList[2] = pos
            case equipment.Car4:
                self.carPosList[3] = pos
            case _:
                logger.warning(f"unknown equipment:{traffic}")
    @pyqtSlot()
    def updateEquipmentTimeroutSlot(self):
        width = self.widgetEquipment.width()
        height = self.widgetEquipment.height()

        #更新基站位置
        self.labelStation1.setGeometry(0, 0, self.labelStation1.width(), self.labelStation1.height())
        self.labelStation2.setGeometry(width - self.labelStation2.width(), 0, self.labelStation2.width(), self.labelStation2.height())
        self.labelStation3.setGeometry(width - self.labelStation3.width(), height - self.labelStation3.height(), 
                                       self.labelStation3.width(), self.labelStation3.height())
        self.labelStation4.setGeometry(0, height - self.labelStation4.height(), self.labelStation4.width(), self.labelStation4.height())

        #更新平台位置
        self.labelPlatform1.setGeometry(self.labelStation1.x() + self.labelStation1.width() , 
                                        self.labelStation1.y() + self.labelStation1.height(),self.labelPlatform1.width(),self.labelPlatform1.height())
        self.labelPlatform2.setGeometry(self.labelStation2.x() - self.labelPlatform2.width(),
                                        self.labelStation2.y() + self.labelStation2.height(), self.labelPlatform2.width(), self.labelPlatform2.height())
        self.labelPlatform3.setGeometry(self.labelStation3.x() - self.labelPlatform3.width(),
                                        self.labelStation3.y() - self.labelPlatform3.height(), self.labelPlatform3.width(), self.labelPlatform3.height())
        self.labelPlatform4.setGeometry(self.labelStation4.x() + self.labelStation4.width(),
                                        self.labelStation4.y() - self.labelPlatform4.height(), self.labelPlatform4.width(), self.labelPlatform4.height())
        self.__calAndUpdateTrafficPos()

4、createTaskWidget.py

python 复制代码
import logging
from xmlrpc.client import DateTime

import json
from PyQt6 import QtWidgets, QtCore
from PyQt6.QtCore import pyqtSlot, Qt, QSettings, QFile, QFileInfo, QPoint, QSize, QIODevice, QDateTime, pyqtSignal
from PyQt6.QtGui import QAction, QIcon, QDoubleValidator
from PyQt6.QtWidgets import QWidget, QHeaderView, QMessageBox, QTableWidgetItem, QFileDialog, QMenu, QGridLayout, \
    QTableWidget, QListWidgetItem, QComboBox, QListWidget

import createTaskWidgetUI
import paraSettingWidgetUI
import resources
from projectPara import ProjectPara, equipment, equipmentToZh, arrivedPos, arrivedPosToZh, executeAction, \
    executeActionToZh

logger = logging.getLogger(ProjectPara.PROJECT)

class DisabledWheelComboBox(QComboBox):
    """继承QComboBox并重写wheelEvent方法来禁用滚轮功能"""
    def wheelEvent(self, event):
        # 忽略滚轮事件,阻止QComboBox响应鼠标滚轮
        event.ignore()

class ListWidgetItem(QWidget):
    def __init__(self):
        super().__init__()
        self.valueInit()
        self.controlInit()
    def valueInit(self):
        pass
    def controlInit(self):
        self.setObjectName("ListWidgetItem")
        self.resize(536, 219)
        self.gridLayout = QtWidgets.QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setSpacing(0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label = QtWidgets.QLabel(parent=self)
        self.label.setStyleSheet("color: rgb(255, 0, 0);\n"
                                 "font: 25pt \"Microsoft YaHei UI\";")
        self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.label.setObjectName("label")
        self.horizontalLayout.addWidget(self.label)
        self.tableWidget = QtWidgets.QTableWidget(parent=self)
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(3)
        self.tableWidget.setRowCount(12)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        self.tableWidget.horizontalHeader().setCascadingSectionResizes(False)
        self.tableWidget.verticalHeader().setVisible(False)
        self.tableWidget.verticalHeader().setCascadingSectionResizes(False)
        self.horizontalLayout.addWidget(self.tableWidget)
        self.horizontalLayout.setStretch(0, 1)
        self.horizontalLayout.setStretch(1, 6)
        self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)


        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText("设备")
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText("到达位置")
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText("执行动作")

        self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)

        #添加combox
        for i in range(self.tableWidget.rowCount()):
            combox = DisabledWheelComboBox()
            for j in range(equipment.MaxEquip.value):
                combox.addItem(equipmentToZh(equipment(j)))
            self.tableWidget.setCellWidget(i,0,combox)

        for i in range(self.tableWidget.rowCount()):
            combox = DisabledWheelComboBox()
            for j in range(arrivedPos.MaxPos.value):
                combox.addItem(arrivedPosToZh(arrivedPos(j)))
            self.tableWidget.setCellWidget(i,1,combox)

        for i in range(self.tableWidget.rowCount()):
            combox = DisabledWheelComboBox()
            for j in range(executeAction.MaxAction.value):
                combox.addItem(executeActionToZh(executeAction(j)))
            self.tableWidget.setCellWidget(i,2,combox)

    def setIndex(self,index):
        self.label.setText("%02d"%index)
    def getIndex(self) ->str:
        return self.label.text()

    def setExpand(self, isExpand:bool):
        if isExpand:
            # 鼠标进入时增大宽度
            geo = self.geometry()
            geo.setHeight(geo.height()*2)
            self.setGeometry(geo)

            parent = self.parent()
            if parent and hasattr(parent, 'parent'):
                list_widget = parent.parent()
                if isinstance(list_widget, QListWidget):
                    # 找到对应的 QListWidgetItem
                    item = None
                    for i in range(list_widget.count()):
                        if list_widget.itemWidget(list_widget.item(i)) is self:
                            item = list_widget.item(i)
                            break
                    if item:
                        # 更新项的大小提示
                        geo = self.geometry()
                        # 更新项的大小提示
                        item.setSizeHint(QSize(geo.width(),int(450)))
                        # 强制刷新布局
                        list_widget.updateGeometries()
                        # 或者使用 list_widget.viewport().update() 也可以
                        # list_widget.viewport().update()
        else:
            # 鼠标离开时恢复宽度
            parent = self.parent()
            if parent and hasattr(parent, 'parent'):
                list_widget = parent.parent()
                if isinstance(list_widget, QListWidget):
                    # 找到对应的 QListWidgetItem
                    item = None
                    for i in range(list_widget.count()):
                        if list_widget.itemWidget(list_widget.item(i)) is self:
                            item = list_widget.item(i)
                            break
                    if item:
                        geo = self.geometry()
                        # 更新项的大小提示
                        item.setSizeHint(QSize(geo.width(), 170))
                        # 强制刷新布局
                        list_widget.updateGeometries()
                        # 或者使用 list_widget.viewport().update() 也可以
                        # list_widget.viewport().update()
        self.update()
    def getTaskInfo(self) :
        listVal = []
        for i in range(self.tableWidget.rowCount()):
            dictVal = {}
            widget = self.tableWidget.cellWidget(i,0)
            if widget and isinstance(widget,QComboBox):
                dictVal["equipment"] = widget.currentIndex()

            widget = self.tableWidget.cellWidget(i,1)
            if widget and isinstance(widget,QComboBox):
                dictVal["arrivedPos"] = widget.currentIndex()

            widget = self.tableWidget.cellWidget(i,2)
            if widget and isinstance(widget,QComboBox):
                dictVal["executeAction"] = widget.currentIndex()

            listVal.append(dictVal)
        return listVal
    def setTaskInfo(self,info):
        for i in  range(self.tableWidget.rowCount()):
            widget = self.tableWidget.cellWidget(i,0)
            if widget and isinstance(widget,QComboBox):
                widget.setCurrentIndex(int(info[i]["equipment"]))

            widget = self.tableWidget.cellWidget(i,1)
            if widget and isinstance(widget,QComboBox):
                widget.setCurrentIndex(info[i]["arrivedPos"])

            widget = self.tableWidget.cellWidget(i,2)
            if widget and isinstance(widget,QComboBox):
                widget.setCurrentIndex(info[i]["executeAction"])
            #print(info[i]["equipment"])



class CreateTaskWidget(QWidget, createTaskWidgetUI.Ui_CreateTaskWidget):
    updateTaskSignal = pyqtSignal()
    def __init__(self, parent=None):
        super(CreateTaskWidget, self).__init__(parent)
        self.setupUi(self)
        self.valueInit()
        self.controlInit()
    def valueInit(self):
        self.currentTaskFilePath = ""
        self.currentTaskFile:QFile = QFile()
    def controlInit(self):
        self.btnNew.clicked.connect(self.btnClickedSlot)
        self.btnOpen.clicked.connect(self.btnClickedSlot)
        self.btnSave.clicked.connect(self.btnClickedSlot)

        self.listWidget.customContextMenuRequested.connect(self.customContextMenuSlotSlot)
        self.listWidget.itemClicked.connect(self.listWidgetItemClickedSlot)

        # 设置验证器
        validator = QDoubleValidator(0.0, 1000000.0, 3, self)
        validator.setNotation(QDoubleValidator.Notation.StandardNotation)
        self.lineEditAttitude.setValidator(validator)
    def refreshListIndex(self):
        if self.listWidget.count() == 0:
            return
        for i in range(self.listWidget.count()):
            item = self.listWidget.item(i)
            widget = self.listWidget.itemWidget(item)
            if widget and isinstance(widget, ListWidgetItem):
                widget.setIndex(i)
    def __verifyTaskName(self,taskList):
        lineNo = 0
        info = ""
        if len(taskList) == 0:
            return False,0,"任务条目为空!"
        equipList = []
        for task in taskList:
            equipList.clear()
            for item in task:
                equip = equipment(int(item["equipment"]))
                pos = arrivedPos(int(item["arrivedPos"]))
                action = executeAction(int(item["executeAction"]))
                if equip == equipment.NoEquip:
                    continue
                match equip:
                    case equipment.Airplane1 | equipment.Airplane2 | equipment.Airplane3 | equipment.Airplane4:
                        if pos == arrivedPos.NoPos:
                            return False,lineNo,"飞机到达位置不能为空!"
                        if action == executeAction.NoAction:
                            return False,lineNo,"飞机执行动作不能为空!"
                    case equipment.Car1 | equipment.Car2 | equipment.Car3 | equipment.Car4:
                        if pos == arrivedPos.NoPos:
                            return False,lineNo,"小车到达位置不能为空!"
                        if action == executeAction.NoAction:
                            return False,lineNo,"小车执行动作不能为空!"
                    case equipment.Platform1 | equipment.Platform2 | equipment.Platform3 | equipment.Platform4:
                        pass
                if equip in equipList:
                    return False,lineNo,"任务条目中存在重复的设备!"
                equipList.append(equip)
            lineNo +=1
        return True,lineNo,info
    @pyqtSlot(QListWidgetItem)
    def listWidgetItemClickedSlot(self,item):
        for i in range(self.listWidget.count()):
            itemTemp = self.listWidget.item(i)
            widget = self.listWidget.itemWidget(itemTemp)
            if widget and isinstance(widget, ListWidgetItem):
                widget.setExpand(False)

        widget = self.listWidget.itemWidget(item)
        if widget and isinstance(widget, ListWidgetItem):
            widget.setExpand(True)
    @pyqtSlot()
    def btnClickedSlot(self):
        btn = self.sender()
        if btn == self.btnNew:
            file_path, selected_filter = QFileDialog.getSaveFileName(self, "保存文件", ProjectPara.TASK_PATH,"任务文件 (*.json);")
            if not file_path:
                QMessageBox.warning(self,"错误!","请选择正确文件!")
                return
            self.currentTaskFilePath = file_path
            self.listWidget.clear()
            self.currentTaskFile.setFileName(self.currentTaskFilePath)
            self.lineEditTaskName.setText(QFileInfo(self.currentTaskFile).baseName())
        elif btn == self.btnOpen:
            file_path, selected_filter = QFileDialog.getOpenFileName(self, "选择文件", ProjectPara.TASK_PATH,"任务文件 (*.json);")
            if not file_path:
                QMessageBox.warning(self,"错误!","请选择正确的文件!")
                return
            file = QFile(file_path)
            if not file.open(QIODevice.OpenModeFlag.ReadOnly | QIODevice.OpenModeFlag.Text):
                QMessageBox.warning(self,"文件打开失败!","文件打开失败,请重试!")
                return
            self.currentTaskFilePath = file_path
            self.currentTaskFile.setFileName(file_path)
            data = file.readAll()
            file.close()
            fileInfo = QFileInfo(file_path)
            fileInfoStr = "文件名:" + fileInfo.fileName() + "\n" +\
            "文件大小:" + str(fileInfo.size()) + "\n" +\
            "创建日期:" + fileInfo.birthTime().toString("yyyy-MM-dd hh:mm:ss") + "\n";

            self.textBrowserTaskInfo.setText(fileInfoStr)


            try:
                jsonData = json.loads(data.data().decode('utf-8'))
                self.lineEditTaskName.setText(jsonData["taskName"])
                self.lineEditAttitude.setText(str(jsonData["attitude"]))

                self.listWidget.clear()
                insertIndex = 0
                for taskItem in jsonData["taskItem"]:
                    itemWidgt = ListWidgetItem()
                    item = QListWidgetItem()
                    item.setSizeHint(QSize(self.listWidget.width(), 170))

                    self.listWidget.insertItem(insertIndex, item)
                    itemWidgt.setTaskInfo(taskItem)
                    itemWidgt.setIndex(insertIndex)
                    self.listWidget.setItemWidget(self.listWidget.item(insertIndex), itemWidgt)
                    insertIndex += 1
            except json.JSONDecodeError as e:
                QMessageBox.warning(self,"解析错误","json解析错误!请检查!")
                return
            except :
                QMessageBox.warning(self, "解析错误", "json解析错误!请检查!")
                return

            QMessageBox.information(self,"文件打开成功!","文件打开成功")


        elif btn == self.btnSave:
            taskItem = []
            for i in range(self.listWidget.count()):
                itemTemp = self.listWidget.item(i)
                widget = self.listWidget.itemWidget(itemTemp)
                if widget and isinstance(widget, ListWidgetItem):
                    taskItem.append(widget.getTaskInfo())

            #校验任务
            isOk,line,info = self.__verifyTaskName(taskItem)
            if not isOk:
                QMessageBox.warning(self,"任务错误!",f"任务第{line}个条目错误!错误信息:{info}!")
                return

            if not self.currentTaskFile.open(QIODevice.OpenModeFlag.WriteOnly | QIODevice.OpenModeFlag.Text):
                QMessageBox.warning(self,"文件打开失败!","文件打开失败,请重试!")
                return

            data = {}
            data["taskName"] = self.lineEditTaskName.text()
            data["time"] = QDateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
            data["attitude"] = float(self.lineEditAttitude.text())
            data["taskItem"] = taskItem

            json_string = json.dumps(data, ensure_ascii=False, indent=2)
            self.currentTaskFile.write(json_string.encode("utf-8"))
            self.currentTaskFile.close()
            self.updateTaskSignal.emit()
            QMessageBox.information(self,"保存成功!","任务保存成功!")
    @pyqtSlot(QPoint)
    def customContextMenuSlotSlot(self,pos):
        if not self.currentTaskFilePath:
            QMessageBox.warning(self, "任务不存在!", "请先新建或打开任务!")
            return

        selectItem  = self.listWidget.itemAt(pos)
        if not selectItem:
            logger.warning("item is null")

        actionCreate = QAction("创建任务条目")
        actionCreate.setIcon(QIcon(":/images/resources/images/icon_new.svg"))
        @pyqtSlot()
        def createTriggerSlot():
            itemWidgt = ListWidgetItem()
            item = QListWidgetItem()
            item.setSizeHint(QSize(self.listWidget.width(), 170))
            if selectItem == None:
                insertIndex = self.listWidget.count()
                self.listWidget.insertItem(insertIndex,item)
            else:
                insertIndex =self.listWidget.row(selectItem)+1
                self.listWidget.insertItem(insertIndex, item)
            #itemWidgt.setIndex(insertIndex)
            self.listWidget.setItemWidget(self.listWidget.item(insertIndex), itemWidgt)

            self.refreshListIndex()



        actionCreate.triggered.connect(createTriggerSlot)


        actionDelete = QAction("删除任务条目")
        actionDelete.setIcon(QIcon(":/images/resources/images/icon_delete.svg"))
        if not selectItem:
            actionDelete.setEnabled(False)
        def deleteTriggerSlot():
            reply = QMessageBox.question(
                self,
                '确认删除?',
                f'您确定要删除第{self.listWidget.row(selectItem)}个条目吗?',
                QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
                QMessageBox.StandardButton.No
            )
            if reply == QMessageBox.StandardButton.Yes:
                self.listWidget.takeItem(self.listWidget.row(selectItem))
                self.refreshListIndex()
        actionDelete.triggered.connect(deleteTriggerSlot)

        menu = QMenu()
        menu.addAction(actionCreate)
        menu.addAction(actionDelete)


        menu.exec(self.listWidget.cursor().pos())

5、paraSettingsWidget.py

python 复制代码
from PyQt6.QtCore import pyqtSlot, Qt, QSettings, QFile
from PyQt6.QtWidgets import QWidget, QHeaderView, QMessageBox, QTableWidgetItem
import paraSettingWidgetUI
import resources
from projectPara import ProjectPara


class ParaSettingsWidget(QWidget, paraSettingWidgetUI.Ui_ParaSettingsWidget):
    def __init__(self, parent=None):
        super(ParaSettingsWidget, self).__init__(parent)
        self.setupUi(self)
        self.valueInit()
        self.controlInit()
    def valueInit(self):
        pass
    def controlInit(self):
        self.tableWidgetPlatform.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        self.tableWidgetTraffic.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        self.tableWidgetOther.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)

        for i in range(self.tableWidgetTraffic.rowCount()):
            self.tableWidgetTraffic.item(i, 0).setFlags(self.tableWidgetTraffic.item(i, 0).flags() & ~Qt.ItemFlag.ItemIsEditable)
        for i in range(self.tableWidgetPlatform.rowCount()):
            self.tableWidgetPlatform.item(i, 0).setFlags(self.tableWidgetPlatform.item(i, 0).flags() & ~Qt.ItemFlag.ItemIsEditable)

        self.btnSave.clicked.connect(self.btnClickedSlot)
        self.btnCancel.clicked.connect(self.btnClickedSlot)

        #加载初始值
        settingFile = QFile(ProjectPara.CONFIG_FILE_PATH)
        if settingFile.exists():
            setting = QSettings(ProjectPara.CONFIG_FILE_PATH, QSettings.Format.IniFormat)
            setting.beginGroup("networkPara")
            for i in range(self.tableWidgetNet.columnCount()):
                if i < 4:
                    ipItem = QTableWidgetItem()
                    ipItem.setText(setting.value(f"platform{i + 1}Ip"))
                    portItem = QTableWidgetItem()
                    portItem.setText(setting.value(f"platform{i + 1}Port"))
                    self.tableWidgetNet.setItem(0,i,ipItem)
                    self.tableWidgetNet.setItem(1,i,portItem)
                elif i < 8:
                    ipItem = QTableWidgetItem()
                    ipItem.setText(setting.value(f"airplane{i - 3}Ip"))
                    portItem = QTableWidgetItem()
                    portItem.setText(setting.value(f"airplane{i - 3}Port"))
                    self.tableWidgetNet.setItem(0,i,ipItem)
                    self.tableWidgetNet.setItem(1,i,portItem)
                else:
                    ipItem = QTableWidgetItem()
                    ipItem.setText(setting.value(f"car{i - 7}Ip"))
                    portItem = QTableWidgetItem()
                    portItem.setText(setting.value(f"car{i - 7}Port"))
                    self.tableWidgetNet.setItem(0,i,ipItem)
                    self.tableWidgetNet.setItem(1,i,portItem)
            setting.endGroup()
            setting.beginGroup("trafficPara")
            for i in range(1,self.tableWidgetTraffic.columnCount()):
                if i < 5:
                    startLonItem = QTableWidgetItem()
                    startLonItem.setText(setting.value(f"airplane{i}StartLon"))
                    startLatItem = QTableWidgetItem()
                    startLatItem.setText(setting.value(f"airplane{i}StartLat"))
                    stoptLonItem = QTableWidgetItem()
                    stoptLonItem.setText(setting.value(f"airplane{i}StopLon"))
                    stopLatItem = QTableWidgetItem()
                    stopLatItem.setText(setting.value(f"airplane{i}StopLat"))

                    self.tableWidgetTraffic.setItem(0, i,startLonItem)
                    self.tableWidgetTraffic.setItem(1, i,startLatItem)
                    self.tableWidgetTraffic.setItem(2, i,stoptLonItem)
                    self.tableWidgetTraffic.setItem(3, i,stopLatItem)
                else:
                    startLonItem = QTableWidgetItem()
                    startLonItem.setText(setting.value(f"car{i-4}StartLon"))
                    startLatItem = QTableWidgetItem()
                    startLatItem.setText(setting.value(f"car{i-4}StartLat"))
                    stoptLonItem = QTableWidgetItem()
                    stoptLonItem.setText(setting.value(f"car{i-4}StopLon"))
                    stopLatItem = QTableWidgetItem()
                    stopLatItem.setText(setting.value(f"car{i-4}StopLat"))

                    self.tableWidgetTraffic.setItem(0, i,startLonItem)
                    self.tableWidgetTraffic.setItem(1, i,startLatItem)
                    self.tableWidgetTraffic.setItem(2, i,stoptLonItem)
                    self.tableWidgetTraffic.setItem(3, i,stopLatItem)
            setting.endGroup()
            setting.beginGroup("platformPara")
            for i in range(1, self.tableWidgetPlatform.columnCount()):
                airplaneLonItem = QTableWidgetItem()
                airplaneLonItem.setText(setting.value(f"platform{i}AirplaneLon"))
                airplaneLatItem = QTableWidgetItem()
                airplaneLatItem.setText(setting.value(f"platform{i}AirplaneLat"))
                airplaneYawItem = QTableWidgetItem()
                airplaneYawItem.setText(setting.value(f"platform{i}AirplaneYaw"))

                carLonItem = QTableWidgetItem()
                carLonItem.setText(setting.value(f"platform{i}CarLon"))
                carLatItem = QTableWidgetItem()
                carLatItem.setText(setting.value(f"platform{i}CarLat"))
                carYawItem = QTableWidgetItem()
                carYawItem.setText(setting.value(f"platform{i}CarYaw"))

                self.tableWidgetPlatform.setItem(0, i,airplaneLonItem)
                self.tableWidgetPlatform.setItem(1, i, airplaneLatItem)
                self.tableWidgetPlatform.setItem(2, i,airplaneYawItem)
                self.tableWidgetPlatform.setItem(3, i, carLonItem)
                self.tableWidgetPlatform.setItem(4, i, carLatItem)
                self.tableWidgetPlatform.setItem(5, i, carYawItem)
            setting.endGroup()
            setting.beginGroup("otherPara")
            setting.endGroup()
            setting.beginGroup("otherPara")
            for i in range(self.tableWidgetOther.columnCount()):
                self.tableWidgetOther.setItem(0, i,QTableWidgetItem(setting.value(f"station{i + 1}Lon")))
                self.tableWidgetOther.setItem(1, i, QTableWidgetItem(setting.value(f"station{i + 1}Lat")))

    @pyqtSlot()
    def btnClickedSlot(self):
        btn = self.sender()
        if btn == self.btnSave:
            print("save")
            setting = QSettings(ProjectPara.CONFIG_FILE_PATH,QSettings.Format.IniFormat)
            setting.beginGroup("networkPara")
            for i in range(self.tableWidgetNet.columnCount()):
                try:
                    ip = self.tableWidgetNet.item(0, i).text()
                    port = int(self.tableWidgetNet.item(1, i).text())
                    #setting.setValue(self.tableWidgetNet.item(0, i).text(), val)
                    if i <4:
                        setting.setValue(f"platform{i + 1}Ip", ip)
                        setting.setValue(f"platform{i + 1}Port", port)
                    elif i < 8:
                        setting.setValue(f"airplane{i-3}Ip", ip)
                        setting.setValue(f"airplane{i-3}Port", port)
                    else:
                        setting.setValue(f"car{i-7}Ip", ip)
                        setting.setValue(f"car{i-7}Port", port)
                except:
                    QMessageBox.warning(self,"参数错误!","网络参数表格第%d行%d列参数错误!请检查!"%(1,i+1))
                    return
                finally:
                    pass
            setting.endGroup()
            setting.beginGroup("trafficPara")
            for i in range(1,self.tableWidgetTraffic.columnCount()):
                try:
                    startLon = float(self.tableWidgetTraffic.item(0, i).text())
                    startLat = float(self.tableWidgetTraffic.item(1, i).text())
                    stopLon = float(self.tableWidgetTraffic.item(2, i).text())
                    stopLat = float(self.tableWidgetTraffic.item(3, i).text())
                    if i <5:
                        setting.setValue(f"airplane{i}StartLon", startLon)
                        setting.setValue(f"airplane{i}StartLat", startLat)
                        setting.setValue(f"airplane{i}StopLon", stopLon)
                        setting.setValue(f"airplane{i}StopLat", stopLat)
                    else:
                        setting.setValue(f"car{i-4}StartLon", startLon)
                        setting.setValue(f"car{i-4}StartLat", startLat)
                        setting.setValue(f"car{i-4}StopLon", stopLon)
                        setting.setValue(f"car{i-4}StopLat", stopLat)
                except:
                    QMessageBox.warning(self,"参数错误!","飞机小车参数表格第%d列参数错误!请检查!"%i)
                    return
                finally:
                    pass
            setting.endGroup()
            setting.beginGroup("platformPara")
            for i in range(1, self.tableWidgetPlatform.columnCount()):
                try:
                    airplaneLon = float(self.tableWidgetPlatform.item(0, i).text())
                    airplaneLat = float(self.tableWidgetPlatform.item(1, i).text())
                    airplaneYaw = float(self.tableWidgetPlatform.item(2, i).text())
                    carLon = float(self.tableWidgetPlatform.item(3, i).text())
                    carLat = float(self.tableWidgetPlatform.item(4, i).text())
                    carYaw = float(self.tableWidgetPlatform.item(5, i).text())

                    setting.setValue(f"platform{i}AirplaneLon", airplaneLon)
                    setting.setValue(f"platform{i}AirplaneLat", airplaneLat)
                    setting.setValue(f"platform{i}AirplaneYaw", airplaneYaw)
                    setting.setValue(f"platform{i}CarLon", carLon)
                    setting.setValue(f"platform{i}CarLat", carLat)
                    setting.setValue(f"platform{i}CarYaw", carYaw)
                except:
                    QMessageBox.warning(self,"参数错误!","平台参数表格第%d列参数错误!请检查!"%i)
                    return
                finally:
                    pass

            setting.endGroup()
            setting.beginGroup("otherPara")
            for i in range(self.tableWidgetOther.columnCount()):
                try:
                    lon = float(self.tableWidgetOther.item(0, i).text())
                    lat = float(self.tableWidgetOther.item(1, i).text())

                    setting.setValue(f"station{i + 1}Lon", lon)
                    setting.setValue(f"station{i + 1}Lat", lat)
                except:
                    QMessageBox.warning(self,"参数错误!","其他参数表格第%d列参数错误!请检查!"%i)
                    return
                finally:
                    pass


            QMessageBox.information(self, "保存成功!", "参数设置成功!")

        elif btn == self.btnCancel:
            self.close()

6、完整工程下载

文章顶部下载

二、PyQt6详解

1、PyQt6 概述

  • 核心:基于 Qt 6 库的 Python 绑定,提供跨平台 GUI 开发能力。
  • 特点
    • 支持 Windows、macOS、Linux 等系统。
    • 丰富的组件库(按钮、文本框、表格等)。
    • 信号槽机制实现事件驱动。
    • 可通过 Qt Designer 可视化设计界面。

2、安装

bash 复制代码
pip install PyQt6

3、基本结构

一个最小化 PyQt6 程序:

python 复制代码
import sys
from PyQt6.QtWidgets import QApplication, QLabel

app = QApplication(sys.argv)  # 创建应用对象
window = QLabel("Hello PyQt6!")  # 创建窗口组件
window.show()  # 显示窗口
sys.exit(app.exec())  # 进入事件循环

4、核心组件

4.1 、窗口类

  • QMainWindow:带菜单栏/状态栏的主窗口。
  • QDialog:对话框窗口。
  • QWidget:所有组件的基类。

4.2 、常用控件

python 复制代码
from PyQt6.QtWidgets import (
    QPushButton,  # 按钮
    QLineEdit,    # 单行文本框
    QTextEdit,    # 多行文本框
    QComboBox,    # 下拉框
    QCheckBox,    # 复选框
    QRadioButton  # 单选框
)

5、布局管理

  • QVBoxLayout:垂直排列组件。
  • QHBoxLayout:水平排列组件。
  • QGridLayout:网格布局。
python 复制代码
from PyQt6.QtWidgets import QVBoxLayout, QWidget

layout = QVBoxLayout()
layout.addWidget(QPushButton("按钮1"))
layout.addWidget(QPushButton("按钮2"))

container = QWidget()
container.setLayout(layout)  # 应用布局

6、信号与槽**

  • 信号(Signal):事件触发(如按钮点击)。
  • 槽(Slot):事件处理函数。
python 复制代码
button = QPushButton("点击")
button.clicked.connect(lambda: print("按钮被点击!"))

7、实战示例

python 复制代码
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout

class MyApp(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("PyQt6示例")
        layout = QVBoxLayout()

        btn = QPushButton("点我", self)
        btn.clicked.connect(self.on_click)
        layout.addWidget(btn)

        self.setLayout(layout)

    def on_click(self):
        print("按钮触发事件!")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = MyApp()
    ex.show()
    sys.exit(app.exec())

8、 进阶功能

  • 多线程 :使用 QThread 避免界面冻结。
  • 样式定制:通过 CSS 美化组件。
  • 图形绘制QPainter 实现自定义绘图。
  • 国际化QtLinguist 支持多语言。

9、资源工具**

  • Qt Designer :拖拽设计 .ui 文件,通过 pyuic6 转换为 Python 代码。

    bash 复制代码
    pyuic6 input.ui -o output.py
  • 官方文档PyQt6 Documentation

10、常见问题

  • 界面卡顿:耗时操作需放在子线程。
  • 内存泄漏 :避免循环引用,使用 QObject.deleteLater()
  • 跨平台适配:测试不同系统的显示效果。
相关推荐
fyzy1 个月前
PyQt6 提升自定义窗口部件
pyqt6
闲人编程6 个月前
PyQt6 进阶篇:构建现代化、功能强大的桌面应用
数据库·python·oracle·gui·脚本·pyqt6·软件
程序猿与金融与科技9 个月前
PyQt6实例_pyqtgraph散点图显示工具_代码分享
pyqt6·pyqtgraph·a股
程序猿与金融与科技10 个月前
PyQt6实例_消息工具_使用与完整代码分享
pyqt6·a股
程序猿与金融与科技10 个月前
PyQt6基础_pyqtgraph_折线图with缩放调节
python·pyqt6·pyqtgraph
程序猿与金融与科技10 个月前
PyQt6基础_QTableWidget
pyqt6
程序猿与金融与科技10 个月前
PyQt6实例_pyqtgraph多曲线显示工具_代码分享
pyqt6·pyqtgraph·a股
程序猿与金融与科技10 个月前
PyQt6实例_A股财报数据维护工具_解说并数据与完整代码分享
pyqt6·a股
程序猿与金融与科技10 个月前
PyQt6实例_A股日数据维护工具_界面代码
pyqt6·a股日数据