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 代码。bashpyuic6 input.ui -o output.py -
官方文档 :PyQt6 Documentation
10、常见问题
- 界面卡顿:耗时操作需放在子线程。
- 内存泄漏 :避免循环引用,使用
QObject.deleteLater()。 - 跨平台适配:测试不同系统的显示效果。
