因为需求需要写一个简单的Python GUI界面,期间遇到了一些问题,在这里记录下
安装PyQt6:
pip install pyqt6
使用QTDesigner绘制界面:
我使用Anaconda下载的pyqt6里已经自带了两种工具,下面只需要把工具导入到pycharm中,在settings-External Tools中导入QTDesinger和pyuic两个工具:
其中的参数配置我是这样的,A是我的用户名:
QTDesinger
Program:
C:\Users\A\.conda\envs\python37\Lib\site-packages\qt6_applications\Qt\bin\designer.exe
Working directory:
C:\Users\A\.conda\envs\python37\Lib\site-packages\qt6_applications\Qt\bin
pyuic:
Program:
C:\Users\A\.conda\envs\python37\python.exe
Arguments:
-m PyQt6.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py
Working directory:
$FileDir$
配置好以后,在配置好的tools里就可以找到刚刚配置的工具
在里面拖动好边框以及按钮后,保存会生成ui文件。在ui文件上右键选择工具pyuic,即可在对应的文件夹下生产对应的py文件
在生成的界面下添加调用的代码来生成显示
from PyQt6 import QtCore, QtWidgets
import sys
if __name__ == "__main__":app = QtWidgets.QApplication(sys.argv)MainWindow = QtWidgets.QMainWindow()ui = Ui_Form()#这个是生成的界面窗口类名ui.setupUi(MainWindow)MainWindow.show()sys.exit(app.exec())
把按钮和函数链接起来,实现点击按钮触发对应函数self.inforFileSelect:
self.pushButton.clicked.connect(self.inforFileSelect)
实现选择文件的功能,并把选择的文件名写到界面上:
def inforFileSelect(self):# 选择文件result = QtWidgets.QFileDialog.getOpenFileName()self.lineEdit.setText(result[0])
选择文件夹:
def saveDirSelect(self):# 选择保存文件夹result = QtWidgets.QFileDialog.getExistingDirectory()self.lineEdit_3.setText(result)
在写好界面运行后我发现存在两个问题,一个是发现变更标签上的文字不会实时更新,另一个是发现界面常常出现无响应的情况。
解决方法:
用 QtWidgets.QApplication.processEvents()实时刷新变更的文字:
def changeLabel(self, text="hi"):self.label.setText(text)QtWidgets.QApplication.processEvents()
通过多线程的方法来解决界面无响应的情况:
def processFiles_clicked(self):if self.running_status == 'stop':self.running_status = 'running'from threading import Threadinfor_xml = self.lineEdit.text()scheme_xml = self.lineEdit_2.text()save_dir = self.lineEdit_3.text()thread = Thread(target=process_file, args=(infor_xml, scheme_xml, save_dir, self))thread.start()else:self.changeLabel("正在生成中,别急!")def process_file(infor_xml, scheme_xml, save_dir, Ui_Form):try:Ui_Form.changeLabel("正在读取用户表,请稍后")infor_dict = getUserInfor(infor_xml)Ui_Form.changeLabel("正在读取排班表,请稍后")infor_dict, data_loc, name_list = getSchemeInfor(infor_dict, scheme_xml)for each_date in data_loc:current_date = str(each_date.month) + "." + str(each_date.day)Ui_Form.changeLabel("正在生成" + current_date + "的排表文件")saveInfor(each_date, infor_dict, save_dir, name_list)Ui_Form.changeLabel("生成完成!")Ui_Form.running_status = 'stop'except Exception as e:Ui_Form.changeLabel("出错了,这是报错信息:" + str(e))Ui_Form.running_status = 'stop'
注意这种多线程的方法,需要把耗时的任务写在界面类外,如果写在界面类里作为成员方法,即使使用了多线程还是会卡死,因为这么做还是占用了主界面的那个线程。通过在类里的方法,把self传给外面的函数,让外面的函数可以通过self直接调用对象的方法,来达到更改标签和参数传递的效果。
其实如果用多进程也可以解决卡死的问题,但是多进程的话,不能再把实例self传给外面函数了,这么做的话在启动多进程的时候会复制一遍内容导致不断套娃耗尽资源,因此多进程需要通过其他方式来解决信息传递的问题。
最后附上完成的代码:
from PyQt6 import QtCore, QtWidgets
from SchemeLoader import getSchemeInfor
from UserLoader import getUserInfor
from InforSaver import saveInfordef process_file(infor_xml, scheme_xml, save_dir, Ui_Form):try:Ui_Form.changeLabel("正在读取用户表,请稍后")infor_dict = getUserInfor(infor_xml)Ui_Form.changeLabel("正在读取排班表,请稍后")infor_dict, data_loc, name_list = getSchemeInfor(infor_dict, scheme_xml)for each_date in data_loc:current_date = str(each_date.month) + "." + str(each_date.day)Ui_Form.changeLabel("正在生成" + current_date + "的排表文件")saveInfor(each_date, infor_dict, save_dir, name_list)Ui_Form.changeLabel("生成完成!")Ui_Form.running_status = 'stop'except Exception as e:Ui_Form.changeLabel("出错了,这是报错信息:" + str(e))Ui_Form.running_status = 'stop'class Ui_Form(object):# 设置一个变量监控运行状态running_status = "stop"def setupUi(self, Form):Form.setObjectName("Form")Form.resize(471, 704)self.pushButton = QtWidgets.QPushButton(Form)self.pushButton.setGeometry(QtCore.QRect(170, 100, 121, 41))self.pushButton.setObjectName("选择人员信息表")self.label = QtWidgets.QLabel(Form)self.label.setGeometry(QtCore.QRect(80, 550, 321, 81))self.label.setAutoFillBackground(True)self.label.setObjectName("label")self.lineEdit = QtWidgets.QLineEdit(Form)self.lineEdit.setGeometry(QtCore.QRect(70, 30, 341, 51))self.lineEdit.setObjectName("lineEdit")self.lineEdit_2 = QtWidgets.QLineEdit(Form)self.lineEdit_2.setGeometry(QtCore.QRect(70, 190, 341, 51))self.lineEdit_2.setObjectName("lineEdit_2")self.pushButton_4 = QtWidgets.QPushButton(Form)self.pushButton_4.setGeometry(QtCore.QRect(170, 480, 121, 41))self.pushButton_4.setObjectName("pushButton_4")self.lineEdit_3 = QtWidgets.QLineEdit(Form)self.lineEdit_3.setGeometry(QtCore.QRect(70, 340, 341, 51))self.lineEdit_3.setObjectName("lineEdit_3")self.pushButton_2 = QtWidgets.QPushButton(Form)self.pushButton_2.setGeometry(QtCore.QRect(170, 260, 121, 41))self.pushButton_2.setObjectName("pushButton_2")self.pushButton_3 = QtWidgets.QPushButton(Form)self.pushButton_3.setGeometry(QtCore.QRect(170, 410, 121, 41))self.pushButton_3.setObjectName("pushButton_3")self.retranslateUi(Form)QtCore.QMetaObject.connectSlotsByName(Form)def inforFileSelect(self):# 选择文件result = QtWidgets.QFileDialog.getOpenFileName()self.lineEdit.setText(result[0])def schemeFileSelect(self):# 选择文件result = QtWidgets.QFileDialog.getOpenFileName()self.lineEdit_2.setText(result[0])def saveDirSelect(self):# 选择保存文件夹result = QtWidgets.QFileDialog.getExistingDirectory()self.lineEdit_3.setText(result)def changeLabel(self, text="hi"):self.label.setText(text)QtWidgets.QApplication.processEvents()def processFiles_clicked(self):if self.running_status == 'stop':self.running_status = 'running'from threading import Threadinfor_xml = self.lineEdit.text()scheme_xml = self.lineEdit_2.text()save_dir = self.lineEdit_3.text()thread = Thread(target=process_file, args=(infor_xml, scheme_xml, save_dir, self))thread.start()else:self.changeLabel("正在生成中,别急!")def retranslateUi(self, Form):_translate = QtCore.QCoreApplication.translateForm.setWindowTitle(_translate("Form", "表格信息整合工具"))self.pushButton.setText(_translate("Form", "选择人员信息表"))self.pushButton.clicked.connect(self.inforFileSelect)self.label.setText(_translate("Form", ""))self.pushButton_2.setText(_translate("Form", "选择排班表"))self.pushButton_2.clicked.connect(self.schemeFileSelect)self.pushButton_3.setText(_translate("Form", "选择保存文件夹"))self.pushButton_3.clicked.connect(self.saveDirSelect)self.pushButton_4.setText(_translate("Form", "导出明细表"))self.pushButton_4.clicked.connect(self.processFiles_clicked)if __name__ == "__main__":import sysapp = QtWidgets.QApplication(sys.argv)MainWindow = QtWidgets.QMainWindow()ui = Ui_Form()ui.setupUi(MainWindow)MainWindow.show()sys.exit(app.exec())
界面效果: