Notes

  • Qt Charts是二维图表模块,用于绘制柱状图、饼图、曲线图等常用二维图表。

  • Qt Data Visualization是三维数据图表模块,用于数据的三维显示,如散点的三维空间分布、三维曲面等。

  • pyuic5 是用于将Qt Designer(或Qt Creator内置的UI Designer)可视化设计的界面文件(.ui文件)编译转换为Python程序文件的工具软件。

  • pyrcc5 是用于将Qt Creator里设计的资源文件(.qrc文件)编译转换为Python程序文件的工具软件,资源文件一般存储了图标、图片等UI设计资源。

  • widget 是 label 的父容器。在创建时,将父容器作为参数传入。指定父容器,这样标签才能显示在窗体上。

widget = QtWidgets.QWidget()

label = QtWidgets.Qlabel(widget)
  • 基于QMainWindow类的窗体,具有主窗口的特性,窗口上有主菜单栏、工具栏、状态栏等。

  • QWidget类是所有界面组件的基类,如QLabel、QPushButton等界面组件都是从QWidget类继承而来。

  • Qt Designer中的Property Editor界面,还显示了组件的继承关系。如QLabel的继承关系为:QObject→QWidget→QFrame→QLabel;QPushButton为QObject->QWidget->QAbstractButton。

  • objectName是组件的对象名称,界面上的每个组件都需要一个唯一的对象名称,以便被引用。

  • retranslateUi()函数集中设置了窗体上所有的字符串,利于实现软件的多语言界面。

  • 函数setupUi()用于窗体的初始化,它创建了窗体上的所有组件并设置其属性。窗体是外部传入的,作为所有界面组件的父容器。

# setupUi()函数只创建窗体上的其他组件,而作为容器的窗体是靠外部传入的。
self.ui = Ui_MainWindow()   # 创建UI对象
self.ui.setupUi(self)       # 构造UI界面

# or
baseWidget = QtWidgets.Qwidget()  # 创建窗体的基类QWidget的实例
ui = Ui_MainWindow()  
ui.setupUi(baseWidget)  # 以baseWidget作为传递参数,创建完整窗体
  • Ui_MainWindow的父类是object,不是Qt的窗体界面。

  • 过程化的程序,难以实现业务逻辑功能的有效封装。

  • 界面与业务逻辑分离的设计方法可以有多继承方法,另一种是单继承方法。

  • 在多继承时,使用super()得到的是第一个基类。

class MainWindow(QMainWindow, Ui_MainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setupUi(self)

如上程序中,super()执行之后得到的就是一个QMainWindow对象,可以作为参数传递给setupUi()函数。通过这样的多继承,Ui_MainWindow中定义的窗体上的所有界面组件对象就变成了新定义的类MainWindow的公共属性,可以直接访问这些界面组件。

  • 使用 self.__ui = Ui_MainWindow() 设置私有属性,更符合面向对象封装隔离的设计思想。self.__ui.Lab表示窗体上的对象标签,self.Lab则表示MainWindow类里新定义的属性。两者不易混淆,有利于界面与业务逻辑的分离。

  • 信号(Signal)/槽(Slot):GUI程序设计的主要内容就是对界面上各组件发射的特定信号进行响应,只需要知道什么情况下发射了哪些信号,然后合理地去响应和处理这些信号。

  • 槽(Slot):槽实质上是一个函数,它可以被直接调用。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射(emit)时,关联的槽函数会被自动执行。

  • 用Qt Designer设计界面时,对于需要在窗体业务逻辑类里访问的界面组件,修改其objectName,以便在程序中进行区分。每个组件需要有一个唯一的objectName,自动生成的槽函数名称与objectName有关。

  • 添加总布局后,当窗体大小改变时,各个组件都会自动改变大小。组件之间的层次关系可以在Object Inspector获取。

  • 伙伴关系(Buddy )是为了在程序运行时,在窗体上用快捷键快速将输入焦点切换到某个组件上。

  • Qt的界面组件都是从QWidget继承而来的,都支持信号与槽的功能。

  • UI Designer工具栏里的“Edit Signals/Slots”使用可视化的方式实现信号与槽函数的关联。需要设置Sender、Signal、Receiver、Slot。

sender.signalName.connect(receiver.slotName)
  • 在一个GroupBox中应用布局,体现在代码中就是:
groupBox = QGroupBox(Dialog)

# 以groupBox为父容器创建布局和组件
layout = QHBoxLayout(groupBox)
chkBox1 = QCheckBox(groupBox)
chkBox2 = QCheckBox(groupBox)

# 将部添加到layout中
layout.addWidget(chkBox1)
layout.addWidget(chkBox2)
  • toggled(bool)信号在复选框的状态变化时发射,复选框的勾选状态作为参数传递给函数。

  • 下面的函数实现了信号与槽的关联。

QtCore.QMetaObject.connectSlotsByName(MainWindow)

# 槽函数的命名规则
on_objectName_signalName(signal parameters)
# 例子
on_btnClear_clicked()
  • @pyqtSlot()修饰符用于声明槽函数的参数类型。自定义的槽函数的函数名可以使用“do_”作为前缀,提高可读性。

  • 使用PyQt5.QtCore.pyqtSignal()为一个类定义新的信号。类必须是QObject类的子类。信号具有connect()、disconnect()和emit(),分别对应关联槽函数、断开与槽函数的关联、发射信号。

  • 尽量不要定义overload型信号。

  • 资源文件(.qrc)最主要的功能是存储图标和图片文件,图标通常保存为十六进制编码数据。利用pyrcc5将 .qrc 文件转为对应的Python文件。

  • Qt C++类库和PyQt5之间存在差异的类和接口函数并不多。

第三章 GUI 应用程序设计

  • 在对overload型信号编写槽函数时,如果不清楚哪个是默认的信号,最好直接使用@pyqtSlot()修饰符对参数类型进行声明。

  • QPushButton有一个checkable属性,如果设置为True, QPushButton按钮可以当作CheckBox或RadioButton使用。

  • 属性 autoExclusive=True, checkable=True 可以将一组QPushButton设置为互斥的。

  • QSlider和QScrollBar最常用的一个信号是valueChanged(int),在拖动滑块改变当前值时就会发射这个信号。

  • QTimer主要的属性是interval,是定时中断的周期,单位是毫秒。QTimer主要的信号是timeout(),在定时中断时发射此信号。

timer.timeout.connect()
  • QMainWindow是主窗体类,可以作为一个应用程序的主窗体,具有主菜单栏、工具栏、状态栏等主窗体常见的界面元素。

  • QAction是直接从QObject继承而来的一个类,不是一个可视组件。QAction就是一个实现某些功能的“动作”,可以为其编写槽函数,使用一个QAction对象可以创建菜单项、工具栏按钮,点击菜单项或工具栏按钮就执行了关联的Action的槽函数。

  • 互斥的 Action 通过QActionGroup分组对象实现:

format_group = QActionGroup(self)
format_group.setExclusive(True)
format_group.addAction(self.alignl_action) #左对齐
format_group.addAction(self.alignc_action) #居中
format_group.addAction(self.alignr_action) #右对齐
format_group.addAction(self.alignj_action) #两端
  • 以Word程序为例,窗体最上方是菜单栏(Menu Bar),菜单栏下方是工具栏(Tool Bar),最下方是状态栏(Status Bar)。

  • 状态栏 QStatusbar,addWidget()函数按从左到右顺序将一个组件添加到状态栏,addPermanentWidget()添加的组件则位于状态栏的最右边。

  • 工具栏 QToolbar,addWidget():添加一个界面组件到工具栏;addAction():添加一个QAction对象并创建工具栏按钮; addSeparator():添加一个分隔条。

  • QAction常用的信号是triggered()和triggered(bool),它们是overload型信号。triggered(bool)是带有复选状态参数的信号。

  • QToolButton有一个setDefaultAction()函数,使其与一个Action关联,自动获取Action的文字、图标、ToolTip等设置作为按钮的相应属性。单击一个QToolButton按钮就会执行Action的槽函数,与工具栏上的按钮一样。

  • 下拉菜单的实现方式:创建一个QMenu对象、选择列表项的Action添加作为菜单项;setMenu(QMenu_obj)为一个ToolButton按钮指定下拉菜单、QToolButton的setPopupMode()函数为一个ToolButton按钮的下拉式菜单设置不同的弹出方式;

  • 每个从QWidget继承的类都有信号customContextMenuRequested(),这个信号在鼠标右键单击时发射,为此信号编写槽函数,可以创建和运行右键快捷菜单。

  • 首先创建一个QMenu类型的对象menuList,然后利用QMenu的addAction()方法添加已经设计的Action作为菜单项。创建完菜单后,使用QMenu的exec()函数显示快捷菜单。

menuList = QMenu(self)
menuList.addAction(...)
# 类函数QCursor.pos()获得鼠标光标当前位置
menuList.exec(QCursor.pos())
  • QTreeWidget是创建和管理目录树结构的类。

  • ScrollArea上面放置一个QLabel组件,QLabel的pixmap属性可以显示图片。通过QPixmap对象的操作可进行缩放显示。当图片较小时,显示的图片可以自动居于scrollArea的中间,当显示的图片大小超过scrollArea可显示区域的范围时,scrollArea会自动显示水平或垂直方向的滚动条,用于显示更大范围的区域。

  • QPixmap存储图片数据,并且可以缩放图片,缩放只需调用相应函数,返回缩放后的图片副本。QPixmap.load(fileName)函数直接载入一个图片文件的内容。scaledToHeight(height, mode = Qt.FastTransformation):返回一个缩放后的图片的副本,图片缩放到一个高度height。

  • scaledToWidth(width, mode = Qt.FastTransformation):返回一个缩放后的图片的副本,图片缩放到一个宽度width。

  • scaled(width, height, ratio = Qt.IgnoreAspectRatio , mode = Qt.FastTransformation):返回一个缩放后的图片的副本,图片缩放到宽度width和高度height。

  • QLabel.setPixmap(pixmap)函数显示一个QPixmap类对象pixmap存储的图片。

  • QTableWidget是PyQt5中的表格组件类,每一个单元格是一个QTableWidgetItem对象。

  • QGroupBox组件是常用的容器类组件,可以在一个GroupBox里放置其他界面组件且进行布局。QGroupBox有两个属性:checkable和checked。当checked为False时,GroupBox组件内部的所有组件都被禁用。

  • layoutLeftMargin、layoutTopMargin、layoutRightMargin和layoutBottomMargin这4个属性用于设置布局组件与父容器的4个边距,默认为9。

  • 水平布局类QHBoxLayout和垂直布局类QVBoxLayout都有一个属性spacing,用于设置布局内组件之间的间隔,默认为6。

  • 水平布局QHBoxLayout和垂直布局QVBoxLayout都有一个layoutStretch属性,用于设置各组件宽度分配比例。

# groupBox内有3的组件,如下设置只有一个组件可随窗口伸缩
self.horizontalLayout.setStretch(0, 0, 1)
  • Lay Out Horizontally in Splitter左右分割布局。

第4章 Model/View结构

  • Model/View(模型/视图)结构:源数据由模型(Model)读取,然后在视图(View)组件上显示和编辑,在界面上编辑修改的数据又通过模型保存到源数据。将数据模型和用户界面分离开来。

  • 将界面组件与原始数据分离,又通过数据模型将界面和原始数据关联起来,从而实现界面与原始数据的交互操作。

    • Data(源数据)是原始数据。
    • View(视图或视图组件)是界面组件,视图从数据模型获得数据然后显示在界面上。
    • Model(模型或数据模型)与源数据通信,并为视图组件提供数据接口。
    • Delegate(代理或委托)在视图与模型之间交互操作时提供临时编辑组件的功能。
  • Delegate代理负责从数据模型获取相应的数据,然后显示在编辑器里,修改数据后又将数据保存到数据模型中。

  • 通过数据模型存取的每个数据都有一个模型索引,视图组件和代理都通过模型索引来获取数据。保证数据的表示与数据存取方式的分离。

  • 要获得一个模型索引,必须提供3个参数:行号、列号、父项的模型索引。

  • 在构造数据项的模型索引时,必须指定正确的行号、列号和父节点。

  • QFileSystemModel为本机的文件系统提供一个数据模型,可用于访问本机的文件系统。

  • QStringListModel是用于处理字符串列表的数据模型,可以作为QListView的数据模型,在界面上显示和编辑字符串列表。QListView的setModel()函数用于设置一个数据模型。

  • 数据模型与视图组件之间信号与槽作用的结果,当数据模型的内容发生改变时,通知视图组件更新显示。数据模型的数据与界面上视图组件显示的内容是同步的。

  • QStandardItemModel通常与QTableView组成Model/View结构,实现通用的二维数据的管理。

  • 为TableView组件的某列或某个单元格设置自定义代理组件,根据数据的类型限定使用不同的编辑组件。

第5章 事件处理

  • 基于窗体(Widget)的应用程序都是由事件(event)驱动的,鼠标单击、按下某个按键、重绘某个组件、最小化窗口都会产生相应的事件,应用程序对这些事件作出相应的响应处理以实现程序的功能。

  • app.exec_()开启了应用程序的事件处理循环。

  • QEvent还有很多子类表示具体的事件,如QKeyEvent表示按键事件,QMouseEvent表示鼠标事件,QPaintEvent表示窗体绘制事件。

  • 当一个事件发生时,PyQt5会根据事件的具体类型用QEvent相应的子类创建一个事件实例对象,然后传递给产生事件的对象的event()函数进行处理。

  • QWidget定义了很多的默认事件处理函数,都会传递一个event参数,但是event的类型由具体事件类型决定。

  • 用户在继承于QWidget或其子类的自定义类中可以重新实现这些默认的事件处理函数,从而实现一些需要的功能。如重新实现mouseReleaseEvent()函数对鼠标单击事件进行处理,为QWidget添加clicked()信号。

  • 事件与信号是有区别的,但是也有关联。Qt为某个界面组件定义的信号通常是对某个事件的封装,例如QPushButton有clicked()信号和clicked(bool)信号,就可以看作是对mouseReleaseEvent()事件的不同封装。

  • QLabel没有doubleClicked()信号,可以通过事件处理和自定义信号创建一个具有doubleClicked()信号的新的标签类。

  • 事件与信号的关系:信号可以看作是对事件的一种封装。

  • QEvent.Paint事件类型的默认处理函数是paintEvent(),就是用于绘制窗体背景图片的函数。

  • 事件过滤器(event filter):将一个对象的事件委托给另一个对象来监测并处理。

  • 如下程序,self是两个QLabel组件所在的窗体,这样,界面组件LabHover和LabDBClick就将窗体注册为其事件监测者,在LabHover或LabDBClick组件上触发的事件会发送给窗体进行处理。

self.ui.labHover.installEventFilter(self)
self.ui.labelDBClick.installEventFilter(self)


# 窗体通过重新实现eventFilter()函数对被监测的对象及其事件进行处理
def eventFilter(self, watched, event):
    # 通过watched判断哪个是被监测对象
    # 根据event.type()判断事件类型并作出相应处理

    # 执行父类的eventFilter()函数
    return super().event.Filter(watched, event)
  • QApplication类型的应用程序执行exec_()函数后就开始了事件的循环处理。事件队列未能得到及时处理,用户会感觉到响应迟滞。有两种解决方案:

    • 采用多线程方法。
    • 另外一种简单的处理方法是使用QCoreApplication的类函数processEvents()。
  • 在一个耗时较长的计算处理过程中不允许用户用鼠标或键盘操作。

from PyQt5.QtWidgets import qApp

qApp.processEvents(QEventLoop.ExcludeUserInputEvents)
  • 拖放操作涉及:mousePressEvent()、mouseMoveEvent()、dragEnterEvent()、dropEvent() 事件函数。

  • QLabel组件的scaledContents属性设置为True,让图片适应QLabel组件的大小。

  • setAcceptDrops()是QWidget类定义的函数,用于设置一个窗体组件是否接受放置操作。将界面组件设置为不接受放置,而窗体接受放置,事件也是传播到组件所在的父容器,也就是由窗口处理。

  • MIME (Multipurpose Internet Mail Extensions)是多功能因特网邮件扩展。QMimeData是对MIME数据的封装,在拖放操作和剪切板操作中都用QMimeData类描述传输的数据。

第6章 对话框与多窗口设计

  • 若要打开一个文件,调用类函数QFileDialog.getOpenFileName()。

  • QColorDialog是选择颜色对话框,选择颜色使用类函数QColorDialog.getColor()。

  • QFontDialog是选择字体对话框,选择字体使用类函数QFontDialog.getFont()。

  • QProgressDialog是用于显示进度的对话框,可以在大的循环操作中显示操作进度。

  • QInputDialog有单行字符串输入、整数输入、浮点数输入、列表框选择输入和多行文本输入等多种输入方式。

  • 消息对话框QMessageBox用于显示提示、警告、错误等信息,或进行确认选择。

  • 常用的窗体基类是QWidget、QDialog和QMainWindow,在创建GUI应用程序时选择窗体基类就是从这3个类中选择。

  • QWidget:在没有指定父容器时可作为独立的窗口,指定父容器后可以作为父容器的内部组件。QWidget是所有界面组件的基类。

  • MDI(Multiple Document Interface)就是多文档界面,它是一种应用程序窗口管理方法,一般是在一个应用程序里打开多个同类型的窗口。

第8章 绘图

  • PyQt5提供了两种绘图方法。一种是使用QPainter类在QWidget类提供的画布上画图,可以绘制点、线、圆等各种基本形状,从而组成自己需要的图形。

  • 所有界面组件都是QWidget的子类,界面上的按钮、编辑框等各种组件的界面效果都是使用QPainter绘制出来的。

  • PyQt5另外提供一种基于Graphics View架构的绘图方法,这种方法使用QGraphicsView、QGraphicsScene和各种QGraphicsItem图形项绘图,在一个场景中可以绘制大量图件,且每个图件是可选择、可交互的,如同矢量图编辑软件那样可以操作每个图件。Graphics View架构为用户绘制复杂的组件化图形提供了便利。

  • QPainter是用来进行绘图操作的类,一般的绘图设备包括QWidget、QPixmap、QImage等,这些绘图设备为QPainter提供了一个“画布”。

  • 使用QPainter对象在一个QWidget窗体的paintEvent()事件函数里直接绘图。

  • 可以从QWidget继承一个类,创建自定义界面组件。

  • 在UI可视化设计时,可以使用提升法(promotion)将一个组件类提升为其某个子类。

  • PyQt5为绘制复杂的可交互的图形提供了Graphics View绘图架构,它是一种基于图形项(GraphicsItem)的模型/视图结构。

  • Graphics View架构主要由3部分组成,即场景、视图和图形项。

  • 视图(View)是QGraphicsView提供绘图的视图组件,用于显示场景中的内容。可以为一个场景设置多个视图,用于对同一个数据集提供不同的视口。

  • QGraphicsItem可以被选择、拖放、组合,若编写信号的槽函数代码或事件函数响应代码,还可以实现各种编辑和操作功能。

  • QGraphicsView没有与mouseMoveEvent()相关的信号。从QGraphicsView继承定义一个类QmyGraphicsView,实现鼠标移动事件函数mouseMoveEvent()和鼠标按键事件函数mousePressEvent()的处理,并把事件转换为自定义信号,这样就可以在主程序里设计槽函数响应这些鼠标事件。

第9章 文件

  • os.gtcwd()函数获取当前路径。内建函数open()打开文本文件并读取文件内容。

  • PyQt5中用于文件读写操作的类是QFile,它提供了文件读写的接口函数,可以直接对文件进行读写。

  • Python自带的os和os.path模块中提供了大量的目录和文件操作相关的函数,如获取当前目录、新建目录、复制文件、分离文件的路径和基本文件名、判断文件是否存在等。

  • QFile类是直接与I/O设备打交道进行文件读写操作的类,使用QFile可以直接打开或保存文本文件。

  • 除文本文件之外,其他的需要按照一定的格式定义读写的文件都可称为二进制文件。每种格式的二进制文件都有自己的格式定义,写入数据时按照一定的顺序,读出时也按照相应的顺序。

  • 目录和文件操作包括获取当前目录、新建或删除目录、获取文件的基本文件名和后缀、复制或删除文件等操作。