PyQt5学习日记(一):第一个GUI程序

参考链接:https://zhuanlan.zhihu.com/xdbcb8?utm_source=wechat_session&utm_medium=social&from=singlemessage

一、前言

PyQt5是基于Digia公司强大的图形程式框架Qt5的python接口,由一组Python模块构成。PyQt5本身拥有超过620个类和6000函数及方法。在可以运行于多个平台,包括:Unix, Windows, and Mac OS。

PyQt5的类存在于如下模块当中:

  • QtCore

  • QtGui

  • QtWidgets

  • QtMultimedia

  • QtBluetooth

  • QtNetwork

  • QtPositioning

  • Enginio

  • QtWebSockets

  • QtWebKit

  • QtWebKitWidgets

  • QtXml

  • QtSvg

  • QtSql

  • QtTest

QtCore模块涵盖了包的核心的非GUI功能,此模块被用于处理程序中涉及到的 time、文件、目录、数据类型、文本流、链接、mime、线程或进程等对象。

QtGui模块涵盖多种基本图形功能的类; 包括但不限于:窗口集、事件处理、2D图形、基本的图像和界面 和字体文本。

QtWidgets模块包含了一整套UI元素组件,用于建立符合系统风格的classic界面,非常方便,可以在安装时选择是否使用此功能。

QtMultimedia模块包含了一套类库,该类库被用于处理多媒体事件,通过调用API接口访问摄像头、语音设备、收发消息(radio functionality)等。

QtBluetooth模块包含了处理蓝牙活动的类库,它的功能包括:扫描设备、连接、交互等行为。

QtNetwork模块包含用于网络编程的类库,这组类程序通过提供便捷的TCP/IP 及 UDP 的 c/s 程式码集合,使得基于Qt的网络编程更容易。

QtPositioning模块用于获取位置信息,此模块允许使用多种方式达成定位,包括但不限于:卫星、无线网、文字信息。此应用一般用于网络地图定位系统。

Enginio模块用于构建客户端的应用程式库,用于在运行时访问 Qt Cloud 服务器托管的应用程序。

QtWebSockets模块包含了一组类程序,用以实现websocket协议。

QtWebKit模块包含了用于实现基于webkit2的网络浏览器的类库。

QtWebKitWidgets模块包含用于基于WebKit1的Web浏览器实现的类,用于基于QtWidgets的应用程序

QtXml模块包含了用于处理XML的类库,此模块为SAX和DOM API 的实现提供了方法。

QtSvg模块通过一组类,为显示矢量图形文件的内容提供了方法。

QtSql模块提供了数据库对象的接口以供使用

QtTest模块包含了可以通过单元测试,以调试PyQt5应用程式的功能。

PyQt5并不向下兼容PyQt4,主要是由于其有几个较大的改变。虽不兼容,但是旧代码调整到新库并不是很难。它们的主要差异如下:

Python的模块已经重新构建,一些模块已经被放弃,如:QtScript。其他的模块被分割到一些子模块当中,如:QtGui,QtWebkit。

一些新的模块会推出,如:QtBluetooth,QtPositioning或Enginio。

PyQt5仅支持新型信号和插槽。对SIGNAL()或SLOT()的调用不再支持(这点很重要哦!)。

PyQt5不支持在Qt v5.0中标记为已弃用或过时的Qt API的任何部分。


二、环境搭建

1. 首先在控制台输入如下命令

pyqt5是Python中实现GUI的一个模块,而pyqt5-tools包含了Qt designer这样的绘图软件。

pip install pyqt5

pip install pyqt5-tools

2. 配置PyCharm

1)打开PyCharm,执行快捷键ctrl+alt+s打开设置界面,输入tool,点开external tools,配置qtdesigner。

点击左上角的加号,作如下配置:

在Qt Designer的设置中,Program选择PyQt安装目录中 designer.exe 的路径

Work directory 使用变量 $FileDir$ (点击后面的 Insert macro 按钮可以不用输入双击上屏)

2)设置“PyUIC” — 这个主要是用来将 Qt界面 转换成 py代码

在PyUIC的设置中,其他的都差不多,Program 写入Python的地址,Parameters写入

1
-m PyQt5.uic.pyuic  $FileName$ -o $FileNameWithoutExtension$.py

把上面的Python路径修改成自己的即可!

Work directory 使用变量 $FileDir$

三、第一个PyQt5程序

利用PyQt5写界面的话,可以通过手动代码实现;也可以先通过Qt designer生成.ui文件,然后利用PyUIC将.ui文件转成.py文件。我主要介绍手动代码实现(因为ui我学不会!emmmm)。所以不懂刚才的环境配置也没关系,只要你装了PyQt5这个库就可以啦!

老规矩,先上代码~


3.1 第一个窗口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# coding = utf-8

import sys
from PyQt5.QtWidgets import QApplication, QWidget

if __name__ == '__main__':

app = QApplication(sys.argv)

w = QWidget()
w.resize(600,300)
w.move(600,600)
w.setWindowTitle('QzmVc1')
w.show()

sys.exit(app.exec_())

执行的结果如下图:

我想这个大概就是最简单的图形界面程序了,虽然简单但是确实现了图形界面程序均有的功能。

import sys
from PyQt5.QtWidgets import QApplication, QWidget

这段代码表明我们引入了python的相关模块,这就包括了sys模块,以及PyQt5中的QtWidgets模块。我们在之前(上面)描述了QtWidgets模块包含了一整套UI元素组件,用于建立符合系统风格的classic界面,非常方便。同时为了更方便的使用我们还明确了使用QtWidgets模块中的QApplication, QWidget。

app = QApplication(sys.argv)

每个PyQt5应用程序必须创建一个应用程序对象。 sys.argv参数是来自命令行的参数列表。 Python脚本可以从shell运行。 写了这句话就能让我们的程序从命令行启动。

w = QWidget()

QWidget小部件是PyQt5中所有用户界面对象的基类。 我们提供了QWidget的默认构造函数。 默认构造函数没有父类。 没有父类口小部件称为窗口。

w.resize(600,300)

resize()方法调整窗口小部件的大小。这里我们设定窗口的大小:宽600像素,高300像素。

w.move(600,600)

move()方法将小部件移动到屏幕上x = 600,y = 600坐标处的位置。这里我们来看一下屏幕坐标是怎么设定的,来看看下图。

通过上图我们可以知晓,坐标原点是从哪里开始计算的,我们要是将程序中的(600,600)这个坐标改成(0,0),窗口就会跑到左上角去了,大家可以试试。

w.setWindowTitle(‘QzmVc1’)

这个方法顾名思义,就是设置窗口的标题。

w.show()

show()方法在屏幕上显示窗口小部件。 一个小部件首先在内存中创建,然后在屏幕上显示。

sys.exit(app.exec_())

最后,我们进入应用程序的主循环(main loop)。 事件处理从这一点开始。 主循环(main loop)从窗口系统接收事件并将它们分派到应用程序小部件。 如果我们调用exit()方法或者主窗口小部件被破坏,那么主循环(main loop)就会结束。

sys.exit()方法确保一个干净的退出。

exec_()方法有一个下划线。 这是因为exec是一个Python关键字。 因此,使用exec_()。

注意:为了便于初学者理解,我们把主循环(main loop)简单的谈一下。GUI应用程序都是事件驱动的。比如键盘事件、鼠标事件等等。还有一些事件来自于系统内部,比如定时事件、其它文件事件等等。在没有任何事件的情况下,应用程序处于睡眠状态。这种事件驱动机制,GUI应用程序都需要一个主循环(main loop)。主循环(main loop)控制应用程序什么时候进入睡眠状态,什么时候被唤醒。所以主循环(main loop)就是干这个的。


3.2 为窗口设置图标

接下来呢,我们把这个窗口美化一下,增加一个图标。当然写法上调整一下,上面的代码基本上是按照面向过程来写的,下面开始我们采用面向对象的方式进行编码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#coding = utf-8

import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtGui import QIcon

class Ico(QWidget):

def __init__(self):
super().__init__()
self.initUI()

def initUI(self):

self.setGeometry(600, 600, 600, 300)
self.setWindowTitle('QzmVc1')
self.setWindowIcon(QIcon(path)) # path填图片路径名
self.show()

if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Ico()
sys.exit(app.exec_())

下面我们对新出现的代码进行相应的解析。

from PyQt5.QtGui import QIcon

从PyQt5.QtGui中引入QIcon这个类,也是为了便于图标的设定。

1
2
3
4
5
class Ico(QWidget):

def __init__(self):
super().__init__()
self.initUI()

很明显,我这里继承了QWidget这个基类,并自定义了一个名为Ico的新类。同时对这个Ico的新类进行了初始化。

self.initUI()

程序的GUI界面用initUI()函数创建。

self.setGeometry(600, 600, 600, 300)
self.setWindowTitle(‘QzmVc1’)
self.setWindowIcon(QIcon(path)) # path填图片路径名

上面的三种方法都已经从QWidget类中继承的。 setGeometry()做了两件事情:它在屏幕上定位窗口并设置它的大小;前两个参数是窗口的x和y位置;第三个是宽度;第四个是窗口的高度。实际上,它在一个方法中组合了resize()和move()方法。 最后一个方法设置应用程序图标。 为此,我们创建了一个QIcon对象。 QIcon接收到我们要显示的图标的路径。


3.3 关闭窗口

接下我们再这个窗口增加一个关闭按钮并实现关闭窗口的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QCoreApplication

class Ico(QWidget):

def __init__(self):
super().__init__()

self.initUI()

def initUI(self):

self.setGeometry(600, 600, 600, 300)
self.setWindowTitle('QzmVc1')
self.setWindowIcon(QIcon(path)) # path填图片路径名

qbtn = QPushButton('退出', self)
qbtn.clicked.connect(QCoreApplication.instance().quit)
qbtn.resize(70,30)
qbtn.move(50, 50)

self.show()

if __name__ == '__main__':

app = QApplication(sys.argv)
ex = Ico()
sys.exit(app.exec_())

我们来解析一下代码。

from PyQt5.QtWidgets import QPushButton
from PyQt5.QtCore import QCoreApplication

因为需要增加按钮,所以我们引入了QPushButton类。同时我们还需要一个来自QtCore模块的对象。

qbtn = QPushButton(‘Quit’, self)

我们创建一个按钮。该按钮是QPushButton类的一个实例。构造函数的第一个参数是按钮的标签。第二个参数是父窗口小部件。父窗口小部件是示例窗口小部件,它是通过QWidget继承的。

qbtn.clicked.connect(QCoreApplication.instance().quit)

PyQt5中的事件处理系统采用信号和槽机制构建。 如果我们点击按钮,点击的信号被发出。 槽可以是Qt槽函数或任何Python可调用的函数。 QCoreApplication包含主事件循环; 它处理和调度所有事件。 instance()方法给我们当前的实例。

请注意,QCoreApplication是通过QApplication创建的。 点击的信号连接到终止应用程序的quit()方法。

通信在两个对象之间完成:发送方和接收方。 发送方是按钮,接收者是应用对象。

简单的来说就是按钮发出被单击的信号,连接到退出程序的方法。

这里大家看不懂没有关系,信号和槽机制很重要,可以说是PyQt5图形界面编程中十分重要的概念,我们会后面单独花费一章来讲解。


3.4 第一个完整程序——猜数字游戏

上回我们说到了改变主窗口图标,设置一个小按钮、并且关闭这个窗口。

现在我们将更加完善一下相关的小部件(唯一的按钮用途有所改变),来完成一个简单的猜数字游戏。ok,开始吧!

本次课程涉及的知识点,主要是以下几点:

  • 按钮小提示
  • QLineEdit小部件使用
  • QMessageBox的使用
  • 关闭窗口事件触发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import sys
from PyQt5.QtWidgets import QWidget,QApplication,QPushButton,QMessageBox,QLineEdit
from PyQt5.QtGui import QIcon
from random import randint

class Example(QWidget):
def __init__(self):
super().__init__()
self.initUi()
self.num = randint(1,100)

def initUi(self):
self.setGeometry(600,600,800,600)
self.setWindowTitle('猜数字')
self.setWindowIcon(QIcon('C:\\Users\\QzmVc1\\Pictures\\Saved Pictures\\20181022195131.jpg'))

self.bt1 = QPushButton('我猜',self)
self.bt1.setGeometry(380,150,70,30)
self.bt1.setToolTip('点击这里猜数字')
self.bt1.clicked.connect(self.showMessage)

self.text = QLineEdit('在这里输入数字',self)
self.text.selectAll()
self.text.setFocus()
self.text.setGeometry(350,100,150,30)

self.show()

def showMessage(self):
guessnumber = int(self.text.text())
print(self.num)

if guessnumber > self.num:
QMessageBox.about(self,'看结果','猜大了')
self.text.setFocus()
elif guessnumber < self.num:
QMessageBox.about(self,'看结果','猜小了')
self.text.setFocus()
else:
QMessageBox.about(self,'看结果','答对了!进入下一轮!')
self.num = randint(1,100)
self.text.clear()
self.text.setFocus()

def closeEvent(self, event):
reply = QMessageBox.question(self,'确认','确认退出吗',QMessageBox.Yes | QMessageBox.No,QMessageBox.Yes)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()

if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

上面这个程序实现的主要功能是,系统随机产生一个1-100之间的整数,我们去猜,看看能否猜中这个数字。

import sys

from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QMessageBox, QLineEdit

from PyQt5.QtGui import QIcon

from random import randint

本次程序中我们使用到的模块,较上次教程多了QMessageBox, QLineEdit类和random模块。

1
2
3
4
5
6
7
def __init__(self):

super().__init__()

self.initUI()

self.num = randint(1,100)

类在进行初始化的时候,自动产生一个1-100的随机整数。

self.bt1.setToolTip(‘点击这里猜数字’)

要创建一个工具提示,我们调用setTooltip()方法。 我们可以使用富文本格式。鼠标停留在按钮上就会出现,如下图:

self.bt1.clicked.connect(self.showMessage)

当按钮被单击时我们调用showMessage())方法去响应执行。下一章节我们重点介绍PyQt5信号与槽的关系。

self.text = QLineEdit(‘在这里输入数字’, self)

self.text.selectAll()

self.text.setFocus()

self.text.setGeometry(350,100,150,30)

这里建立一个QLineEdit对象,用于让玩家输入数字。“在这里输入数字”是当窗口出现时出现的默认字符;selectAll()方法则是可以理解为将“在这里输入数字”进行全选,方便输入数字,否则你还得手动全选删除默认字符,如下图:

setFocus()就是让焦点置于文本栏中,方便用户输入,不然还得手动在文本栏中单击一下,很是麻烦。没有焦点的效果如下:

setGeometry()就是设置小部件的摆放坐标以及大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def showMessage(self):
guessnumber = int(self.text.text())
print(self.num)

if guessnumber > self.num:
QMessageBox.about(self,'看结果','猜大了')
self.text.setFocus()
elif guessnumber < self.num:
QMessageBox.about(self,'看结果','猜小了')
self.text.setFocus()
else:
QMessageBox.about(self,'看结果','答对了!进入下一轮!')
self.num = randint(1,100)
self.text.clear()
self.text.setFocus()

这里就是对猜测的数字和系统随机生成的数字就行判断了。因为QLineEdit输入的内容是str类型的,所有我们要进行类型转换。

QMessageBox.about就是弹出一个对话框,告诉你结果是什么样的。如下图:

事实上QMessageBox除了有about外,还有我们程序中用到的QMessageBox.question

QMessageBox.critical

QMessageBox.warning

QMessageBox.information

QMessageBox对话框包含类型只是图标不同其他无太大差别。

这里在说明一下,当我们回答正确的时候,调用clear()方法,将文本栏里面的内容清除,同时重新生成一个随机数,并将焦点置于文本栏中。

1
2
3
4
5
6
7
8
9
10
11
def closeEvent(self, event):

reply = QMessageBox.question(self, '确认', '确认退出吗', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

if reply == QMessageBox.Yes:

event.accept()

else:

event.ignore()

如果关闭QWidget,则生成QCloseEvent。 要修改widget的行为,我们需要重新实现closeEvent()事件处理程序。

我们显示一个带有两个按钮的消息框:Yes和No。第一个字符串出现在标题栏上。 第二个字符串是对话框显示的消息文本。 第三个参数指定出现在对话框中的按钮的组合。 最后一个参数是默认按钮。 它是初始键盘焦点的按钮。 返回值存储在答复变量中。

这里我们根据返回值进行判断。 如果我们单击Yes按钮,我们接受导致关闭窗口小部件并终止应用程序的事件。 否则我们忽略关闭事件。