之前在帮朋友写数据处理程序时刚接触GUI编程,写的界面真是稀烂,文章链接: python制作GUI可视化程序 ,这一次刚好老师也有个数据处理的需求,正好深度学习一下GUI编程。
上一次编程完全没有借助工具,单纯靠一个vs code撸代码着实是麻烦且不实用,本次编程前先做了准备工作,下载pycharm社区版本工具,配置3种外部工具(名字可自定义),Qt Designer+PyUIC+PyRCC,先说下他们的作用,Qt Designer用于通过pycharm启动Qt Designer软件,PyUIC用于把Qt Designer生成的UI文件一键转成python文件,PyRCC是图片资源的加载(我用的很少),也就是说,其实这三个工具并非必须项,只是为了便捷开发而已,网上参考文件很多,可以自行了解具体配置项。
写GUI程序之前,当然首先得把python程序写好才好整理界面逻辑,我是使用了jupyter notebook先行写好了处理程序,再开始写exe程序,具体的python处理程序代码我放在了文末以供参考,本篇文章主要介绍pyqt5。
配置好外部工具之后就可以直接通过如下路径直接启动designer了
我们在designer中设计完界面,保存之后可以看到在目录下会生成一个以ui后缀结尾的文件,选中这个文件(不要选错了文件,否则会把你现有的python文件清空),再点击外部工具中的PyUIC,即可生成一个对应的python文件
这里面有一个问题,如果在转化后的python文件中写代码逻辑(上图中为first.py),你会发现每次重新设计了ui,自己写的逻辑代码都会被清空,所以最好的办法是让逻辑与界面分离。
我们可以新建一个入口文件,上图中为xiaoduV1.py,代码如下:
from PyQt5 import QtWidgets,QtGui,QtCore
from py 文件名 import 类名
class MainWindow(QtWidgets.QMainWindow, 类名):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
# 此处编写业务逻辑代码
if __name__ == "__main__":
importsys
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
这样每一次都不用担心要修改UI窗口了。
xiaoduV1.py代码如下,对于widget的部件使用就不再介绍了,看下源文档的函数基本能了解它的作用,网上参考文档也很多
# -*- coding: utf-8 -*-
from PyQt5 import QtWidgets,QtGui,QtCore
from PyQt5.QtWidgets import QMessageBox,QFileDialog
from first import Ui_MainWindow
import pandas as pd
import numpy as np
import warnings
from pathlib import Path
import random
from datetime import date
warnings.simplefilter("ignore")
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.pushButton.clicked.connect(self.processFile)
self.pushButton_2.clicked.connect(self.getFilePath)
# 此处编写业务逻辑代码
self.file_flag = False
def getFilePath(self):
self.file_flag = True
base_dir = QFileDialog.getExistingDirectory(self, "请选择文件夹路径", "C:")
self.base_dir = base_dir
self.label_3.setText(self.base_dir)
def processFile(self):
if not self.file_flag:
QMessageBox.warning(self, "请注意", "请输入文件夹路径", QMessageBox.Ok)
return
base_dir = Path(self.base_dir)
input_text = self.textEdit.toPlainText()
if not input_text:
QMessageBox.warning(self,"请注意","请输入考点内容",QMessageBox.Ok)
return
input_list = input_text.strip("\n").strip("%").split("%")
kaodian= []
for i in range(len(input_list)):
each_kaodian_str = input_list[i].split(",")[0].strip()
each_number_str = int(input_list[i].split(",")[1].strip())
kaodian.append((each_kaodian_str, each_number_str))
output_dir = base_dir / "output"
Path(output_dir).mkdir(parents=True, exist_ok=True)
data_list_file = []
for file in base_dir.glob("*.xlsx"):
_ = pd.read_excel(file, sheet_name='需标考点题目表', dtype=object)
data_list_file.append(_)
zxkd_df = pd.read_excel(file, sheet_name='最小考点', header=None, index_col=0, dtype=object)
data_concat = pd.concat(data_list_file)
data_concat = data_concat[[x for x in data_concat.columns if "Unnamed" not in x]]
data_concat.dropna(subset=['题目ID'], inplace=True)
data_concat.dropna(axis='columns', how='all', inplace=True)
data_concat.fillna(value="None", inplace=True)
data_concat['题目ID'] = data_concat['题目ID'].apply(lambda x: str(x).replace(".0", ""))
data_concat.to_excel(output_dir / Path('xlsx合并文件供检查参考.xlsx'), index=False)
choices = zxkd_df.index.values
data_concat['kaodian_list'] = data_concat['题目ID'].map(str) + "," + data_concat['新标考点(最小级)第一考点'] + "," + data_concat['新标考点(最小级)第二考点'] + "," + data_concat['新标考点(最小级)第三考点'] + "," + data_concat['新标考点(最小级)第四考点']
concat_dict = {}
for choice in choices:
empty_list = []
data_concat['kaodian_list'].apply(self.getIDlist, zxkd=choice, empty_list=empty_list)
concat_dict.update({choice: list(set(empty_list))})
group_number = self.spinBox.value()
output_dict = {}
# 定义在全局的编号列表
is_already_used_list = []
for h in range(len(kaodian)):
for k, v in concat_dict.items():
wanna_id_list = []
all_id_list = []
if k == kaodian[h][0]:
all_id_list = v
for j in range(group_number):
m_group = random.sample(all_id_list, kaodian[h][1])
is_already_used_list += m_group
is_already_used_list = list(set(is_already_used_list))
all_id_list = list(set(all_id_list) - set(is_already_used_list) - set(m_group))
wanna_id_list.append(m_group)
if not all_id_list:
print("questions is less than you want!")
output_dict.update({f"{k}": wanna_id_list})
pd.DataFrame(output_dict).to_excel(output_dir / Path(f'output-file-{date.today()}.xlsx'), index=False)
print("end!")
self.label_4.setText("Success!")
def getIDlist(self,kaodian_list, zxkd, empty_list):
if zxkd in kaodian_list:
number = kaodian_list.split(",")[0]
empty_list.append(str(number))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
pyinstaller打包注意事项,你所打包的模块必须是在全局环境变量下拥有,举个例子,我的虚拟环境中存在pyqt5,但是全局环境下没有安装,打包的exe程序运行就会闪退,你可以通过命令行CMD模式下运行exe程序即可看到报错提示,例如我的错误提示就是“pyqt5模块未找到”。
pyinstaller -F -w x.py
-F , --onefile 打包成單一個執行檔,但是打开软件可能会很慢
-w , --windowed , --noconsole 打包時會去除命令視窗
参考:# pycharm配置pyqt5
https://blog.csdn.net/qq_35451572/article/details/85229408