传送门:PyQt图像软件(基于项目的学习)
某医院发育筛查测试 量化软件
患者基本信息
筛查测试表(已有模板表格)
各项原始分值 运动、社会适应、智力;
各项计算分值 IQ EQ
对应建议
保存至本地(PDF/Word),考虑模板替换
基于python 3.6 + QT
因为量化表有三大分类,且各部分参数指标较多,因此采用堆叠布局的方式
主界面由两个frame构成
https://blog.csdn.net/qq_28077617/article/details/121876632
对于有多个输入框(lineEdit)时,可以考虑引入Tab快捷键,具体设置参考这篇文章;
https://blog.csdn.net/weixin_39626452/article/details/86700178?spm=1001.2014.3001.5506
UI
self.dateEdit = QtWidgets.QDateEdit(self.gridLayoutWidget)
self.dateEdit.setObjectName("dateEdit")
# 设置日期显示格式
self.dateEdit.setDisplayFormat('yyyy-MM-dd')
# 设置允许日历控件弹出
self.dateEdit.setCalendarPopup(True)
# 当日期改变时触发槽函数
self.dateEdit.dateChanged.connect(self.onTimeChanged)
业务代码
def onTimeChanged(self,time):
birthtime = time
print(birthtime)
# 获取当前时间
testTime = datetime.datetime.now().strftime('%Y-%m-%d')
# 回传子页面 setText设置输入框显示值
self.lineEdit_TestTime.setText(testTime)
print(testTime)
# 设置日期范围
self.dateEdit.setMinimumDate(QDate.currentDate().addDays(-365*10))
self.dateEdit.setMaximumDate(QDate.currentDate().addDays(0))
Stacked Widget,然后参考上面那篇文章,下面主要是注意事项;
# 建立堆叠布局信号与槽连接
self.pushButton_Sports.clicked.connect(self.frameController)
self.pushButton_Social.clicked.connect(self.frameController)
self.pushButton_Iq.clicked.connect(self.frameController)
如果将信号与槽建立的代码放在按钮创建代码前,就会出现
AttributeError: 'AppMainWindow' object has no attribute 'pushButton_Sports'
的错误
查看这篇文章
进行转换,
pyrcc5 -o re_rc.py 你自己的资源文件名.qrc
场景再现:如上图,我有很多复选框,我想依次获取复选框的选取状态;应该指出每个checkBox都有独一无二的名字,其格式一般为checkBox_XXX (xxx为编号),这种命名方式为我们遍历提供了便利;
参考这个回答,传统的for循环+拼接对象字符串是不可行的,因为你获得的是一个字符串而非对象名;**我们使用getattr()**方法来生成checkBOx对象名
# 这里的self是页面父类-主窗口
try:
# 针对运动表,判断其最后一个得分项
for i in range(1, 31):
# 遍历checkBox对象
m = getattr(self, "checkBox_%d" % i)
if m.isChecked():
# print("checked" + m)
print("num" + str(i) + ": " + str(m.isChecked()))
sports_pass = i
print("the final score is:" + str(sports_pass))
except Exception as e:
print(e)
这里要说明的是,如果你使用print(m)
想打出对象名字是会报错的,报错信息大概应该是:
must be str, not QCheckBox (这里也是为什么要进行异常捕获的原因)
这里的m可以认为是一个对象引用,print函数只能打印字符串,所以这里报错没毛病,我想这也是前面那篇回答有人运行会报错的原因;
如果硬要打印出m,可以使用str(m),这时你会发现它是一个object,打印出来的应该是地址?
主要用于生成关键字字典,来进行对比替换
参考这篇文章
pyqt5已经提供了文件资源对话框,即getSaveFileName;
filepath, type = QFileDialog.getSaveFileName(self, '报告保存', '/'+ '%s_报告单.docx' % model, 'docx(*.docx)')
# '报告保存' 是对话框的标题,中间那一长串是保存的路径,后面是默认保存位置
# 这里我是根据患者测试时间和姓名 默认给出了文件名字,使用%s 占位符 将字符串model和 路径以及后缀名拼接
doc_t.save(filepath)
# 再使用doc_t.save将文件保存至指定filepath
参考这篇文章
在打开保存对话框之后,如果取消保存,这时候程序会返回一个空路径(‘’),这时会报错,因此需要判断程序路径是否为空。
import datetime
import docx
from PyQt5.QtWidgets import QFileDialog
def createReport(self,list):
try:
# 需制作缴费通知单数据
report_data = list
# print(list)
# 模板内设置的标志:
tags_1 = ['name','id','gender','birthday','preweek','testdate','months','sports',
'social','intel','min','mi','dqn','dq','advice']
# print(report_data)
# 生成字典类型的数据集
notice_dict = dict(zip(tags_1, report_data))
# print(notice_dict)
doc_t, runs_t = get_runs('报告模板.docx')
# 遍历模板run对象 和 notice_dict key 匹配
# 匹配成功则替换 run 内容
for run_t in runs_t:
if run_t.text in notice_dict.keys():
run_t.text = str(notice_dict[run_t.text])
#生成格式化文件名
model = datetime.datetime.now().strftime('%Y%m%d') + '_'+ list[0]
# 文件保存路径
save_doc(self,doc_t,model)
except Exception as e:
print(e)
# 定义获取 word 模板正文段落、表格 run 对象函数
def get_runs(path):
doc = docx.Document(path)
runs_g = []
# 获取段落 run 对象
# print(doc.paragraphs)
for par_g in doc.paragraphs:
for run_g in par_g.runs:
runs_g.append(run_g)
# print(run_g.text)
# # 获取表格 run 对象
# table_g = doc.tables[0]
# for cell_g in table_g._cells:
# for cell_par_g in cell_g.paragraphs:
# for cell_run_g in cell_par_g.runs:
# runs_g.append(cell_run_g)
# print(cell_run_g.text)
return doc, runs_g
# 文件保存路径
def save_doc(self,doc_t,model):
filepath, type = QFileDialog.getSaveFileName(self, '文件保存', '/'+ '%s_报告单.docx' % model, 'docx(*.docx)')
print(filepath)
doc_t.save(filepath)
#
def trigger_helpDoc(self):
webbrowser.open('Help.pdf', new=1)
网上搜了很多,大部分是调用word接口,然后再Pyqt框架内展示文档;我这里的需求是使用python调用外部软件,使文档在相应软件中打开;
使用pywin32com库pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pypiwin32
from win32com.client import Dispatch
# 这里定义了打开的程序接口
app = Dispatch('Word.Application')
# 这里是打开文件的路径
app.Documents.Open(self.pathtemp)
# 设置word文件可见
app.Visible = True
time.sleep(0.5)
这里有一个问题,那就是我的报告必须是生成并保存了之后才能预览,我想在报告保存前进行预览,不过失败了
# 具体过程参见前面的 docx文件生成并保存
doc_t, runs_t = get_runs('报告模板.docx')
app.Documents.Open(doc_t)
生成不可执行文件
1、据了解是图标大小不对 16*16
2、ico文件路径 image\logo.ico 而不是\image\logo.ico
需要将图标文件拷贝到exe执行文件下,
这篇文章是yyds
建议在pipenv环境下依然使用 pyinstaller XXX.spec 配置文件来输出程序
我的软件从原来的200多M缩小到了40多兆;
直接先安装pip install PyQt5-sip== 12.9.1
然后再安装PyQt5成功
可以看到打包前资源文件全是散在文件夹下面,看起来很low
使用Winrar二次打包之后,文件变为一个整体:
使用Winrar
参考文章2
不过打包下来这个图标改不了,后面发现是我的Winrar版本问题,没有更改图标这个选项
这里一般是定义了函数 但没有给出返回值
这个不i管用
后面发现是我直接把.xlsx文档改为.csv的原因
参考这篇文字
def get_runs(path):
doc = docx.Document(path)
runs_g = []
# 获取段落 run 对象
print(doc.paragraphs)
for par_g in doc.paragraphs:
for run_g in par_g.runs:
runs_g.append(run_g)
print(run_g.text)
# 获取表格 run 对象
# table_g = doc.tables[0]
# for cell_g in table_g._cells:
# for cell_par_g in cell_g.paragraphs:
# for cell_run_g in cell_par_g.runs:
# runs_g.append(cell_run_g)
return doc, runs_g
为了查找问题,打印出遍历的过程,注意这里要使用run_g.text方法才能打出具体内容,否则只是打印出对象地址;
打印出来发现最后得到的是 a dvice,也就是advice被拆分开来了,所以最终替换的时候也没有找对对应的关键字;
解决办法是把模板中、程序中的advice和关键字又打了一遍,不知道为什么有这个问题???玄学???