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)协议即开源协议。
版本
目前有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 | # 初始化button类,得到一个按键对象 |
信号:
- clicked():按键被点击后触发的信号;
- pressed():按键倍按下触发的信号;
- released():按键被释放(抬起)时触发的信号;
构造方法:
1 | # 添加一个按键,并设置图标和文本 |
2、行编辑框QLineEdit
1 | # 初始化类,得到对象 |
信号:
- cursorPositionChanged(int,int):光标位置改变触发;
- editingFinished():编辑完成后触发的信号(编辑框失去光标);
- textChanged(const QString&):文本内容改变时触发的信号;
构造方法:
1 | # 生成一个行编辑框 |
方法:
- clear(self):清除编辑框内容;
- setText(self, QString):设置编辑框的内容;
- text(self):取编辑框内的文本内容;
- setMaxLength(self, int):设置编辑框能够输入的最大长度
3、文本编辑框QTextEdit
1 | # QTextEdit类 |
信号:
- cursorPositionChanged():光标位置改变触发;
- textChanged(const QString&):文本内容改变时触发的信号;
构造方法:
1 | # 生成一个文本编辑框 |
方法:
- clear(self):清除编辑框内容;
- setText(self, QString):设置编辑框的内容;
- toPlainText(self):以文本的格式取编辑框内的文本内容;
- toHtml(self):以HTML的格式取编辑框内的文本内容;
4、标签QLabel
1 | userlabel = QtGui.QLabel('label', widget) |
构造方法:
1 | __init__(self, QWidget parent = None, Qt.WindowFlags flags = 0): |
方法:
- clear(self):清除编辑框内容;
- setText(self, QString):设置编辑框的内容;
- text(self):取编辑框内的文本内容;
5、下拉框QComboBox
1 | # 得到下拉框对象 |
信号:
- 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 | # 树结构对象 |
信号:
- 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 | __init__(self, int type = QTreeWidgetItem.Type) |
方法:
- 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 | # 使用move方法确定组件的位置 |
2、框布局
基本的布局类是QHBoxLayout 和 QVBoxLayout ,它们可以横向和纵向排列窗口组件。QHBoxLayout布局类为水平布局类,在该布局中的组件呈水平排列。QVBoxLayout 布局类为垂直布局类,在该布局中的组件呈垂直排列。框布局就是结合水平布局与垂直布局,使组件能够二维分布
1 | import sys |
3、网格布局
最常用的布局类是网格布局,网格布局把空间划分成行和列,一般使用 QGridLayout 类来创建网格布局。布局类型设置好以后使用addWidget() 方法来把窗口组件加到网格中,参数是部件(widget),行(row)和列(column)数字。
- addWidget(widget-obj):给布局添加组件,将我们需要用到的控件加入到布局中;
- addLayout(Layout-obj):给布局添加另一个布局,使用框布局通常需要用该方法将水平布局和垂直布局结合;
代码实例:
1 | import sys |
GUI页面
我们将利用QtGui模块实现生成GUI界面,学习常用的几个类。
实例一:学习了解几个方法
1 | #!/usr/bin/python |
实例二:了解事件和QMessageBox
1 | # -*- coding: utf-8 -*- |
实例三:
学习主GUI界面的状态栏、菜单栏、工具栏。工具栏一般不常用,常用的是另外两个。
注意:QMainWindow类才有状态栏、菜单栏、工具栏。QWidget与QDialog类型的窗口都不能这样使用。
1 | # -*- coding: utf-8 -*- |
事件、信号与槽
这部分是PyQt程序一个核心部分。开发PyQt程序必须要掌握理解以下概念
事件
事件(Events)是 GUI 程序中很重要的一部分。它由用户或系统产生。当我们调用程序的 exec_()方法时,程序就会进入主循环中。主循环捕获事件并将它们发送给相应的对象进行处理,事件往往伴随着一个信号的发生,或一个信号的处理。
这里事件(Events)包括鼠标点击、键盘按键输入等事件。
1 | #!/usr/bin/python |
信号与槽
当用户单击一个按钮,拖动一个滑块或进行其它动作时,相应的信号就会被触发。除此之外,信号还可以因为环境的变化而被发射。比如一个运行的时钟将会发射时间信号等。而所谓的槽则是一个方法(函数),该方法将会响应它所连接的信号。在 Python 中,槽可以是任何可以被调用的对象。槽一般为事件处理函数,专门用于处理某个信号。
在PyQt中,通过connect()方法连接信号与槽函数。此方法一般传入的参数为发送信号的对象、信号、对信号作出反应的方法(槽函数)。
1 | connect(sender, QtCore.SIGNAL('clicked()'), revicer, QtCore.SLOT()) |
我们来看一个信号与槽的代码实例,目前我们用的最多就是button按钮的信号(clicked())。
1 | # -*- coding: utf-8 -*- |
自定义信号
当然,我们也可以自定义信号。自定义信号不仅可以用于改变页面的显示状态,还可用于GUI线程与逻辑线程之间的数据传递。
1 | # 创建一个不传递参数的信号 |
注意:使用自定义的槽函数处理自定义带传递参数的信号时,槽函数的参数与信号的参数必须保持一致(参数类型、参数个数、参数顺序)。
1 | # -*- coding: utf-8 -*- |
画图
此部分为开发PyQt程序的核心部分。
前面我们介绍了GUI界面的相关组件、以及如何通过代码实现得到一个GUI界面。对于简单的图形界面,我们可以通过编写源码实现。但是如果图形界面过于复杂时,此时通过编码方式生成图形界面,工作量是非常巨大。此时我们可以通过PyQt工具包的Designer工具设计图形界面,保存得到.ui文件,然后通过命令得到此GUI界面的python源码。
1 | pyuic4 gui.ui > gui.py |
现场演示如何使用此工具。
Designer工具
1、组件栏:显示组件,供用户选择
2、主界面栏:显示主界面,相当与一个画布,可以在上面设计我们需要的页面
3、对象编辑栏:包括几个部分
- 对象查看器:查看当前页面上存在的对象以及层次结构,显示对象名与对象类型,我们可以在这里修改对象名;
- 属性编辑器:用于编辑对象的属性信息,在页面或对象查看器上选择对象,然后在属性编辑器上就能查看或修改到该对象所拥有的属性;
- 资源浏览器:查看可用资源;
- 信号/槽编辑器:创建信号与槽的关系,可以指定到某个对象
- 动作编辑器:编辑动作,可添加到菜单栏或工具栏
1 | import sys |
多线程
此部分为图形化编程核心部分。
在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 | # -*- coding: utf-8 -*- |
开发PyQt程序
开发过程
- 确认需求
- 画图得到gui.ui文件,通过命令转换为python源码
- 编写主界面源码,得到界面
- 定义config.ini文件用来保存GUI界面的参数信息
- 编写后端代码(线程类)
- 连接信号和槽函数
- 调试运行
注意事项
安装PyQt时,注意版本之间的对应关系(包括位版本,如32位、64位)。
编写线程代码时,注意线程卡死问题。
GUI界面参数传递到后台,注意参数的类型和值。一般的,都需要对参数进行处理。
(这里推荐python内置函数eval())