PyQt5入门教程2019/12/11更新:我平时不看CSDN的,之前一时兴起发了过来,没想到反响还不错。这次就顺便把后来新增的一个小节放上来,并且在文末增加了我的GitHub(一看GitHub就知道我是个菜鸡,大家都是互相学习啦~) 注:这是当时闲着无聊写到github page的,在CSDN上也看了大佬们各种各样的教程跟疑难杂症解答,感觉我这个不放出来也有点可惜,希望各位能够从中收益吧。 在网上看了不少关于PyQt5的中文教程,但是无外乎是过时了,讲解不清晰易懂,或者资料不完整。Youtube上面倒是有不少视频,但是不少Youtuber居然还在手写ui而不是利用方便快捷的Qt Designer。仅有的几个视频虽然利用了Qt Designer来设计UI,但是他们并没有将UI跟逻辑分离,这种行为并不是我期望的。 为此,我花费了不少时间在网上寻找各种资料。于是乎,我最终还是下定决心把自己的学习过程给记录下来。记录下来是给我自己复习跟参考的,如果有人能够从中受益,那也挺好,不用浪费时间去到处找答案。 0x00 安装环境清单我使用的环境如下:
如果你使用的是OSX或者Linux,请自行替换教程中的一些操作。 本文并不讨论Python和VSCode的安装,如果没有VSCode,你可以用各种同类IDE替代或者安装它。 本文不讨论多Python共存,毕竟Python2.7在2020年就要退役了,而且我本人也没这需求。 0x01 安装PyQt5下面直接使用pip来安装PyQt5,此处可能是pip/pip3,或者两者皆可,后面不再重复 直接pip安装PyQt5
由于Qt Designer已经在Python3.5版本从PyQt5转移到了tools,因此我们还需要安装pyqt5-tools
到这一步,PyQt5就安装完成了,你可以通过下面若干可选的操作来检查是否已经安装成功:
0x02 初识Qt Designer注:Qt Designer的界面是全英文的,幸运的是有汉化方法,不过因为我本人用不上,所以如果有这方面需求可以自行搜索。 我比较习惯用Win+S呼出Cornata主面板(搜索框)来启动各种应用,那么这里就是在搜索框中输入designer并敲回车,就能够启动Qt Designer了。 初次启动会弹出这个“New Form”窗口,一般来说选择“Main Window”然后点击“Create”就可以了。下方有个“Show this Dialogue on Startup”的checkbox,如果不想每次启动都看到这个“New Form”窗口,可以取消勾选。
大致了解了每个板块之后,就可以正式开始编写第一个UI了 0x03 HelloWorld!注:从这里开始,相关代码可以在/assets/code/pyqt5中找到 注:本文用到的代码都在我github,就不在CSDN这里上传了 通常来说,编写GUI有两种方法:第一种就是直接使用方便快捷的Qt Designer,第二种就是写代码。在有Qt Designer的情况下,是完全不推荐费时费力去手写GUI代码的。Qt Designer可以所见即所得,并且可以方便的修改并做出各种调整。 按照惯例,我们先来实现一个能够显示HelloWorld的窗口。 1)添加文本 在左侧的“Widget Box”栏目中找到“Display Widgets”分类,将“Label”拖拽到屏幕中间的“MainWindow”画布上,你就获得了一个仅用于显示文字的文本框,如下图所示。 2)编辑文本 双击上图中的“TextLabel”,就可以对文本进行编辑,这里我们将其改成“HelloWorld!”,如下图所示。如果文字没有完全展示出来,可以自行拖拽空间改变尺寸。 特别提醒,编辑完文本之后记得敲击回车令其生效! 3)添加按钮 使用同样的方法添加一个按钮(PushButton)并将其显示的文本改成“HelloWorld!”,如下图所示。 4)修改窗口标题 下面修改窗口标题。选中右上方的"Object Inspector"中的“MainWindow”,然后在右侧中部的"Property Editor"中找到“windowTitle”这个属性,在Value这一栏进行修改,修改完记得敲击回车。 5)编辑菜单栏 注意到画布的左上方有个“Type Here”,双击它即可开始编辑菜单栏。菜单栏支持创建多级菜单以及分割线(separator)。我随意创建了一些菜单项目,如下图所示。 6)预览 使用快捷键Ctrl+R预览当前编写的GUI(或者从菜单栏的Form > Preview / Preview in进入) 7)保存 如果觉得完成了,那就可以保存成*.ui的文件,这里我们保存为HelloWorld.ui。为了方便演示,我将文件保存到D盘。 8)生成Python代码 使用cmd将目录切到D盘并执行下面的命令。请自行将下面命令中的name替换成文件名,比如本例中的“HelloWorld.ui”
生成的代码应该类似下图所示 9)运行Python代码 此时尝试运行刚刚生成的“HelloWorld.py”是没用的,因为生成的文件并没有程序入口。因此我们在同一个目录下另外创建一个程序叫做“main.py”,并输入如下内容。在本例中,gui_file_name就是HelloWorld,请自行替换。
然后运行“main.py”,你就能看到刚刚编写的GUI了! 10)组件自适应 如果你刚刚尝试去缩放窗口,会发现组件并不会自适应缩放,因此我们需要回到Qt Designer中进行一些额外的设置。 点击画布空白处,然后在上方工具栏找到grid layout或者form layout,在本例中我们使用grid layout。两种layout的图标如下图所示。 顺带一提,上图中layout的左边有三条横线以及三条竖线的图标,这两个是用于对齐组件,非常实用。 设置grid layout后,我们使用Ctrl+R预览,这次组件可以自适应了!因为我们已经将UI(HelloWorld.py/HelloWorld.ui)跟逻辑(main.py)分离,因此直接重复步骤7-8即可完成UI的更新,无需改动逻辑(main.py)部分。 0x04 Interaction刚刚写的HelloWorld中,我们设置的按钮(PushButton)是没有实际作用的,因为我们并没有告诉这个按钮应该做什么。实际上,要让这个按钮做点什么只需要增加一行代码就可以了。 1)获取按钮id 打开HelloWorld.ui,在designer中选中对应的按钮,从“Property Editor”中可以得知这个按钮的“objectName”叫做“pushButton”,如下图所示。 2)设置触发 Qt中有“信号和槽(signal and slot)”这个概念,不过目前无需深究,也无需在Designer中去设置对应按钮的“信号和槽”,直接在“main.py”中“MainWindow.show()”的后面加入下面这样的一行代码
下面简单解释下这行代码
3)设置函数 既然刚刚设置了按钮的触发并绑定了一个函数click_success,我们就要在“main.py”中实现它。示例如下
4)运行! UI跟逻辑分离的好处就在这里,我们这次不用去管“HelloWorld.py”了,直接运行修改完的“main.py”。点击按钮,这次你会发现在控制台中有了我们预设的输出。 0x05 Conversion这次我们来进行实战演练,编写一个带GUI的汇率转换器。 1)设计UI 通过上面的讲解,你应该能够毫无压力的设计上面这样的UI并获得对应的代码。如果不行,那么不建议继续往下阅读,应当回头复习。 2)传参 现在我们有了GUI的代码以及上一节中使用的“main.py”,我们可以开始编写这个汇率转换器的逻辑部分。 在上一节,我们介绍了如何让按钮响应点击操作,但是并没有接受任何参数,而且只是在控制台输出。但是,上一节中说明了并不能通过正常的方式进行传参。因此,对于传参,有两种解决方案,一种是使用lambda,还有一种是使用functool.partial。在接下来的环节中我们会使用partial。 partial的用法如下所示:
既然使用partial传参,那么我们就要在程序(main.py)的头部加上下面这行。
然后我们把上一节中的按钮触发那行代码修改成下面这样。
3)编写convert函数 首先,我们要获取用户输入的数字。为了使得教程简洁易懂,我们这次只讲解单向的汇率转换。既然是单项的转换,那么我们只需要获取左侧的文本框id。在本例中,左侧的文本框id为lineEdit。如果你对此感到一头雾水,请停下并回头复习。 获取文本使用的是text()方法,因此读取用户输入的代码如下
接着我们进行汇率转换,注意这里要进行类型转换
最后我们让右边的文本框显示结果
下面是convert函数的代码
一个简单的汇率转换器就这样诞生了! 那么,如何知道一个组件都有什么方法呢?直接去Qt官方文档查看就可以了。本节使用到的lineEdit的相关方法在这里 0x06 threading1)前言 这几天在用PyQt5写东西的时候遇到这样一个问题,网上资料也特别少,我感觉值得拿出来说一说。 我的程序中使用了threading模块,GUI作为主线程去启动负责逻辑处理的子线程。其中,我设计的GUI里头有一个日志框,用来代替终端显示各种日志输出。既然子线程是负责逻辑处理,那么想当然的就会直接在子线程操作GUI的显示。 都说了想当然,那当然不行咯,在子线程对GUI操作的时候,终端会出现下面这个错误,但是程序又不会马上闪退。
更让人摸不着头脑的是,过一阵子闪退的时候,会出现下面这句话:
这啥玩意儿?能说人话么?一番搜索之后,发现这个原来英语叫做“Segmentation fault (core dumped)”。 "Segmentation fault"用人话来说大概就是“你尝试访问你无法访问的内存”。 然后我把上面的报错信息搜索了下,发现之前有人在StackOverflow问过,但是答案牛头不对马嘴,不过倒是在评论区发现了大佬的留言。
简而言之,就是说虽然报错显示QTextCursor,但是实际上是在其它线程通过Qt内部的方法间接调用了这个东西。 热心大佬还留了个链接,我跟过去看了,收获不少。
这个终于说到点子上了,一句话总结就是子线程不能调用主线程的QtGui类。 所以大佬给出的方案如下:
大概翻译下,就是说可以通过信号和槽来完成子线程跟GUI所在的主线程的通信,就是通过在子线程释放信号,传递到主线程的槽来完成。 可惜的是,大佬并没有给出示例代码,那接下来就是动手实践了。 2)实践 首先我们在子线程的代码中创建一个对象,并且继承QObject(因为需要释放信号)。
做完这些之后,主线程中别忘了连击信号和槽,比如 不仅如此,其实其它需要共享的信息,也可以通过自定义信号和槽来传递。 那么,现在就可以愉快的在PyQt程序中使用threading模块了。 0x0? 小结本文只是抛砖引玉,上面这些只是PyQt5的入门内容。不过学会了简单的交互方法,其它的也差不多能依葫芦画瓢做出来。 本文中设计的程序在/assets/code/pyqt5中。 那么,就先写到这里了! 0xff Info我不是大佬,不是CS专业的,水平相对比较菜,只是一个对计算机感兴趣的业余爱好者,在这方面基本上是自学成才,毕竟兴趣是第一生产力嘛~ 其中这个tree项目的GitHub Page就放了些乱七八糟的自学笔记,有到处搜刮的也有原创的。有帮助的话不如给我这个tree项目点个星呗(手动滑稽)。 |
|