PyQt工具开发PyQt程序

Posted by FanHao on 2019-09-28

Python图形界面

python支持多种图形界面的第三方库。包括:

  • TK 图形库,由Tcl语言开发的。python调用内置tkinter库。
  • wxWidgets 图形库,由C++语言开发。Python第三方库wxPython封装了此图形库。
  • Qt 非常强大的应用程序开发框架,可开发GUI或非GUI程序。python第三方库PyQt。
  • GTK 对应python的第三库为PyGTK。

Qt概念

Qt是一个由C++编写的跨平台的应用程序开发框架。可以用来开发GUI程序,也可以用来开发非GUI程序。目前Qt有两种版本,一种是商业版本(付费使用),另一种则是基于(L)GPL v3的开源版本。

Qt具体在某些方面的应用,大家可以访问Qt官网)进行查看。

PyQt概念

Qt和PyQt有什么区别?简而言之,PyQt是Qt的一个wrapper(封装)。PyQt为python开发GUI程序提供了解决方案,同样PyQt也是跨平台的,支持Linux、Windows、Mac OS。从PyQt4开始,所有平台上均支持GPL(General Public License)协议即开源协议。

PyQt官方网站请点击。PyQt4官方文档请点击

版本

目前有PyQt4和PyQt5两种版本,且两者不兼容。PyQt4在官网介绍说明中,未来将不会得到支持,所以PyQt5是未来的一个趋势。当然目前PyQt4使用最为广泛。

本文主要以PyQt4进行介绍。

安装

PyQt安装这一块,可能比较复杂。需要下载包手动安装。工具包下载地址

首先PyQt工具包版本需要与Python版本进行匹配。版本位数也必须要匹配,比如python解释器是32位,那么工具包也必须是32位的。这里推荐大家版本都采用64位。

组件模块

PyQt4为跨平台的工具包,它包括了300多个类和6000多个方法。由于过多的类和方法,整个工具库被划分成了多个模块。不同模块主要实现不同的功能,这一部分大家主要了解以下模块的主要作用。并学习掌握几个关键模块。

具体模块如下:

  • QtGui 模块包括图形化窗口组件和及相关类。包括如按钮、窗体、状态栏、滑块、位图、颜色、字体等等。
  • QtCore 模块包括了核心的非GUI功能,该模块用来对时间、文件、目录、各种数据类型、流、网址、媒体类型、线程或进程进行处理。
  • QtHelp 模块包含了用于创建和查看可查找的文档的类。
  • QtNetwork 模块包括网络编程的类。这些类可以用来编写TCP/IP和UDP的客户端和服务器。它们使得网络编程更容易和便捷。
  • QtOpenGL 模块使用OpenGL库来渲染3D和2D图形。该模块使得Qt GUI库和OpenGL库无缝集成。
  • QtScript 模块包含了使PyQt应用程序使用JavaScript解释器编写脚本的类。
  • QtSql 模块提供操作数据库的类。
  • QtSvg 模块提供了显示SVG文件内容的类。可缩放矢量图形(SVG)是一种用XML描述二维图形和图形应用的语言。
  • QtTest 模块包含了对PyQt应用程序进行单元测试的功能。(PyQt没有实现完全的Qt单元测试框架,相反,它假设使用标准的Python单元测试框架来实现模拟用户和GUI进行交互。)
  • QtWebKit 模块实现了基于开源浏览器引擎WebKit的浏览器引擎。
  • QtXml 包括处理XML文件的类,该模块提供了SAX和DOM API的接口。
  • QtAssistant 模块包含的类允许集成 Qt Assistant到PyQt应用程序中,提供在线帮助。
  • QtDesigner 模块包含的类允许使用PyQt扩展 Qt Designer。
  • uic 模块包含的类用来处理.ui文件,该文件由Qt Designer创建,用于描述整个或者部分用户界面。它包含的加载.ui文件和直接渲染以及从.ui文件生成Python代码为以后执行的类。
常用组件

以下描述组件均为GUI界面的组件,属于QtGui模块。重点关注几个常用组件的信号和方法。

1、按键QPushButton

1
2
# 初始化button类,得到一个按键对象
ok_button = QtGui.QPushButton('OK', widget)

信号:

  • clicked():按键被点击后触发的信号;
  • pressed():按键倍按下触发的信号;
  • released():按键被释放(抬起)时触发的信号;

构造方法:

1
2
# 添加一个按键,并设置图标和文本
__init__(self, QIcon, QString_text, QWidget parent = None)

2、行编辑框QLineEdit

1
2
# 初始化类,得到对象
useredit = QtGui.QLineEdit(widget)

信号:

  • cursorPositionChanged(int,int):光标位置改变触发;
  • editingFinished():编辑完成后触发的信号(编辑框失去光标);
  • textChanged(const QString&):文本内容改变时触发的信号;

构造方法:

1
2
3
4
# 生成一个行编辑框
__init__(self, QWidget parent = None)
# 生成一个带初始值的行编辑框
__init__(self, QString contents, QWidget parent = None)

方法:

  • clear(self):清除编辑框内容;
  • setText(self, QString):设置编辑框的内容;
  • text(self):取编辑框内的文本内容;
  • setMaxLength(self, int):设置编辑框能够输入的最大长度

3、文本编辑框QTextEdit

1
2
# QTextEdit类
logedit = QtGui.QTextEdit(widget)

信号:

  • cursorPositionChanged():光标位置改变触发;
  • textChanged(const QString&):文本内容改变时触发的信号;

构造方法:

1
2
3
4
# 生成一个文本编辑框
__init__(self, QWidget parent = None)
# 生成一个有初始文本的文本编辑框
__init__(self, QString_text, QWidget parent = None)

方法:

  • clear(self):清除编辑框内容;
  • setText(self, QString):设置编辑框的内容;
  • toPlainText(self):以文本的格式取编辑框内的文本内容;
  • toHtml(self):以HTML的格式取编辑框内的文本内容;

4、标签QLabel

1
userlabel = QtGui.QLabel('label', widget)

构造方法:

1
2
3
__init__(self, QWidget parent = None, Qt.WindowFlags flags = 0):
# 带值的标签
__init__(self, QString_text, QWidget parent = None, Qt.WindowFlags flags = 0)

方法:

  • clear(self):清除编辑框内容;
  • setText(self, QString):设置编辑框的内容;
  • text(self):取编辑框内的文本内容;

5、下拉框QComboBox

1
2
# 得到下拉框对象
select_box= QtGui.QComboBox(widget)

信号:

  • currentIndexChanged(int):选择项改变时触发,返回当前选项序号
  • currentIndexChanged(const QString&):选择项改变时触发,返回当前选项文本
  • activated(int):当用户选择下拉列表的项时触发,即使选择项未改变
  • activated(const QString&):当用户选择下拉列表的项时触发,即使选择项未改变

构造方法:

1
__init__(self, QWidget parent = None)

方法:

  • addItem(self, QString text, QVariant userData = QVariant()):给下拉列表添加下拉项;
  • addItems(self, QStringList texts):给下拉列表添加多个下拉项;
  • currentIndex(self):返回当前下拉列表的下拉项序号;
  • currentText(self):返回当前下拉列表的下拉项文本;
  • setItemText(self, int index, QString text):设置下拉项的文本

6、树结构QTreeWidget

1
2
# 树结构对象
casetree = QtGui.QTreeWidget(self.centralwidget)

信号:

  • itemClicked(QTreeWidgetItem*,int):选项被点击时触发
  • itemCollapsed(QTreeWidgetItem*):子选项被隐藏时触发
  • itemDoubleClicked(QTreeWidgetItem*,int):选项被双击时触发
  • itemExpanded(QTreeWidgetItem*):子选项被展示时触发

构造方法:

1
__init__(self, QWidget parent = None)

方法:

  • setColumnCount(self, int columns):设置树结构的列数;
  • setHeaderLabels(self, QStringList labels):设置列标签;
  • headerItem(self):返回用于树窗口小部件标题的项目;

7、树目录QTreeWidgetItem

1
root= QtGui.QTreeWidgetItem(self.casetree)

构造方法:

1
2
3
4
5
6
7
8
9
__init__(self, int type = QTreeWidgetItem.Type)
__init__(self, QStringList strings, int type = QTreeWidgetItem.Type)
__init__(self, QTreeWidget parent, int type = QTreeWidgetItem.Type)
__init__(self, QTreeWidget parent, QStringList strings, int type = QTreeWidgetItem.Type)
__init__(self, QTreeWidget parent, QTreeWidgetItem preceding, int type = QTreeWidgetItem.Type)
__init__(self, QTreeWidgetItem parent, int type = QTreeWidgetItem.Type)
__init__(self, QTreeWidgetItem parent, QStringList strings, int type = QTreeWidgetItem.Type)
__init__(self, QTreeWidgetItem parent, QTreeWidgetItem preceding, int type = QTreeWidgetItem.Type)
__init__(self, QTreeWidgetItem other)

方法:

  • addChild(self, QTreeWidgetItem child):添加子节点;
  • checkState(self, int column):检查点击状态;
  • setCheckState(self, int column, Qt.CheckState state):设置选项的选中状态,第二个参数为QT.Checked表示选中,Qt.UnChecked表示未选中;
  • setData(self, int column, int role, QVariant value):设置选项的角色值;
  • setText(self, int column, QString atext):设置选项的文本内容
界面布局

这一块大家了解一下即可。基本上GUI界面都是通过绘图得到的。

1、绝对定位

用像素(px)指定每个组件(控件)的大小和位置。

1
2
3
4
# 使用move方法确定组件的位置
move(x, y)
# 开始的两个参数是窗体的x和y的位置,a、b为组件的宽度、高度
setGeometry(x, y, a, b)

2、框布局

基本的布局类是QHBoxLayout 和 QVBoxLayout ,它们可以横向和纵向排列窗口组件。QHBoxLayout布局类为水平布局类,在该布局中的组件呈水平排列。QVBoxLayout 布局类为垂直布局类,在该布局中的组件呈垂直排列。框布局就是结合水平布局与垂直布局,使组件能够二维分布

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
import sys
from PyQt4 import QtGui

class MainWindow(QtGui.QWidget):
"""docstring for ClassName"""
def __init__(self, parent=None):
super(MainWindow, self).__init__()
self.initUI()

def initUI(self):
okButton = QtGui.QPushButton("OK")
cancelButton = QtGui.QPushButton("Cancel")
hbox = QtGui.QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(okButton)
hbox.addStretch(1)
hbox.addWidget(cancelButton)
vbox = QtGui.QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)
vbox.addStretch(1)
self.setLayout(vbox)
self.setWindowTitle('box layout')

if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

3、网格布局

最常用的布局类是网格布局,网格布局把空间划分成行和列,一般使用 QGridLayout 类来创建网格布局。布局类型设置好以后使用addWidget() 方法来把窗口组件加到网格中,参数是部件(widget),行(row)和列(column)数字。

  • addWidget(widget-obj):给布局添加组件,将我们需要用到的控件加入到布局中;
  • addLayout(Layout-obj):给布局添加另一个布局,使用框布局通常需要用该方法将水平布局和垂直布局结合;

代码实例:

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
import sys
from PyQt4 import QtGui

class MainWindow(QtGui.QWidget):
"""docstring for ClassName"""
def __init__(self, parent=None):
super(MainWindow, self).__init__()
self.initUI()

def initUI(self):
self.setWindowTitle('grid layout')
names = ['Cls', 'Bck', '', 'Close', '7', '8', '9', '/',
'4', '5', '6', '*', '1', '2', '3', '-',
'0', '.', '=', '+']
grid = QtGui.QGridLayout()
j = 0
pos = [(0, 0), (0, 1), (0, 2), (0, 3),
(1, 0), (1, 1), (1, 2), (1, 3),
(2, 0), (2, 1), (2, 2), (2, 3),
(3, 0), (3, 1), (3, 2), (3, 3 ),
(4, 0), (4, 1), (4, 2), (4, 3)]
for i in names:
button = QtGui.QPushButton(i)
if j == 2:
grid.addWidget(QtGui.QLabel(''), 0, 2)
else:
grid.addWidget(button, pos[j][0], pos[j][1])
j = j + 1
self.setLayout(grid)

if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
GUI页面

我们将利用QtGui模块实现生成GUI界面,学习常用的几个类。

实例一:学习了解几个方法

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
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui

def main():
# 创建app对象,每个PyQt程序都必须创建一个Application对象
# sys.argv获取参数列表
app = QtGui.QApplication(sys.argv)
# w为QWidget类的对象,QWidget窗口组件是PyQt4所有用户界面的基类
w =QtGui.QWidget()
# 初始化界面的大小,以下单位为像素(px)
w.resize(250, 150)
# 移动窗口的位置
w.move(300, 300)
# 设置界面标题
w.setWindowTitle('Simple GUI')
# 设置应用程序图标
w.setWindowIcon(QtGui.QIcon('icons/web.png'))
# show()方法将GUI界面显示在屏幕上
w.show()
# 退出循环时,调用exit()方法关闭
sys.exit(app.exec_())

if __name__ == '__main__':
main()

实例二:了解事件和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
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui

class MainWindow(QtGui.QMainWindow):
"""主GUI界面窗口"""
def __init__(self, parent=None):
# 继承父类的构造方法,也可称之为超类
super(MainWindow, self).__init__()
# setGeometry()方法定义了窗体在屏幕上的位置,并设置窗体的大小。
# 开始的两个参数是窗体的x和y的位置,后面两个为GUI界面的宽度、高度
self.setGeometry(200,200,600,500)
# 设置标题
self.setWindowTitle('test')
# 气泡提示
self.setToolTip('This is a <b>QWidget</b> widget.')
# 设置字体
# QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10))

def closeEvent(self, event):
"""
关闭GUI界面时,将会产生一个 QCloseEvent事件。
这里重写了closeEvent方法,实现关闭窗口时需要确认。改变了组件的行为。
"""
reply = QtGui.QMessageBox.question(self,'Message','Are you sure to quit?',QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.Yes:
event.accept()
else:
event.ignore()

if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

实例三

学习主GUI界面的状态栏、菜单栏、工具栏。工具栏一般不常用,常用的是另外两个。

注意:QMainWindow类才有状态栏、菜单栏、工具栏。QWidget与QDialog类型的窗口都不能这样使用。

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
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui, QtCore

class MainWindow(QtGui.QMainWindow):
"""主GUI界面窗口"""
def __init__(self, parent=None):
# 继承父类的构造方法
super(MainWindow, self).__init__()
self.resize(600,500)
self.setWindowTitle('Test')
# 调用QMainWindow的statusBar()方法得到状态栏
# 调用showMessage()方法显示状态信息
self.statusBar().showMessage('Ready')

# 定义一个exit的动作,设置图标和名字
exit = QtGui.QAction(QtGui.QIcon(''),'Exit',self)
# 设置动作的快捷键
exit.setShortcut('Ctrl+Q')
# 状态栏显示exit动作提示信息
exit.setStatusTip('Exit the Application')
# connect()方法连接信号和槽函数
self.connect(exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
# 调用menuBar()方法得到菜单栏
menu = self.menuBar()
# 调用addMenu()方法得到菜单项
tools = menu.addMenu('&Tools')
# 添加动作到菜单
tools.addAction(exit)

# 通过addToolBar()方法得到工具栏,工具栏一般不常用。
self.toolbar = self.addToolBar('GoBack')
#初始化一个动作
goback = QtGui.QAction(QtGui.QIcon(''),'goback',self)
#设置动作的快捷方式
goback.setShortcut('Ctrl+D')
#将动作添加到工具栏
self.toolbar.addAction(goback)


if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

事件、信号与槽

这部分是PyQt程序一个核心部分。开发PyQt程序必须要掌握理解以下概念

事件

事件(Events)是 GUI 程序中很重要的一部分。它由用户或系统产生。当我们调用程序的 exec_()方法时,程序就会进入主循环中。主循环捕获事件并将它们发送给相应的对象进行处理,事件往往伴随着一个信号的发生,或一个信号的处理。

这里事件(Events)包括鼠标点击、键盘按键输入等事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui, QtCore

class KeyExample(QtGui.QWidget):

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

self.setWindowTitle('Escape')
self.resize(500, 500)

def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
self.close()

if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
ex = KeyExample()
ex.show()
sys.exit(app.exec_())
信号与槽

当用户单击一个按钮,拖动一个滑块或进行其它动作时,相应的信号就会被触发。除此之外,信号还可以因为环境的变化而被发射。比如一个运行的时钟将会发射时间信号等。而所谓的槽则是一个方法(函数),该方法将会响应它所连接的信号。在 Python 中,槽可以是任何可以被调用的对象。槽一般为事件处理函数,专门用于处理某个信号。

在PyQt中,通过connect()方法连接信号与槽函数。此方法一般传入的参数为发送信号的对象、信号、对信号作出反应的方法(槽函数)。

1
connect(sender, QtCore.SIGNAL('clicked()'), revicer, QtCore.SLOT())

我们来看一个信号与槽的代码实例,目前我们用的最多就是button按钮的信号(clicked())。

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
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui, QtCore

class ButtonExample(QtGui.QMainWindow):

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

self.initUI()

def initUI(self):
button1 = QtGui.QPushButton("Button 1", self)
button1.move(30, 50)

button2 = QtGui.QPushButton("Button 2", self)
button2.move(150, 50)
# 连接信号与槽函数
self.connect(button1, QtCore.SIGNAL('clicked()'),
self.buttonClicked)

self.connect(button2, QtCore.SIGNAL('clicked()'),
self.buttonClicked)

self.statusBar().showMessage('Ready')
self.setWindowTitle('Event sender')
self.resize(290, 150)

def buttonClicked(self):
"""自定义槽函数,对信号作出反应"""
# sender()方法获取产生信号的对象
sender = self.sender()
self.statusBar().showMessage(sender.text() + ' was pressed')

if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
button = ButtonExample()
button.show()
sys.exit(app.exec_())
自定义信号

当然,我们也可以自定义信号。自定义信号不仅可以用于改变页面的显示状态,还可用于GUI线程与逻辑线程之间的数据传递。

1
2
3
4
5
6
7
8
# 创建一个不传递参数的信号
self.mysignal = QtCore.SIGNAL('bigthan10()')
# 使用emit()方法发送信号
self.emit(self.mysignal)
# 创建一个传递参数的信号,参数类型为int
self.mysignal = QtCore.pyqtSignal(int)
# 发送信号
self.mysignal.emit(20)

注意:使用自定义的槽函数处理自定义带传递参数的信号时,槽函数的参数与信号的参数必须保持一致(参数类型、参数个数、参数顺序)。

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
# -*- coding: utf-8 -*-
import sys
import time
from PyQt4 import QtGui, QtCore

class MainWindow(QtGui.QMainWindow):
# 定义一个信号,信号不能定义在构造函数中,否则会报错
mysignal = QtCore.pyqtSignal(str)
def __init__(self):

super(MainWindow,self).__init__()
self.setGeometry(100,100,500,500)
self.setWindowTitle('test')
#self.setWindowIcon(QtGui.QIcon('C:\Users\Administrator\cut.png'))
self.statusbar =self.statusBar() #创建一个状态栏
self.mysignal.connect(self.statusMessage) #连接信号与槽函数
date =time.ctime()
self.mysignal.emit(str(date)) #发射信号

def statusMessage(self,date):
# 自定义槽函数
self.statusbar.showMessage("now is %s!"%date)

if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

画图

此部分为开发PyQt程序的核心部分。

前面我们介绍了GUI界面的相关组件、以及如何通过代码实现得到一个GUI界面。对于简单的图形界面,我们可以通过编写源码实现。但是如果图形界面过于复杂时,此时通过编码方式生成图形界面,工作量是非常巨大。此时我们可以通过PyQt工具包的Designer工具设计图形界面,保存得到.ui文件,然后通过命令得到此GUI界面的python源码。

1
pyuic4 gui.ui > gui.py

现场演示如何使用此工具。

Designer工具

1、组件栏:显示组件,供用户选择

2、主界面栏:显示主界面,相当与一个画布,可以在上面设计我们需要的页面

3、对象编辑栏:包括几个部分

  • 对象查看器:查看当前页面上存在的对象以及层次结构,显示对象名与对象类型,我们可以在这里修改对象名;
  • 属性编辑器:用于编辑对象的属性信息,在页面或对象查看器上选择对象,然后在属性编辑器上就能查看或修改到该对象所拥有的属性;
  • 资源浏览器:查看可用资源;
  • 信号/槽编辑器:创建信号与槽的关系,可以指定到某个对象
  • 动作编辑器:编辑动作,可添加到菜单栏或工具栏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sys
from PyQt4 import QtGui,QtCore
from gui import Ui_MainWindow

class MainWindoW(QtGui.QMainWindow):
"""导入gui.py得到图形界面"""
def __init__(self):
super(MainWindoW, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)

if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
wm = MainWindoW()
wm.show()
sys.exit(app.exec_())

多线程

此部分为图形化编程核心部分。

在GUI编程中,由于主界面是一个无限循环,且一直监听事务的发生,所以如果在主UI线程中做一些耗时的操作,会导致线程阻塞卡死,所以PyQt编程需要将主UI线程与逻辑线程分离,将一些耗时的操作都放在子线程里执行,主线程与子线程之间的数据传递通过自定义信号实现,PyQt中定义的线程管理类为位于QtCore包的QThread类。

线程管理类QThread

首先我们先学习QThread类有哪些方法。

1、信号

  • finished() 当线程执行完成触发该信号
  • started() 当线程开始时触发该信号
  • terminated() 当线程被中断停止时触发该信号

2、方法

  • __init__(self, QObject parent = None) parent参数为父类线程的窗口类
  • quit(self) 终止线程执行
  • terminate(self) 终止线程,可能立即终止,也不能不会立即终止。
  • wait(self, int msecs = ULONG_MAX) 线程睡眠,等待
  • start(self) 拉起线程,并执行。
  • run(self) 线程主体函数,调用start()方法后,便会执行run()方法中的代码。一般逻辑处理代码均写在run()函数中
  • currentThread() 返回当前的线程对象
  • currentThreadId() 返回当前线程Id
线程之间数据传递

1、通过PyQt中信号与槽的机制传递数据。

2、通过自定义的公共配置文件传递数据(如config.ini)。

编写一个子线程

继承QThread类,重写run方法。

代码实例如下:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# -*- coding: utf-8 -*-
import sys, time
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import QPushButton, QLineEdit

class TimeThread(QtCore.QThread):
"""工作线程"""
# 自定义信号,指定参数类型为str
signal_time = QtCore.pyqtSignal(str)

def __init__(self, parent=None):
super(TimeThread, self).__init__(parent)

def start_timer(self):
self.start()

def stop_timer(self):
self.terminate()
self.wait()

def run(self):
while True:
print("Working", self.thread())
print(time.ctime(),type(time.ctime()))
# 发送信号,并携带参数值
self.signal_time.emit(time.ctime())
self.sleep(1)


class TimeWindow(QtGui.QMainWindow):
def __init__(self):
super(TimeWindow, self).__init__()

self.init_ui()
# 实例化子线程
self.timer_t = TimeThread()
# 信号连接槽函数
self.timer_t.signal_time.connect(self.update_timer_tv)

def init_ui(self):
"""生成GUI界面"""
self.resize(300, 300)
self.setWindowTitle('Time Clock')
self.statusBar().showMessage('Ready')
# 初始化行标签对象
self.timer_tv = QLineEdit(self)
self.timer_tv.setText("Wait")
self.timer_tv.setGeometry(15, 15, 270, 25)
# self.timer_tv.move(15, 15)

lable1 = QtGui.QLabel('Current Time',self)
lable1.move(150,100)

# 初始化设置Quit按键对象
stop_btn = QPushButton('Stop', self)
stop_btn.setToolTip('Click to Stop')
stop_btn.move(150, 150)
# 连接clicked信号和槽函数
self.connect(stop_btn, QtCore.SIGNAL("clicked()"), self.click_stop_button)

# 初始化设置start按键对象
start_btn = QPushButton('Start', self)
start_btn.setToolTip("Click to start")
start_btn.move(50, 150)
# 连接clicked信号和槽函数
self.connect(start_btn, QtCore.SIGNAL("clicked()"), self.click_start_button)

def click_start_button(self):
"""槽函数拉起执行子线程"""
self.timer_t.start_timer()
self.statusBar().showMessage('Go')

def click_stop_button(self):
"""槽函数终止子线程"""
self.timer_t.stop_timer()
self.statusBar().showMessage('Ready')

def update_timer_tv(self, text):
# 槽函数
self.timer_tv.setText(text)


if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = TimeWindow()
window.show()
sys.exit(app.exec_())

开发PyQt程序

开发过程
  1. 确认需求
  2. 画图得到gui.ui文件,通过命令转换为python源码
  3. 编写主界面源码,得到界面
  4. 定义config.ini文件用来保存GUI界面的参数信息
  5. 编写后端代码(线程类)
  6. 连接信号和槽函数
  7. 调试运行
注意事项
  1. 安装PyQt时,注意版本之间的对应关系(包括位版本,如32位、64位)。

  2. 编写线程代码时,注意线程卡死问题。

  3. GUI界面参数传递到后台,注意参数的类型和值。一般的,都需要对参数进行处理。

    (这里推荐python内置函数eval())