科学计算一直用matlab,用的顺手偶尔也写个界面,控制个硬件啥的,但是跨平台产生的执行文件带着runtime实在是太不方便了(偶然发现imgtest居然是matlab写的)。于是乎,抓紧学习一下python和QT制作GUI的技能。
对于python不能算零基础吧,几年前玩过一阵,那时候写了个自动挂号的程序,快到放号时间就自动登录,通过微信发送手机验证码,然后就开始挂号。由于太菜,还是没抢到号。不过python对于有一定编程基础的人来说还是很友好的,网上查一些资料,拼拼凑凑就可以做出自己想要的简单代码。于是这一次(其实已经第n次了,每次都终止于对QT的理解)依然用这个套路。
需要准备的知识
先前很多次的尝试,其实都止步在了2和4,对面向对象、槽函数等概念无法理解。这次硬着头皮,先从一些简单的例子,把这两个概念都理解了如何使用,虽然距离掌握还有距离,但自己写一些简单的GUI应该没问题了。这大概花了3个晚上,大约6个小时。
(一)寻找轮子
大家都喜欢把别人写好的代码叫轮子。我想做一个界面小软件,可以读取文件,然后把文件里的数据经过处理后,画成曲线。这在matlab里很简单,拖几个控件,写以下回调函数就可以了,非常方便,10分钟大概就能写好。但是就这么个事儿,我折腾了挺久的。首先遇到的问题就是用什么来做界面,可以搜索出很多,比如Tkinter和PysimpleGUI等,QT是其中一种,前面两种是直接用代码实现的,而QT是和matlab类似,可以拖动控件实现可视化的编辑界面,对于我来说是首选。但是它没有回调函数可以编辑,尝试了几次都没摸着头脑。后来还尝试了PysimpleGUI,据说非常简单,类似于搭积木把各个控件排起来(通过代码的形式),可以很快的实现一些简单的界面,当然官方给的例子有很多特别复杂的,甚至是游戏都有,功能也很强大,但我看那界面就是不舒服,觉得不好看,还是QT的界面很美观,而且编辑起来直观一些,最后也放弃了PysimpleGUI。那么就老老实实啃下QT这个骨头吧。
首先找个轮子,可以实现读取文件,并且画曲线的例子,例子里有整个过程,直接把代码复制过来就可以用,但是具体什么原理还不是很懂,于是开始研究什么原理。
(二)研究原理
首先需要理解,界面就是一串代码,使用qtdesigner设计的*.ui文件需要转化成py文件,这个py文件里包含了界面中所有组件的代码,被封装在一个类里(这里就需要用到面向对象的概念了),每个控件都是这个类的属性,属性可以修改。这时候我不理解的是里面的“self”参数,可以搜到很多资料,但是没有系统的学习,感觉还是无法理解很深刻,这个阶段只能照葫芦画瓢,它是函数或者类默认的输入参数。
有了界面的py文件,重新新建一个py文件,把界面py文件当成模块导入,就可以使用界面的类了。有些必须的q代码可以保存下来,以后直接用:
class Window(QMainWindow, Ui_MainWindow):
def __init__(self, app):
self.function1()
def function1
a=1
def function2(self):
a=2
def main():
app = QApplication(sys.argv)
mywindow = Window(app)
mywindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
其中if __name__ == '__main__':是代码执行的起始位置,这里只执行了一个main函数,当然也可以直接把main函数里的四行直接代替main函数,具体我就没再研究都是啥意思了。重点内容都在window类里。里面也有个初始化的函数,逐步执行各个函数。根据需要,修改其中的函数就可以了。
(三)改造轮子
如上所说,只要修改window类中的函数内容就可以了,我主要需要的功能是点击一个按钮实现文件的选择,将路径保存下来,然后再按一个按钮,把这个文件的数据读取出来,并进一步处理后画出曲线。那么需要搞清楚,按按钮如何实现相应的功能呢?这就是qt槽函数的机制,实际上就是控件的一个属性(类似于函数,其输入就是你需要执行的函数),如下所示:
self.pushButton2.clicked.connect(self.dataprocess)
pushButton2就是控件的名字,clicked表示事件的名字,后面的内容表示这个事件会触发dataprocess这个函数,这个函数目前我是定义在了window类里,还没搞清楚类外的函数如何使用。到这里其实就结束了,其它的如怎么实现画曲线,怎么读取文件,这都可以找到相应的轮子,就不需要深入研究了。有需要的时候可以再查资料进一步修改。
最后再提以下,信号的传递,如果上面的dataprocess需要输入参数,不能直接输入,否则程序一运行就会触发这个函数,需要利用qt的信号传输机制,也可以查到,我用的是lambda表达式,具体啥原理没搞清楚。