PyQt5 快速指南



PyQt5 - 简介

PyQt 是一个 GUI 控件工具包。它是Qt 的 Python 接口,Qt 是一个最强大和流行的跨平台 GUI 库之一。PyQt 由 RiverBank Computing Ltd. 开发。最新版本的 PyQt 可从其官方网站下载 - riverbankcomputing.com

PyQt API 是一组包含大量类和函数的模块。QtCore 模块包含用于处理文件和目录等的非 GUI 功能,而QtGui 模块包含所有图形控件。此外,还有用于处理 XML (QtXml)、SVG (QtSvg) 和 SQL (QtSql) 等的模块。

以下是常用模块的列表:

  • QtCore - 其他模块使用的核心非 GUI 类

  • QtGui - 图形用户界面组件

  • QtMultimedia - 用于低级别多媒体编程的类

  • QtNetwork - 用于网络编程的类

  • QtOpenGL - OpenGL 支持类

  • QtScript - 用于评估 Qt 脚本的类

  • QtSql - 使用 SQL 进行数据库集成的类

  • QtSvg - 用于显示 SVG 文件内容的类

  • QtWebKit - 用于渲染和编辑 HTML 的类

  • QtXml - 用于处理 XML 的类

  • QtWidgets - 用于创建经典桌面式 UI 的类

  • QtDesigner - 用于扩展 Qt Designer 的类

支持环境

PyQt 兼容所有流行的操作系统,包括 Windows、Linux 和 Mac OS。它具有双重许可,可在 GPL 和商业许可下使用。最新稳定版本为PyQt5-5.13.2。

Windows

提供与 Python 3.5 或更高版本兼容的 32 位或 64 位架构的轮子。推荐的安装方法是使用PIP实用程序:

pip3 install PyQt5

要安装诸如 Qt Designer 之类的开发工具以支持 PyQt5 轮子,以下是命令:

pip3 install pyqt5-tools

您也可以从源代码在 Linux/macOS 上构建 PyQt5 www.riverbankcomputing.com/static/Downloads/PyQt5

PyQt5 - 最新动态

PyQt5 API 不自动兼容早期版本。因此,涉及 PyQt4 模块的 Python 代码应通过进行相关更改手动升级。本章列出了 PyQt4 和 PyQt5 之间的主要区别。

PyQt5 不支持早于 v2.6 的 Python 版本。

PyQt5 不支持 QObject 类的 connect() 方法来连接信号和槽。因此,用法不再能实现:

QObject.connect(widget, QtCore.SIGNAL(‘signalname’), slot_function)

仅定义以下语法:

widget.signal.connect(slot_function)

早期 QtGui 模块中定义的类已分布在QtGui、QtPrintSupportQtWidgets模块中。

在新 QFileDialog 类中,getOpenFileNameAndFilter() 方法被getOpenFileName()替换,getOpenFileNamesAndFilter()getOpenFileNames()替换,getSaveFileNameAndFilter()getSaveFileName()替换。这些方法的旧签名也已更改。

PyQt5 没有规定定义从多个 Qt 类子类化的类。

pyuic5 实用程序(用于从 Designer 的 XML 文件生成 Python 代码)不支持 --pyqt3-wrapper 标志。

pyrcc5 不支持 -py2 和 -py3 标志。pyrcc5 的输出与所有 Python v2.6 及更高版本兼容。

PyQt5 始终自动调用sip.setdestroyonexit()并调用其拥有的所有包装实例的 C++ 析构函数。

PyQt5 - Hello World

使用 PyQt 创建简单的 GUI 应用程序涉及以下步骤:

  • 从 PyQt5 包导入 QtCore、QtGui 和 QtWidgets 模块。

  • 创建 QApplication 类的应用程序对象。

  • QWidget 对象创建一个顶级窗口。在其中添加 QLabel 对象。

  • 将标签的标题设置为“hello world”。

  • 使用 setGeometry() 方法定义窗口的大小和位置。

  • 通过app.exec_()方法进入应用程序的主循环。

以下是使用 PyQt 执行 Hello World 程序的代码:

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
def window():
   app = QApplication(sys.argv)
   w = QWidget()
   b = QLabel(w)
   b.setText("Hello World!")
   w.setGeometry(100,100,200,50)
   b.move(50,20)
   w.setWindowTitle("PyQt5")
   w.show()
   sys.exit(app.exec_())
if __name__ == '__main__':
   window()

上述代码产生以下输出:

Hello World

也可以开发上述代码的面向对象解决方案。

  • 从 PyQt5 包导入 QtCore、QtGui 和 QtWidgets 模块。

  • 创建 QApplication 类的应用程序对象。

  • 基于 QWidget 类声明窗口类

  • 添加 QLabel 对象并将标签的标题设置为“hello world”。

  • 使用 setGeometry() 方法定义窗口的大小和位置。

  • 通过app.exec_()方法进入应用程序的主循环。

以下是面向对象解决方案的完整代码:

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class window(QWidget):
   def __init__(self, parent = None):
      super(window, self).__init__(parent)
      self.resize(200,50)
      self.setWindowTitle("PyQt5")
      self.label = QLabel(self)
      self.label.setText("Hello World")
      font = QFont()
      font.setFamily("Arial")
      font.setPointSize(16)
      self.label.setFont(font)
      self.label.move(50,20)
def main():
   app = QApplication(sys.argv)
   ex = window()
   ex.show()
   sys.exit(app.exec_())
if __name__ == '__main__':
   main()
Hello Worlds

PyQt5 - 主要类

PyQt API 是一个大型的类和方法集合。这些类在 20 多个模块中定义。

以下是一些常用模块:

序号 模块和描述
1

QtCore

其他模块使用的核心非 GUI 类

2

QtGui

图形用户界面组件

3

QtMultimedia

用于低级别多媒体编程的类

4

QtNetwork

用于网络编程的类

5

QtOpenGL

OpenGL 支持类

6

QtScript

用于评估 Qt 脚本的类

7

QtSql

使用 SQL 进行数据库集成的类

8

QtSvg

用于显示 SVG 文件内容的类

9

QtWebKit

用于渲染和编辑 HTML 的类

10

QtXml

用于处理 XML 的类

11

QtWidgets

用于创建经典桌面式 UI 的类。

12

QtDesigner

用于扩展 Qt Designer 的类

13

QtAssistant

支持在线帮助

PyQt5 开发工具是用于 Qt 开发的实用程序集合。以下是一些此类实用程序的选择列表:

序号 工具名称和描述
1

assistant

Qt Assistant 文档工具

2

pyqt5designer

Qt Designer GUI 布局工具

3

linguist

Qt Linguist 翻译工具

4

lrelease

将 ts 文件编译成 qm 文件

5

pylupdate5

提取翻译字符串并生成或更新 ts 文件

6

qmake

Qt 软件构建工具

7

pyqt5qmlscene

QML 文件查看器

8

pyqmlviewer

QML 文件查看器

9

pyrcc5

Qt 资源文件编译器

10

pyuic5

用于从 ui 文件生成代码的 Qt 用户界面编译器

11

pyqmltestrunner

在 QML 代码上运行单元测试

12

qdbus

列出 D-Bus 服务的命令行工具

13

QDoc

软件项目的文档生成器。

14

Qhelpgenerator

生成和查看 Qt 帮助文件。

15

qmlimportscanner

解析并报告 QML 导入

PyQt API 包含 400 多个类。QObject 类位于类层次结构的顶部。它是所有 Qt 对象的基类。此外,QPaintDevice 类是所有可以绘制的对象的基类。

QApplication 类管理 GUI 应用程序的主要设置和控制流程。它包含主事件循环,在该循环中处理和分派由窗口元素和其他来源生成的事件。它还处理系统范围和应用程序范围的设置。

QWidget 类派生自 QObject 和 QPaintDevice 类,是所有用户界面对象的基类。QDialogQFrame 类也派生自 QWidget 类。它们有自己的子类系统。

以下是一些常用控件的选择列表

序号 控件和描述
1

QLabel

用于显示文本或图像

2

QLineEdit

允许用户输入一行文本

3

QTextEdit

允许用户输入多行文本

4

QPushButton

一个命令按钮来调用动作

5

QRadioButton

能够从多个选项中选择一个

6

QCheckBox

能够选择多个选项

7

QSpinBox

能够增加/减少整数值

8

QScrollBar

能够访问超出显示光圈的控件内容

9

QSlider

能够线性地更改绑定值。

10

QComboBox

提供一个下拉列表供选择

11

QMenuBar

包含 QMenu 对象的水平条

12

QStatusBar

通常位于 QMainWindow 的底部,提供状态信息。

13

QToolBar

通常位于 QMainWindow 的顶部或浮动。包含操作按钮

14

QListView

在 ListMode 或 IconMode 中提供一个可选择的项目列表

15

QPixmap

用于在 QLabel 或 QPushButton 对象上显示的屏幕外图像表示

16

QDialog

模态或非模态窗口,可以向父窗口返回信息

典型的基于 GUI 的应用程序的顶级窗口是由QMainWindow控件对象创建的。上面列出的一些控件占据其指定位置在这个主窗口中,而其他控件则使用各种布局管理器放置在中央控件区域中。

下图显示了 QMainWindow 框架:

QMainWindow

PyQt5 - 使用 Qt Designer

PyQt 安装程序附带一个名为Qt Designer的 GUI 构建器工具。使用其简单的拖放界面,可以快速构建 GUI 界面而无需编写代码。然而,它不是像 Visual Studio 这样的 IDE。因此,Qt Designer 没有调试和构建应用程序的功能。

启动 Qt Designer 应用程序,它是开发工具的一部分,安装在虚拟环境的 scripts 文件夹中。

Virtual Environment

通过选择“文件”→“新建”菜单开始设计 GUI 界面。

New Menu

然后,您可以将所需的控件从左侧窗格的控件框中拖放到表单上。您还可以为放在表单上的控件的属性赋值。

Widget

设计的表单保存为 demo.ui。此 ui 文件包含设计中控件及其属性的 XML 表示。此设计通过使用 pyuic5 命令行实用程序转换为 Python 等效项。此实用程序是 Qt 工具包 uic 模块的包装器。pyuic5 的用法如下:

pyuic5 -x demo.ui -o demo.py

在上述命令中,-x 开关向生成的 Python 脚本(来自 XML)添加少量附加代码,以便它成为一个自执行的独立应用程序。

if __name__ == "__main__":
   import sys
   app = QtGui.QApplication(sys.argv)
   Dialog = QtGui.QDialog()
   ui = Ui_Dialog()
   ui.setupUi(Dialog)
   Dialog.show()
   sys.exit(app.exec_())

执行生成的 Python 脚本将显示以下对话框:

python demo.py
Dialog Box

用户可以在输入字段中输入数据,但是点击“添加”按钮不会产生任何操作,因为它没有与任何函数关联。对用户生成的响应做出反应被称为**事件处理**。

PyQt5 - 信号与槽

与以顺序方式执行的控制台模式应用程序不同,基于 GUI 的应用程序是事件驱动的。函数或方法是响应用户操作(例如单击按钮、从集合中选择项目或鼠标单击等,称为**事件**)而执行的。

用于构建 GUI 界面的小部件充当此类事件的来源。每个从 QObject 类派生的 PyQt 小部件都设计为响应一个或多个事件发出**“信号”**。信号本身不执行任何操作。相反,它“连接”到一个**“槽”**。槽可以是任何**可调用的 Python 函数**。

使用 Qt Designer 的信号/槽编辑器

首先设计一个带有 LineEdit 控件和 PushButton 的简单窗体。

Slot Editor

如果按下按钮,希望文本框的内容被清除。QLineEdit 小部件为此目的提供了一个 clear() 方法。因此,按钮的**clicked**信号需要连接到文本框的**clear()**方法。

首先,从“编辑”菜单中选择“编辑信号/槽”(或按 F4)。然后用鼠标突出显示按钮并将光标拖动到文本框。

Cursor

释放鼠标后,将显示一个对话框,显示按钮的信号和小部件的方法。选择 clicked 信号和 clear() 方法。

Clear Method

右下角的信号/槽编辑器窗口将显示结果。

Editor Window

保存 ui 文件,并根据以下代码所示从 ui 文件构建和生成 Python 代码。

pyuic5 -x signalslot.ui -o signalslot.py

生成的 Python 代码将通过以下语句显示信号和槽之间的连接。

self.pushButton.clicked.connect(self.lineEdit.clear)

运行 signalslot.py,并在 LineEdit 中输入一些文本。如果按下按钮,文本将被清除。

构建信号-槽连接

无需使用 Designer,您可以通过以下语法直接建立信号-槽连接。

widget.signal.connect(slot_function)

假设单击按钮时要调用一个函数。这里,clicked 信号需要连接到一个可调用函数。可以使用以下任何一种技术来实现。

button.clicked.connect(slot_function)

示例

在下面的示例中,两个 QPushButton 对象(b1 和 b2)被添加到 QDialog 窗口中。我们希望在分别单击 b1 和 b2 时调用函数 b1_clicked() 和 b2_clicked()。

当单击 b1 时,clicked() 信号连接到 b1_clicked() 函数。

b1.clicked.connect(b1_clicked())

当单击 b2 时,clicked() 信号连接到 b2_clicked() 函数。

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

def window():
   app = QApplication(sys.argv)
   win = QDialog()
   b1 = QPushButton(win)
   b1.setText("Button1")
   b1.move(50,20)
   b1.clicked.connect(b1_clicked)
   
   b2 = QPushButton(win)
   b2.setText("Button2")
   b2.move(50,50)
   b2.clicked.connect(b2_clicked)
   
   win.setGeometry(100,100,200,100)

   win.setWindowTitle("PyQt5")
   win.show()
   sys.exit(app.exec_())

def b1_clicked():
   print ("Button 1 clicked")

def b2_clicked():
   print ("Button 2 clicked")

if __name__ == '__main__':
   window()

上述代码产生以下输出:

PushButton

输出

Button 1 clicked
Button 2 clicked

PyQt5 - 布局管理

可以通过指定以像素为单位测量的绝对坐标,将 GUI 小部件放置在容器窗口内。坐标相对于 setGeometry() 方法定义的窗口尺寸。

setGeometry() 语法

QWidget.setGeometry(xpos, ypos, width, height)

在下面的代码片段中,尺寸为 300x200 像素的顶级窗口显示在显示器上的 (10, 10) 位置。

import sys
from PyQt4 import QtGui

def window():
   app = QtGui.QApplication(sys.argv)
   w = QtGui.QWidget()
	
   b = QtGui.QPushButton(w)
   b.setText("Hello World!")
   b.move(50,20)
	
   w.setGeometry(10,10,300,200)
   w.setWindowTitle(“PyQt”)
   w.show()
   sys.exit(app.exec_())
	
if __name__ == '__main__':
   window()

一个**PushButton**小部件添加到窗口中,并放置在窗口左上角位置向右 50 像素,向下 20 像素的位置。

然而,这种绝对定位并不合适,原因如下:

  • 即使调整窗口大小,小部件的位置也不会改变。

  • 在具有不同分辨率的不同显示设备上,外观可能不一致。

  • 布局的修改很困难,因为它可能需要重新设计整个窗体。

Original and Resized Window

PyQt API 提供布局类,用于更优雅地管理容器内小部件的位置。布局管理器相对于绝对定位的优点是:

  • 窗口内的小部件会自动调整大小。

  • 确保在具有不同分辨率的显示设备上外观一致。

  • 可以动态添加或删除小部件,而无需重新设计。

Qt 工具包定义了各种可以与 Qt Designer 实用程序一起使用的布局。

Display Class

以下是我们将在这章中逐一讨论的类的列表。

序号 类和描述
1 QBoxLayout

QBoxLayout 类垂直或水平排列小部件。它的派生类是 QVBoxLayout(用于垂直排列小部件)和 QHBoxLayout(用于水平排列小部件)。

2 QGridLayout

GridLayout 类对象以网格形式呈现,单元格按行和列排列。该类包含 addWidget() 方法。可以通过指定单元格的行数和列数来添加任何小部件。

3 QFormLayout

QFormLayout 是一种创建两列窗体的便捷方法,其中每一行都包含一个与标签关联的输入字段。按照惯例,左列包含标签,右列包含输入字段。

PyQt5 - 基本控件

以下是我们将在这章中逐一讨论的小部件列表。

序号 控件和描述
1 QLabel

QLabel 对象充当占位符,用于显示不可编辑的文本或图像,或动画 GIF 电影。它也可以用作其他小部件的助记键。

2 QLineEdit

QLineEdit 对象是最常用的输入字段。它提供一个框,可以在其中输入一行文本。为了输入多行文本,需要 QTextEdit 对象。

3 QPushButton

在 PyQt API 中,QPushButton 类对象呈现一个按钮,单击该按钮可以编程调用某个函数。

4 QRadioButton

QRadioButton 类对象呈现一个带有文本标签的可选择按钮。用户可以选择窗体上显示的多个选项之一。此类派生自 QAbstractButton 类。

5 QCheckBox

当将 QCheckBox 对象添加到父窗口时,文本标签前会出现一个矩形框。与 QRadioButton 一样,它也是一个可选择按钮。

6 QComboBox

QComboBox 对象呈现一个下拉列表,供用户从中选择项目。它在窗体上占据最小的屏幕空间,只需显示当前选择的项目即可。

7 QSpinBox

QSpinBox 对象为用户提供一个文本框,该文本框显示一个整数,其右侧带有向上/向下按钮。

8 QSlider 小部件和信号

QSlider 类对象为用户提供一个凹槽,可以在其上移动手柄。它是控制有界值的经典小部件。

9 QMenuBar、QMenu 和 QAction

QMainWindow 对象标题栏正下方的水平 QMenuBar 用于显示 QMenu 对象。

10 QToolBar

QToolBar 小部件是一个可移动的面板,包含文本按钮、带图标的按钮或其他小部件。

11 QInputDialog

这是一个预配置的对话框,带有一个文本字段和两个按钮,“确定”和“取消”。用户单击“确定”按钮或按 Enter 键后,父窗口将收集文本框中的输入。

12 QFontDialog

另一个常用的对话框,字体选择器小部件是 QDialog 类的视觉外观。此对话框的结果是一个 Qfont 对象,父窗口可以使用该对象。

13 QFileDialog

此小部件是一个文件选择器对话框。它使用户能够浏览文件系统并选择要打开或保存的文件。可以通过静态函数或在对话框对象上调用 exec_() 函数来调用该对话框。

14 QTab

如果窗体有太多字段无法同时显示,则可以将它们排列在选项卡式小部件的每个选项卡下放置的不同页面中。QTabWidget 提供一个选项卡栏和一个页面区域。

15 QStacked

QStackedWidget 的功能类似于 QTabWidget。它还有助于有效利用窗口的客户区。

16 QSplitter

这是另一个高级布局管理器,它允许通过拖动它们之间的边界来动态更改子小部件的大小。Splitter 控件提供一个可以拖动以调整控件大小的手柄。

17 QDock

可停靠窗口是一个子窗口,可以保持浮动状态,也可以附加到主窗口的指定位置。QMainWindow 类的主窗口对象有一个为可停靠窗口保留的区域。

18 QStatusBar

QMainWindow 对象在底部保留一个水平条作为状态栏。它用于显示永久性或上下文状态信息。

19 QList

QListWidget 类是一个基于项目的界面,用于向列表中添加或删除项目。列表中的每个项目都是一个 QListWidgetItem 对象。ListWidget 可以设置为多选。

20 QScrollBar

滚动条控件使用户能够访问位于可视区域之外的文档部分。它提供当前位置的视觉指示器。

21 QCalendar

QCalendar 小部件是一个有用的日期选择器控件。它提供基于月份的视图。用户可以使用鼠标或键盘选择日期,默认日期为今天的日期。

PyQt5 - QDialog 类

**QDialog** 小部件呈现一个顶级窗口,主要用于收集用户的响应。它可以配置为**模态**(它会阻止其父窗口)或**非模态**(可以绕过对话框窗口)。

PyQt API 有许多预配置的对话框小部件,例如 InputDialog、FileDialog、FontDialog 等。

示例

在下面的示例中,对话框窗口的**WindowModality**属性决定它是模态还是非模态。可以将对话框上的任何一个按钮设置为默认按钮。当用户按下 Escape 键时,对话框将被**QDialog.reject()**方法丢弃。

顶级 QWidget 窗口上的 PushButton 单击后会产生一个对话框窗口。对话框框的标题栏上没有最小化和最大化控件。

用户无法将此对话框框置于后台,因为它的 WindowModality 设置为**ApplicationModal**。

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

def window():
   app = QApplication(sys.argv)
   w = QWidget()
   btn = QPushButton(w)
   btn.setText("Hello World!")
   btn.move(100,50)
   btn.clicked.connect(showdialog)
   w.setWindowTitle("PyQt Dialog demo")
   w.show()
   sys.exit(app.exec_())

def showdialog():
   dlg = QDialog()
   b1 = QPushButton("ok",dlg)
   b1.move(50,50)
   dlg.setWindowTitle("Dialog") 9. PyQt5 — QDialog Class
   dlg.setWindowModality(Qt.ApplicationModal)
   dlg.exec_()

if __name__ == '__main__':
   window()

上面的代码产生以下输出。单击主窗口中的按钮,对话框弹出。

QDialog Class Output

PyQt5 - QMessageBox

**QMessageBox** 是一个常用的模态对话框,用于显示一些信息消息,并可以选择要求用户通过单击其上的任何一个标准按钮来响应。每个标准按钮都有一个预定义的标题、一个角色并返回一个预定义的十六进制数字。

与 QMessageBox 类关联的重要方法和枚举在以下表格中给出:

序号 方法和描述
1

setIcon()

显示与消息严重性相对应的预定义图标

  • 提问
  • 信息
  • 警告
  • 严重错误
2

setText()

设置要显示的主要消息的文本

3

setInformativeText()

显示附加信息

4

setDetailText()

对话框显示“详细信息”按钮。单击它后,将显示此文本

5

setTitle()

显示对话框的自定义标题

6

setStandardButtons()

要显示的标准按钮列表。每个按钮都与

QMessageBox.Ok 0x00000400

QMessageBox.Open 0x00002000

QMessageBox.Save 0x00000800

QMessageBox.Cancel 0x00400000

QMessageBox.Close 0x00200000

QMessageBox.Yes 0x00004000

QMessageBox.No 0x00010000

QMessageBox.Abort 0x00040000

QMessageBox.Retry 0x00080000

QMessageBox.Ignore 0x00100000

7

setDefaultButton()

将按钮设置为默认按钮。如果按下 Enter 键,它将发出 clicked 信号

8

setEscapeButton()

将按钮设置为在按下 Escape 键时视为已单击

示例

在下面的示例中,顶级窗口上按钮的 clicked 信号,连接的函数显示 messagebox 对话框。

msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText("This is a message box")
msg.setInformativeText("This is additional information")
msg.setWindowTitle("MessageBox demo")
msg.setDetailedText("The details are as follows:")

setStandardButton() 函数显示所需的按钮。

msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)

buttonClicked() 信号连接到一个槽函数,该函数识别信号源的标题。

msg.buttonClicked.connect(msgbtn)

示例的完整代码如下:

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

def window():
   app = QApplication(sys.argv)
   w = QWidget()
   b = QPushButton(w)
   b.setText("Show message!")
   
   b.move(100,50)
   b.clicked.connect(showdialog)
   w.setWindowTitle("PyQt MessageBox demo")
   w.show()
   sys.exit(app.exec_())

def showdialog():
   msg = QMessageBox()
   msg.setIcon(QMessageBox.Information)
   
   msg.setText("This is a message box")
   msg.setInformativeText("This is additional information")
   msg.setWindowTitle("MessageBox demo")
   msg.setDetailedText("The details are as follows:")
   msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
   msg.buttonClicked.connect(msgbtn)

   retval = msg.exec_()

def msgbtn(i):
   print ("Button pressed is:",i.text())

if __name__ == '__main__':
   window()

以上代码产生以下输出。单击主窗口的按钮时,将弹出消息框:

QMessageBox Output

如果单击消息框上的“确定”或“取消”按钮,则会在控制台中产生以下输出:

Button pressed is: OK
Button pressed is: Cancel

PyQt5 - 多文档界面

典型的GUI应用程序可能有多个窗口。选项卡式和小部件允许一次激活一个窗口。但是,很多时候这种方法可能不太有用,因为其他窗口的视图被隐藏了。

同时显示多个窗口的一种方法是将它们创建为独立窗口。这称为SDI **(单文档界面)**。这需要更多的内存资源,因为每个窗口可能都有自己的菜单系统、工具栏等。

MDI **(多文档界面)**应用程序消耗的内存资源较少。子窗口在主容器内相互关联地排列。容器小部件称为 **QMdiArea**。

QMdiArea 小部件通常占据 QMainWindow 对象的中心小部件。此区域中的子窗口是 **QMdiSubWindow** 类的实例。可以将任何 QWidget 设置为 subWindow 对象的内部小部件。MDI 区域中的子窗口可以以级联或平铺方式排列。

下表列出了 QMdiArea 类和 QMdiSubWindow 类的重要方法:

序号 方法和描述
1

addSubWindow()

在 MDI 区域中添加小部件作为新的子窗口

2

removeSubWindow()

删除作为子窗口内部小部件的小部件

3

setActiveSubWindow()

激活子窗口

4

cascadeSubWindows()

以级联方式排列 MDiArea 中的子窗口

5

tileSubWindows()

以平铺方式排列 MDiArea 中的子窗口

6

closeActiveSubWindow()

关闭活动子窗口

7

subWindowList()

返回 MDI 区域中的子窗口列表

8

setWidget()

将 QWidget 设置为 QMdiSubwindow 实例的内部小部件

QMdiArea 对象发出 subWindowActivated() 信号,而 windowStateChanged() 信号由 QMdisubWindow 对象发出。

示例

在以下示例中,包含 QMainWindow 的顶级窗口具有菜单和 MdiArea。

self.mdi = QMdiArea()
self.setCentralWidget(self.mdi)
bar = self.menuBar()
file = bar.addMenu("File")

file.addAction("New")
file.addAction("cascade")
file.addAction("Tiled")

菜单的 Triggered() 信号连接到 windowaction() 函数。

file.triggered[QAction].connect(self.windowaction)

菜单的新操作在 MDI 区域中添加一个子窗口,其标题带有增量编号。

MainWindow.count = MainWindow.count+1
sub = QMdiSubWindow()
sub.setWidget(QTextEdit())
sub.setWindowTitle("subwindow"+str(MainWindow.count))
self.mdi.addSubWindow(sub)
sub.show()

菜单的级联和平铺按钮分别以级联和平铺方式排列当前显示的子窗口。

完整代码如下:

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class MainWindow(QMainWindow):
   count = 0

   def __init__(self, parent = None):
      super(MainWindow, self).__init__(parent)
      self.mdi = QMdiArea()
      self.setCentralWidget(self.mdi)
      bar = self.menuBar()

      file = bar.addMenu("File")
      file.addAction("New")
      file.addAction("cascade")
      file.addAction("Tiled")
      file.triggered[QAction].connect(self.windowaction)
      self.setWindowTitle("MDI demo")

   def windowaction(self, q):
      print ("triggered")
   
      if q.text() == "New":
         MainWindow.count = MainWindow.count+1
         sub = QMdiSubWindow()
         sub.setWidget(QTextEdit())
         sub.setWindowTitle("subwindow"+str(MainWindow.count))
         self.mdi.addSubWindow(sub)
         sub.show()

      if q.text() == "cascade":
         self.mdi.cascadeSubWindows()

      if q.text() == "Tiled":
         self.mdi.tileSubWindows()

def main():
   app = QApplication(sys.argv)
   ex = MainWindow()
   ex.show()
   sys.exit(app.exec_())

if __name__ == '__main__':
   main()

运行以上代码,以级联和平铺形式显示三个窗口:

Multiple Document Interface Output1

Multiple Document Interface Output2

Multiple Document Interface Output3

PyQt5 - 拖放

**拖放** 功能对于用户来说非常直观。它存在于许多桌面应用程序中,用户可以在其中将对象从一个窗口复制或移动到另一个窗口。

基于 MIME 的拖放数据传输基于 **QDrag** 类。**QMimeData** 对象将其数据与其对应的 MIME 类型关联。它存储在剪贴板中,然后用于拖放过程中。

以下 QMimeData 类函数允许方便地检测和使用 MIME 类型。

测试器 获取器 设置器 MIME 类型
hasText() text() setText() text/plain
hasHtml() html() setHtml() text/html
hasUrls() urls() setUrls() text/uri-list
hasImage() imageData() setImageData() image/*
hasColor() colorData() setColorData() application/x-color

许多 QWidget 对象支持拖放操作。允许拖动其数据的对象已设置 setDragEnabled(),必须将其设置为 true。另一方面,小部件应该响应拖放事件以便存储拖入它们的数据。

  • **DragEnterEvent** 提供一个事件,该事件在拖动操作进入目标小部件时发送到目标小部件。

  • **DragMoveEvent** 用于拖放操作正在进行时。

  • **DragLeaveEvent** 在拖放操作离开小部件时生成。

  • 另一方面,**DropEvent** 在放下操作完成后发生。可以根据条件接受或拒绝事件的建议操作。

示例

在以下代码中,**DragEnterEvent** 验证事件的 MIME 数据是否包含文本。如果是,则接受事件的建议操作,并将文本作为新项目添加到 ComboBox 中。

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class combo(QComboBox):
   def __init__(self, title, parent):
      super(combo, self).__init__( parent)
      self.setAcceptDrops(True)

   def dragEnterEvent(self, e):
      print (e)

      if e.mimeData().hasText():
         e.accept()
      else:
         e.ignore()

   def dropEvent(self, e):
      self.addItem(e.mimeData().text())

class Example(QWidget):
   def __init__(self):
      super(Example, self).__init__()

      self.initUI()

   def initUI(self):
      lo = QFormLayout()
      lo.addRow(QLabel("Type some text in textbox and drag it into combo box"))
   
      edit = QLineEdit()
      edit.setDragEnabled(True)
      com = combo("Button", self)
      lo.addRow(edit,com)
      self.setLayout(lo)
      self.setWindowTitle('Simple drag and drop')
def main():
   app = QApplication(sys.argv)
   ex = Example()
   ex.show()
   app.exec_()

if __name__ == '__main__':
   main()

上述代码产生以下输出:

Drag and Drop Output

PyQt5 - 数据库处理

PyQt5 库包含 **QtSql** 模块。它是一个详尽的类系统,用于与许多基于 SQL 的数据库通信。它的 **QSqlDatabase** 通过连接对象提供访问权限。以下是当前可用的 SQL 驱动程序列表:

序号 驱动程序类型和说明
1

QDB2

IBM DB2

2

QIBASE

Borland InterBase 驱动程序

3

QMYSQL

MySQL 驱动程序

4

QOCI

Oracle 调用接口驱动程序

5

QODBC

ODBC 驱动程序(包括 Microsoft SQL Server)

6

QPSQL

PostgreSQL 驱动程序

7

QSQLITE

SQLite 3 版或更高版本

8

QSQLITE2

SQLite 2 版

示例

在本节中,使用静态方法建立与 SQLite 数据库的连接:

db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('sports.db')

QSqlDatabase 类的其他方法如下:

序号 方法和描述
1

setDatabaseName()

设置要与其建立连接的数据库的名称

2

setHostName()

设置安装数据库的主机的名称

3

setUserName()

指定连接的用户名

4

setPassword()

设置连接对象的密码(如有)

5

commit()

提交事务,如果成功则返回 true

6

rollback()

回滚数据库事务

7

close()

关闭连接

**QSqlQuery** 类具有执行和操作 SQL 命令的功能。可以执行 DDL 和 DML 类型的 SQL 查询。第一步是使用以下语句创建 SQlite 数据库:

db = QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('sportsdatabase.db')

接下来,使用 **QSqlQuery()** 方法获取 Query 对象,并调用其最重要的方法 exec_(),该方法将包含要执行的 SQL 语句的字符串作为参数。

query = QtSql.QSqlQuery()
query.exec_("create table sportsmen(id int primary key, " "firstname varchar(20), lastname varchar(20))")

以下脚本创建了一个 SQLite 数据库 sports.db,其中包含一个填充了五个记录的 sportsperson 表。

import sys
from PyQt5.QtSql import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

def createDB():
   db = QSqlDatabase.addDatabase('QSQLITE')
   db.setDatabaseName('sportsdatabase.db')

   if not db.open():
      msg = QMessageBox()
      msg.setIcon(QMessageBox.Critical)
      msg.setText("Error in Database Creation")
      retval = msg.exec_()
      return False
   query = QSqlQuery()

   query.exec_("create table sportsmen(
      id int primary key, ""firstname varchar(20), lastname varchar(20))")

   query.exec_("insert into sportsmen values(101, 'Roger', 'Federer')")
   query.exec_("insert into sportsmen values(102, 'Christiano', 'Ronaldo')")
   query.exec_("insert into sportsmen values(103, 'Ussain', 'Bolt')")
   query.exec_("insert into sportsmen values(104, 'Sachin', 'Tendulkar')")
   query.exec_("insert into sportsmen values(105, 'Saina', 'Nehwal')")
   return True

if __name__ == '__main__':
   app = QApplication(sys.argv)
   createDB()

要确认 SQLite 数据库已创建,并在其中的 sportsmen 表中添加了上述记录,请使用名为 **SQLiteStudio** 的 SQLite GUI 实用程序。

Database Handling

PyQt 中的 **QSqlTableModel** 类是一个高级接口,它提供可编辑的数据模型,用于读取和写入单个表中的记录。此模型用于填充 **QTableView** 对象。它为用户提供了一个可滚动和可编辑的视图,可以将其放在任何顶级窗口上。

QSqlTableModel 对象的声明方式如下:

model = QtSql.QSqlTableModel()

其编辑策略可以设置为以下任何一种:

QSqlTableModel.OnFieldChange 所有更改将立即应用
QSqlTableModel.OnRowChange 当用户选择不同的行时,将应用更改
QSqlTableModel.OnManualSubmit 所有更改都将被缓存,直到调用 submitAll() 或 revertAll() 为止

示例

在以下示例中,sportsperson 表用作模型,策略设置为:

model.setTable('sportsmen') 
model.setEditStrategy(QtSql.QSqlTableModel.OnFieldChange)
   model.select()

QTableView 类是 PyQt 中 Model/View 框架的一部分。QTableView 对象的创建方式如下:

view = QtGui.QTableView()
view.setModel(model)
view.setWindowTitle(title)
return view

此 QTableView 对象和两个 QPushButton 小部件被添加到顶级 QDialog 窗口。add 按钮的 clicked() 信号连接到 addrow(),该函数对模型表执行 insertRow()。

button.clicked.connect(addrow)
def addrow():
   print model.rowCount()
   ret = model.insertRows(model.rowCount(), 1)
   print ret

与 delete 按钮关联的槽执行一个 lambda 函数,该函数删除用户选择的行。

btn1.clicked.connect(lambda: model.removeRow(view1.currentIndex().row()))

完整代码如下:

import sys
from PyQt5.QtSql import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

def initializeModel(model):
   model.setTable('sportsmen')
   model.setEditStrategy(QSqlTableModel.OnFieldChange)
   model.select()
   model.setHeaderData(0, Qt.Horizontal, "ID")
   model.setHeaderData(1, Qt.Horizontal, "First name")
   model.setHeaderData(2, Qt.Horizontal, "Last name")

def createView(title, model):
   view = QTableView()
   view.setModel(model)
   view.setWindowTitle(title)
   return view

def addrow():
   print (model.rowCount())
   ret = model.insertRows(model.rowCount(), 1)
   print (ret)

def findrow(i):
   delrow = i.row()

if __name__ == '__main__':
   app = QApplication(sys.argv)
   db = QSqlDatabase.addDatabase('QSQLITE')
   db.setDatabaseName('sportsdatabase.db')
   model = QSqlTableModel()
   delrow = -1
   initializeModel(model)

   view1 = createView("Table Model (View 1)", model)
   view1.clicked.connect(findrow)

   dlg = QDialog()
   layout = QVBoxLayout()
   layout.addWidget(view1)

   button = QPushButton("Add a row")
   button.clicked.connect(addrow)
   layout.addWidget(button)

   btn1 = QPushButton("del a row")
   btn1.clicked.connect(lambda: model.removeRow(view1.currentIndex().row()))
   layout.addWidget(btn1)

   dlg.setLayout(layout)
   dlg.setWindowTitle("Database Demo")
   dlg.show()
   sys.exit(app.exec_())

上述代码产生以下输出:

Database Handling Output

尝试添加和删除一些记录,然后返回到 SQLiteStudio 以确认事务。

PyQt5 - 绘图 API

PyQt 中的所有 **QWidget** 类都是从 QPaintDevice 类派生的。**QPaintDevice** 是二维空间的抽象,可以使用 QPainter 在其上绘制。绘图设备的尺寸以像素为单位测量,从左上角开始。

**QPainter** 类在小部件和其他可绘制设备(如打印机)上执行低级绘图。通常,它用于小部件的绘图事件中。每当小部件的外观更新时,就会发生 **QPaintEvent**。

通过调用 **begin()** 方法激活绘图器,而 **end()** 方法则停用它。在这两者之间,通过以下表格中列出的适当方法绘制所需的图案。

序号 方法和描述
1

begin()

开始在目标设备上绘图

2

drawArc()

在起始角度和结束角度之间绘制一条弧线

3

drawEllipse()

在矩形内绘制一个椭圆

4

drawLine()

绘制一条线,指定端点坐标

5

drawPixmap()

从图像文件中提取 pixmap,并将其显示在指定位置

6

drwaPolygon()

使用坐标数组绘制多边形

7

drawRect()

绘制一个矩形,从左上角坐标开始,具有给定的宽度和高度

8

drawText()

在给定坐标处显示文本

9

fillRect()

使用 QColor 参数填充矩形

10

setBrush()

设置用于绘图的画笔样式

11

setPen()

设置用于绘图的笔的颜色、大小和样式

示例

在以下代码中,使用了 PyQt 的各种绘图方法。

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class Example(QWidget):
   def __init__(self):
      super(Example, self).__init__()
      self.initUI()

   def initUI(self):
      self.text = "hello world"
      self.setGeometry(100,100, 400,300)
      self.setWindowTitle('Draw Demo')
      self.show()

   def paintEvent(self, event):
      qp = QPainter()
      qp.begin(self)
      qp.setPen(QColor(Qt.red))
      qp.setFont(QFont('Arial', 20))
      qp.drawText(10,50, "hello Python")
      qp.setPen(QColor(Qt.blue))
      qp.drawLine(10,100,100,100)
      qp.drawRect(10,150,150,100)
      qp.setPen(QColor(Qt.yellow))
      qp.drawEllipse(100,50,100,50)
      qp.drawPixmap(220,10,QPixmap("pythonlogo.png"))
      qp.fillRect(20,175,130,70,QBrush(Qt.SolidPattern))
      qp.end()

def main():
   app = QApplication(sys.argv)
   ex = Example()
   sys.exit(app.exec_())

if __name__ == '__main__':
   main()

上述代码产生以下输出:

Database Handling Outputs

PyQt5 - BrushStyle 常量

在本节中,我们将学习画笔样式常量。

画笔样式常量

以下是画笔样式常量:

Qt.NoBrush 无画笔图案
Qt.SolidPattern 均匀颜色
Qt.Dense1Pattern 极其密集的画笔图案
Qt.HorPattern 水平线
Qt.VerPattern 垂直线
Qt.CrossPattern 交叉水平线和垂直线
Qt.BDiagPattern 向后对角线
Qt.FDiagPattern 向前对角线
Qt.DiagCrossPattern 交叉对角线

预定义的 QColor 样式

以下是预定义的 QColor 样式:

Qt.NoBrush 无画笔图案
Qt.SolidPattern 均匀颜色
Qt.Dense1Pattern 极其密集的画笔图案
Qt.HorPattern 水平线
Qt.VerPattern 垂直线
Qt.CrossPattern 交叉水平线和垂直线
Qt.BDiagPattern 向后对角线
Qt.FDiagPattern 向前对角线
Qt.DiagCrossPattern 交叉对角线

预定义的 QColor 对象

以下是预定义的 QColor 对象:

Qt.white
Qt.black
Qt.red
Qt.darkRed
Qt.green
Qt.darkGreen
Qt.blue
Qt.cyan
Qt.magenta
Qt.yellow
Qt.darkYellow
Qt.gray

PyQt5 - QClipboard

**QClipboard** 类提供对系统范围的剪贴板的访问,该剪贴板提供了一种简单的机制,可以在应用程序之间复制和粘贴数据。它的作用类似于 **QDrag** 类,并使用类似的数据类型。

QApplication 类具有一个静态方法 **clipboard()**,它返回对剪贴板对象的引用。任何类型的 MimeData 都可以复制到剪贴板或从剪贴板粘贴。

以下是常用剪贴板类方法:

序号 方法和描述
1

clear()

清除剪贴板内容

2

setImage()

将 QImage 复制到剪贴板

3

setMimeData()

将 MIME 数据设置到剪贴板

4

setPixmap()

将 Pixmap 对象复制到剪贴板

5

setText()

将 QString 复制到剪贴板

6

text()

从剪贴板检索文本

与剪贴板对象关联的信号是:

序号 方法和说明
1

dataChanged()

每当剪贴板数据更改时

示例

在以下示例中,两个 TextEdit 对象和两个 Pushbuttons 被添加到顶级窗口。

首先,实例化剪贴板对象。textedit 对象的 Copy() 方法将数据复制到系统剪贴板。单击“粘贴”按钮时,它会获取剪贴板数据并将其粘贴到另一个 textedit 对象中。

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class Example(QWidget):
   def __init__(self):
      super(Example, self).__init__()

      self.initUI()

   def initUI(self):
      hbox = QVBoxLayout()
      self.edit1=QTextEdit()
      hbox.addWidget(self.edit1)
      self.btn1=QPushButton("Copy")
      hbox.addWidget(self.btn1)
      self.edit2=QTextEdit()
      self.btn2=QPushButton("Paste")
      hbox.addWidget(self.edit2)
      hbox.addWidget(self.btn2)
      self.btn1.clicked.connect(self.copytext)
      self.btn2.clicked.connect(self.pastetext)
      self.setLayout(hbox)
      
      self.setGeometry(300, 300, 300, 200)
      self.setWindowTitle('Clipboard')
      self.show()
      
   def copytext(self):

      #clipboard.setText(self.edit1.copy())
      self.edit1.copy()
      print (clipboard.text())

      msg=QMessageBox()
      msg.setText(clipboard.text()+" copied on clipboard")
      msg.exec_()

   def pastetext(self):
      self.edit2.setText(clipboard.text())

app = QApplication(sys.argv)
clipboard=app.clipboard()
ex = Example()
ex.setWindowTitle("clipboard Example")
sys.exit(app.exec_())

上述代码产生以下输出:

QClipboard

PyQt5 - QPixmap 类

QPixmap 类提供图像的屏幕外表示。它可以用作 QPaintDevice 对象,也可以加载到另一个小部件中,通常是标签或按钮。

Qt API 还有另一个类似的类QImage,它针对 I/O 和其他像素操作进行了优化。另一方面,Pixmap 针对屏幕显示进行了优化。这两种格式可以相互转换。

可以读取到 QPixmap 对象中的图像文件类型如下:

BMP Windows 位图
GIF 图形交换格式(可选)
JPG 联合图像专家组
JPEG 联合图像专家组
PNG 便携式网络图形
PBM 便携式位图
PGM 便携式灰度图
PPM 便携式像素图
XBM X11 位图
XPM X11 像素图

以下方法可用于处理 QPixmap 对象:

序号 方法和描述
1

copy()

从 QRect 对象复制像素图数据

2

fromImage()

将 QImage 对象转换为 QPixmap

3

grabWidget()

从给定的小部件创建像素图

4

grabWindow()

创建窗口中数据的像素图

5

load()

加载图像文件作为像素图

6

save()

将 QPixmap 对象保存为文件

7

toImage()

将 QPixmap 转换为 QImage

QPixmap 最常见的用途是在标签/按钮上显示图像。

示例

以下示例演示了使用setPixmap()方法在 QLabel 上显示图像。

完整代码如下:

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

def window():
   app = QApplication(sys.argv)
   win = QWidget()
   l1 = QLabel()
   l1.setPixmap(QPixmap("python.png"))

   vbox = QVBoxLayout()
   vbox.addWidget(l1)
   win.setLayout(vbox)
   win.setWindowTitle("QPixmap Demo")
   win.show()
   sys.exit(app.exec_())

if __name__ == '__main__':
   window()

上述代码产生以下输出:

QPixmap Class
广告
© . All rights reserved.