Python Qt GUI与数据可视化编程
上QQ阅读APP看书,第一时间看更新

3.9 QTreeWidget和QDockWidget

3.9.1 功能概述

本节介绍QTreeWidget、QDockWidget的使用方法,并且结合QLabel的图片显示功能创建一个图片管理器,图3-22是示例Demo3_9运行时界面。这个示例主要演示如下几个组件的使用方法。

图3-22 示例Demo3_9运行时界面

· QTreeWidget目录树组件:QTreeWidget是创建和管理目录树结构的类。示例使用一个QTreeWidget组件管理照片目录,可以添加、删除分组和图片节点。每个节点是一个QTreeWidgetItem对象,每个节点除显示的标题和图标外,还有类型和自定义数据,示例中图片节点的自定义数据存储了完整的图片文件名,点击节点时就可以根据存储的文件名读取图片文件并显示。

· QDockWidget停靠区组件:QDockWidget是可以在QMainWindow窗口停靠,或在桌面最上层浮动的界面组件。本示例将一个QTreeWidget组件放置在一个QDockWidget区域上,设置其可以在主窗体的左侧或右侧停靠,也可以浮动。

· QLabel组件显示图片:窗体工作区右侧是一个QScrollArea组件,ScrollArea上面放置一个QLabel组件,QLabel的pixmap属性可以显示图片。通过QPixmap对象的操作可进行缩放显示,包括放大、缩小、实际大小、适合宽度、适合高度等。

示例Demo3_9由mainWindowApp项目模板创建而来,程序功能主要用Action实现,主菜单和主工具栏都由Action实现。

下面先列出窗体业务逻辑类QmyMainWindow所在文件myMainWindow.py的import部分、两个枚举类型类的定义、QmyMainWindow构造函数部分的代码,其他功能实现代码在讲解过程中逐步介绍。

        import sys
        from PyQt5.QtWidgets import  (QApplication, QMainWindow,
                          QTreeWidgetItem, QLabel, QFileDialog, QDockWidget)
        from enum import Enum     ##枚举类型
        from PyQt5.QtCore import  pyqtSlot, Qt, QDir, QFileInfo
        from PyQt5.QtGui import  QIcon, QPixmap
        from ui_MainWindow import Ui_MainWindow


        class TreeItemType(Enum): ##节点类型枚举类型
            itTopItem=1001         #顶层节点
            itGroupItem=1002       #组节点
            itImageItem=1003       #图片文件节点


        class TreeColNum(Enum):   ##目录树的列号枚举类型
            colItem=0               #分组/文件名列
            colItemType=1          #节点类型列


        class QmyMainWindow(QMainWindow):
            def __init__(self, parent=None):
              super().__init__(parent)    #调用父类构造函数,创建窗体
              self.ui=Ui_MainWindow()     #创建UI对象
              self.ui.setupUi(self)       #构造UI


              self.curPixmap=QPixmap()    #图片
              self.pixRatio=1              #显示比例
              self.itemFlags=(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable
                        | Qt.ItemIsEnabled | Qt.ItemIsAutoTristate)    #节点标志
              self.setCentralWidget(self.ui.scrollArea)
              self.__iniTree()


        ##以下的属性在UI Designer里已经设置,这里是代码设置方法
              self.ui.dockWidget.setFeatures(QDockWidget.AllDockWidgetFeatures)
              self.ui.dockWidget.setAllowedAreas(
                    Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
              self.ui.scrollArea.setWidgetResizable(True)    #自动调整内部组件大小
              self.ui.scrollArea.setAlignment(Qt.AlignCenter)
              self.ui.LabPicture.setAlignment(Qt.AlignCenter)

在构造函数中定义了3个变量,其中self.curPixmap用于存储当前显示的原始图片,图片的放大、缩小等处理都是基于这个原始图片的;self.pixRatio是图片显示比例,1表示原始大小;self.itemFlags是创建的目录树节点的标志,此处定义为一个变量是为了避免重复写相同内容的代码,且便于修改。

有几行代码是对界面上的dockWidget、scrollArea和LabPicture组件的属性设置,这些属性在UI Designer里已经可视化设置了,这里是演示代码设置方法。

3.9.2 窗体可视化设计

1.界面布局设计

窗体工作区左侧是一个QDockWidget组件dockWidget,在dockWidget上放置一个QTreeWidget组件,用水平布局使其充满停靠区。

工作区右侧是一个QScrollArea组件scrollArea,在scrollArea里放置一个QLabel组件,利用QLabel的pixmap属性显示图片。scrollArea内部的组件采用水平布局,当图片较小时,显示的图片可以自动居于scrollArea的中间,当显示的图片大小超过scrollArea可显示区域的范围时,scrollArea会自动显示水平或垂直方向的滚动条,用于显示更大范围的区域。

在主窗体构造函数里将scrollArea设置为主窗体工作区的中心组件后,dockWidget与scrollArea之间自动出现分割条,用于两个组件的左右分割。

2.QDockWidget组件属性设置

在UI Designer里可对DockWidget组件的主要属性进行设置,主要属性有以下两个。

· allowedAreas属性,设置允许停靠区域。停靠区域是枚举类型Qt.DockWidgetArea的值的组合,可以设置在窗口的左、右、顶、底停靠,所有区域都可停靠或不允许停靠。本示例设置为允许左侧和右侧停靠,即构造函数里的语句:

            self.ui.dockWidget.setAllowedAreas(
                              Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

· features属性,设置停靠区组件的特性。features是枚举类型QDockWidget.DockWidgetFeature的值的组合,枚举值有:

♦ QDockWidget.DockWidgetClosable(停靠区可关闭);

♦ QDockWidget.DockWidgetMovable(停靠区可移动);

♦ QDockWidget.DockWidgetFloatable(停靠区可浮动);

♦ QDockWidget.DockWidgetVerticalTitleBar(在停靠区左侧显示垂直标题栏);

♦ QDockWidget.AllDockWidgetFeatures(使用以上所有特征);

♦ QDockWidget.NoDockWidgetFeatures(不能停靠、移动和关闭)。

本示例设置为可关闭、可停靠、可浮动,即构造函数里的语句:

            self.ui.dockWidget.setFeatures(QDockWidget.AllDockWidgetFeatures)

3.QTreeWidget组件的设置

QTreeWidget是目录树结构的界面组件,每个节点是一个QTreeWidgetItem对象。QTreeWidget可以有多列,节点层次可以无限嵌套。

在UI Designer里双击界面上的QTreeWidget组件,可以打开如图3-23所示的设计器,设计器有两页,分别对Columns和Items进行设计。

图3-23 QTreeWidget组件的设计器(Items页面)

Columns页用于设计目录树的列,目录树可以有多列。在设计器里可以添加、删除、移动列,设置列的文字、字体、前景色、背景色、文字对齐方式、图标等。本示例设置了两个列,标题分别为“节点”和“节点类型”。

Items页面用于设计目录树的节点,可对每个节点进行设置,设置文字、字体、图标等,特别是flags属性,可以设置节点是否可选、是否可编辑、是否有CheckBox等,还可以设置节点的CheckState。在图3-23下方有一组按钮可以进行新增节点、新增下级节点、删除节点、改变节点级别、平级移动节点等操作。

使用设计器设计目录树的列和节点,适用于创建固定结构的目录树,但是目录树一般是根据内容动态创建的,需要运用代码实现对节点的动态控制。

4.Action设计

本示例的功能代码大多采用Action实现,在Action编辑器里设计Action,然后利用Action设计主菜单和主工具栏。设计完成的Action如图3-24所示。

图3-24 设计完成的Action

3.9.3 QTreeWidget操作

1.本示例的目录树节点操作规则

本示例的目录树节点操作定义如下一些规则。

· 将目录树的节点分为3种类型,即顶层节点、分组节点和图片节点。

· 窗体创建时初始化目录树,初始化的目录树只有一个顶层节点,这个顶层节点不能被删除,而且不允许再新建顶层节点。

· 顶层节点下允许添加分组节点和图片节点。

· 分组节点下可以添加分组节点或图片节点,分组节点的级数不受限制。

· 图片节点是终端节点,可以在图片节点同级再添加图片节点。

· 创建每个节点时设置其类型信息,图片节点存储图片文件的完整文件名作为自定义数据。

· 单击一个图片文件节点时,显示其关联文件的图片。

2.枚举类型的定义

在文件myMainWindow.py中定义了两种枚举类型,其中TreeItemType是表示节点类型的枚举类型,TreeColNum是表示目录树的各个列的编号的枚举类型,定义如下:

        class TreeItemType(Enum): ##节点类型的枚举类型
            itTopItem=1001         #顶层节点
            itGroupItem=1002       #分组节点
            itImageItem=1003       #图片文件节点


        class TreeColNum(Enum):   ##目录树的列号的枚举类型
            colItem=0               #分组/文件名列
            colItemType=1          #节点类型列

在PyQt5中有大量的枚举类型的定义,很多函数都使用枚举类型的参数。在Python中枚举类型需要定义为一个类,从enum模块中的Enum类继承。枚举类型的枚举常量有对应的值,例如,目录树的列号枚举类型TreeColNum的常量用TreeColNum.colItem和TreeColNum.colItemType表示,但如果要取枚举常量的值,需要用枚举常量的value表示,所以下面的两个等式成立。

        TreeColNum.colItem.value==0
        TreeColNum.colItemType.value==1

在程序设计中将目录树或表格组件的列用枚举类型表示是为了便于后面的修改,例如在目录树中插入了某个列,原来的列的编号就会发生改变,我们只需在枚举类型里更改一下定义就可以了。

当然也可以直接定义一个变量表示某个数值,并当作“常数变量”来使用,但是Python里没有类似于C++中的const关键字,不能保证程序中不会意外修改所定义的“常数变量”。

在Python中也可以用字典数据代替枚举类型数据,但是字典数据的值也是可以在程序里修改的。若要严格使用不可被更改的常量,还是使用枚举类型保险一些。

3.目录树初始化添加顶层节点

在QmyMainWindow的构造函数里调用了自定义函数__iniTree()对目录树进行初始化,只是添加了一个顶层节点,该函数的实现代码如下:

        def __iniTree(self):    ##初始化目录树
            self.ui.treeFiles.clear()
            icon= QIcon(":/images/icons/15.ico")


            item=QTreeWidgetItem(TreeItemType.itTopItem.value)
            item.setIcon(TreeColNum.colItem.value, icon)
            item.setText(TreeColNum.colItem.value, "图片文件")
            item.setFlags(self.itemFlags)
            item.setCheckState(TreeColNum.colItem.value, Qt.Checked)
            item.setData(TreeColNum.colItem.value, Qt.UserRole, "")
            self.ui.treeFiles.addTopLevelItem(item)

QTreeWidget的每个节点是一个QTreeWidgetItem类对象,添加一个节点需先创建节点,并做好相关设置。这里创建节点的语句是:

        item=QTreeWidgetItem(TreeItemType.itTopItem.value)

这里传递了枚举类型常量值TreeItemType.itTopItem.value作为节点类型。在构造函数里传递一个整数表示的类型值之后,用QTreeWidgetItem.type()函数可以返回这个类型值。

QTreeWidgetItem有以下几个重要的接口函数。

· setIcon()和setText():为节点的某一列设置图标和文字,都需要传递一个列号作为参数。列号可以直接用数字,但是为了便于理解代码和统一修改,这里使用了自定义的枚举类型常量TreeColNum.colItem.value,其值为0,表示目录树的第1列。

· setFlags()函数:设置节点的一些属性标志,是Qt.ItemFlag枚举类型常量的组合,传递的参数self.itemFlags在构造函数里已经定义为:

            self.itemFlags=(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable
                            | Qt.ItemIsEnabled | Qt.ItemIsAutoTristate)

这表示节点是可选择的、可以勾选的(节点前面出现一个复选框)、可以使用的、自动三态切换的。三态指的是枚举类型Qt.CheckState所表示的三种状态,即:

♦ Qt.Unchecked(未被勾选);

♦ Qt.PartiallyChecked(部分被勾选);

♦ Qt.Checked(被勾选)。

· setData()函数:为节点的某一列设置一个角色数据,setData()函数原型为:

            setData(self, column, role, value)

其中,column是列号,role是表示角色的值,value是任意类型的数据(即Qt C++中的QVariant)。代码中设置节点数据的语句是:

            item.setData(TreeColNum.colItem.value, Qt.UserRole, "")

它为节点的第1列,角色Qt.UserRole,设置了一个空字符串数据。Qt.UserRole是枚举类型Qt.ItemDataRole中一个预定义的值,关于节点的角色和Qt.ItemDataRole在4.1节详细介绍。

创建并设置好节点后,用QTreeWidget.addTopLevelItem()函数将节点作为顶层节点添加到目录树。

4.添加目录节点

actTree_AddFolder是用于添加组节点的Action,只有当目录树上的当前节点类型是itTopItem或itGroupItem时才可以添加组节点。actTree_AddFolder的triggered()信号的槽函数代码如下:

        @pyqtSlot()    ##添加目录节点
        def on_actTree_AddFolder_triggered(self):
            dirStr=QFileDialog.getExistingDirectory()    #选择目录
            if (dirStr == ""):
              return


            parItem=self.ui.treeFiles.currentItem()      #当前节点
            if (parItem == None):
              parItem=self.ui.treeFiles.topLevelItem(0)
            icon= QIcon(":/images/icons/open3.bmp")
            dirObj=QDir(dirStr)           #QDir对象
            nodeText=dirObj.dirName()     #最后一级目录的名称


            item=QTreeWidgetItem(TreeItemType.itGroupItem.value)    #节点类型
            item.setIcon(TreeColNum.colItem.value, icon)
            item.setText(TreeColNum.colItem.value, nodeText)         #第1列
            item.setText(TreeColNum.colItemType.value, "Group")      #第2列
            item.setFlags(self.itemFlags)
            item.setCheckState(TreeColNum.colItem.value, Qt.Checked)
            item.setData(TreeColNum.colItem.value, Qt.UserRole, dirStr)   #关联数据
            parItem.addChild(item)
            parItem.setExpanded(True)    #展开节点

在目录树中添加顶层节点时用QTreeWidget.addTopLevelItem()函数,但是添加非顶层节点时,要将节点添加到父节点下面,节点和父节点都是QTreeWidgetItem对象,添加子节点用QTreeWidgetItem的addChild()函数。

这段代码的功能是选择一个目录,用目录的末级名称作为节点的名称,创建一个分组节点,节点的类型是TreeItemType.itGroupItem.value,节点的关联数据是完整的目录名称。

5.添加图片文件节点

actTree_AddFiles是添加图片文件节点的Action,当目录树的当前节点为任何类型时这个Action都可用。actTree_AddFiles的槽函数代码如下:

        @pyqtSlot()    ##添加图片文件节点
        def on_actTree_AddFiles_triggered(self):
            fileList, flt=QFileDialog.getOpenFileNames(self,
                          "选择一个或多个文件", "", "Images(*.jpg)")
            if (len(fileList)<1):    #fileList是list[str]
              return
            item=self.ui.treeFiles.currentItem()                       #当前节点
            if (item.type()==TreeItemType.itImageItem.value):         #当前是图片节点
              parItem=item.parent()
            else:     #否则取当前节点为父节点
              parItem=item


            icon= QIcon(":/images/icons/31.ico")
            for i in range(len(fileList)):
              fullFileName=fileList[i]         #带路径文件名
              fileinfo=QFileInfo(fullFileName)
              nodeText=fileinfo.fileName()     #不带路径文件名
              item=QTreeWidgetItem(TreeItemType.itImageItem.value)   #节点类型
              item.setIcon(TreeColNum.colItem.value, icon)           #第1列的图标
              item.setText(TreeColNum.colItem.value, nodeText)       #第1列的文字
              item.setText(TreeColNum.colItemType.value, "Image")     #第2列的文字
              item.setFlags(self.itemFlags)
              item.setCheckState(TreeColNum.colItem.value, Qt.Checked)
              item.setData(TreeColNum.colItem.value, Qt.UserRole, fullFileName)
              parItem.addChild(item)


            parItem.setExpanded(True)    #展开节点

代码首先用类函数QFileDialog.getOpenFileNames()获取图片文件列表,返回结果变量fileList是一个字符串列表,列表的每一项是所选择的一个文件名。

然后判断当前节点类型,选择一个分组节点作为父节点parItem。

再根据选择的文件列表,为每一个文件创建一个节点,并添加到父节点下。创建的节点类型设置为TreeItemType.itImageItem.value,表示图片节点,节点关联的数据是图片文件带路径的完整文件名,这个数据在单击节点打开图片文件时会用到。

这段代码里涉及的打开文件对话框QFileDialog在6.1节有详细介绍,获取文件信息的类QFileInfo在9.4节有详细介绍。

6.当前节点变化后的响应

目录树上当前节点变化时,会发射currentItemChanged()信号,为此信号创建槽函数,实现当前节点类型判断、几个Action的使能控制、显示图片等功能,代码如下:

        def on_treeFiles_currentItemChanged(self, current, previous):
            if (current == None):
              return
            nodeType=current.type()    #获取节点类型
            if (nodeType==TreeItemType.itTopItem.value):        #顶层节点
                self.ui.actTree_AddFolder.setEnabled(True)
                self.ui.actTree_AddFiles.setEnabled(True)
                self.ui.actTree_DeleteItem.setEnabled(False)     #顶层节点不能删除
            elif (nodeType==TreeItemType.itGroupItem.value):    #分组节点
                self.ui.actTree_AddFolder.setEnabled(True)
                self.ui.actTree_AddFiles.setEnabled(True)
                self.ui.actTree_DeleteItem.setEnabled(True)
            elif (nodeType==TreeItemType.itImageItem.value):    #图片节点
                self.ui.actTree_AddFolder.setEnabled(False)
                self.ui.actTree_AddFiles.setEnabled(True)
                self.ui.actTree_DeleteItem.setEnabled(True)
                self.__displayImage(current)    #显示图片

函数的参数current和previous都是QTreeWidgetItem类型的变量,分别表示当前节点和前一节点。

通过current.type()可以获得当前节点的类型,也就是在创建节点时设置的枚举类型TreeItemType的具体值。代码根据当前节点类型控制界面上3个Action的使能状态,如果是图片文件节点,还调用__displayImage()函数显示节点关联的图片,这个函数的功能实现在后面介绍QLabel图片显示部分再详细介绍。

7.删除节点

除了顶层节点,选中一个分组节点或图片文件节点后可以删除此节点及其子节点。actTree_DeleteItem是实现节点删除的Action,其槽函数代码如下:

        @pyqtSlot()    ##删除当前节点
        def on_actTree_DeleteItem_triggered(self):
            item =self.ui.treeFiles.currentItem()
            parItem=item.parent()
            parItem.removeChild(item)

QTreeWidgetItem.removeChild()函数用于移除一个节点及其所有子节点。一个节点不能移除自己,所以需要获取其父节点,使用父节点的removeChild()函数来移除该节点。

若是要删除顶层节点,则需要使用QTreeWidget.takeTopLevelItem(index)函数,index是节点的序号。在Python中,移除的节点会被自动删除。

8.节点的遍历

目录树的节点都是QTreeWidgetItem类,可以嵌套多层节点。有时需要在目录树中遍历所有节点,例如按条件查找某些节点、统一修改节点的标题等。遍历节点需要用到QTreeWidgetItem类的一些关键函数,还需要使用递归调用函数。

actTree_ScanItems实现工具栏上“遍历节点”的功能,其槽函数及相关自定义函数代码如下:

        @pyqtSlot()    ##遍历节点
        def on_actTree_ScanItems_triggered(self):
            count=self.ui.treeFiles.topLevelItemCount()
            for i in range(count):
              item=self.ui.treeFiles.topLevelItem(i)
              self.__changeItemCaption(item)


        def __changeItemCaption(self, item):    ##递归调用函数,修改节点标题
            title="*"+item.text(TreeColNum.colItem.value)
            item.setText(TreeColNum.colItem.value, title)
            if (item.childCount()>0):
              for i in range(item.childCount()):
                  self.__changeItemCaption(item.child(i))

QTreeWidget的顶层节点没有父节点,如果要访问所有顶层节点,需要用到以下两个函数。

· topLevelItemCount():返回顶层节点个数。

· topLevelItem(index):返回序号为index的顶层节点。

自定义函数__changeItemCaption(item)用于改变节点item及其所有子节点的标题。这个函数是一个递归调用函数,即在这个函数里还会调用函数自己。它的前两行更改传递来的节点item的标题,即在标题前加星号。后面的代码根据item.childCount()是否大于0,判断这个节点是否有子节点,如果有子节点,在后面的for循环里逐一获取子节点,并作为参数再调用__changeItemCaption()函数。

3.9.4 QLabel和QPixmap显示图片

1.显示节点关联的图片

在目录树上单击一个节点后,如果节点类型为TreeItemType.itImageItem.value(图片节点),就会调用__displayImage(item)函数显示节点item的图片。__displayImage()函数的代码如下:

        def __displayImage(self, item):                 ##显示节点item的图片
            filename=item.data(TreeColNum.colItem.value, Qt.UserRole)
            self.ui.statusBar.showMessage(filename)    #状态栏显示文件名
            self.curPixmap.load(filename)               #原始图片
            self.on_actZoomFitH_triggered()             #适合高度显示
            self.ui.actZoomFitH.setEnabled(True)
            self.ui.actZoomFitW.setEnabled(True)
            self.ui.actZoomIn.setEnabled(True)
            self.ui.actZoomOut.setEnabled(True)
            self.ui.actZoomRealSize.setEnabled(True)

QTreeWidgetItem.data()函数返回节点存储的数据,也就是用setData()设置的数据。前面在添加图片节点时,将带路径的文件名存储为节点的数据,这里的第一行语句就可以获得节点存储的图片文件全名。

self.curPixmap是在构造函数中定义的一个QPixmap类型的变量,用于保存原始大小图片。QPixmap.load(fileName)函数直接载入一个图片文件的内容。

然后调用函数on_actZoomFitH_triggered()显示图片,这是actZoomFitH的槽函数,以适应高度的形式显示图片。

2.图片缩放与显示

有几个Action实现图片的缩放显示,包括适合宽度、适合高度、放大、缩小、实际大小等,这几个槽函数的代码如下:

        @pyqtSlot()    ##适应高度显示图片
        def on_actZoomFitH_triggered(self):
            H=self.ui.scrollArea.height()    #得到scrollArea的高度
            realH=self.curPixmap.height()    #原始图片的实际高度
            self.pixRatio=float(H)/realH     #当前显示比例,必须转换为浮点数
            pix=self.curPixmap.scaledToHeight(H-30)    #图片缩放到指定高度
            self.ui.LabPicture.setPixmap(pix)          #设置Label的PixMap


        @pyqtSlot()    ##适应宽度显示图片
        def on_actZoomFitW_triggered(self):
            W=self.ui.scrollArea.width()-20
            realW=self.curPixmap.width()
            self.pixRatio=float(W)/realW
            pix=self.curPixmap.scaledToWidth(W-30)
            self.ui.LabPicture.setPixmap(pix)          #设置Label的PixMap


        @pyqtSlot()    ##放大显示
        def on_actZoomIn_triggered(self):
            self.pixRatio=self.pixRatio*1.2
            W=self.pixRatio*self.curPixmap.width()
            H=self.pixRatio*self.curPixmap.height()
            pix=self.curPixmap.scaled(W, H)    #图片缩放到指定高度和宽度,保持长宽比例
            self.ui.LabPicture.setPixmap(pix)


        @pyqtSlot()    ##缩小显示
        def on_actZoomOut_triggered(self):
            self.pixRatio=self.pixRatio*0.8
            W=self.pixRatio*self.curPixmap.width()
            H=self.pixRatio*self.curPixmap.height()
            pix=self.curPixmap.scaled(W, H)    #图片缩放到指定高度和宽度,保持长宽比例
            self.ui.LabPicture.setPixmap(pix)


        @pyqtSlot()    ##实际大小
        def on_actZoomRealSize_triggered(self):
            self.pixRatio=1    #恢复显示比例为1
            self.ui.LabPicture.setPixmap(self.curPixmap)

QPixmap存储图片数据,并且可以缩放图片,QPixmap有以下几个函数。

· scaledToHeight(height, mode = Qt.FastTransformation):返回一个缩放后的图片的副本,图片缩放到一个高度height。可选参数mode表示变换模式,是枚举类型Qt.TransformationMode,默认为Qt.FastTransformation,表示快速变换。另一可能的取值是Qt.SmoothTransformation,表示光滑变换。

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

· scaled(width, height, ratio = Qt.IgnoreAspectRatio , mode = Qt.FastTransformation):返回一个缩放后的图片的副本,图片缩放到宽度width和高度height。可选参数ratio是枚举类型Qt.AspectRatioMode,默认为Qt.IgnoreAspectRatio,即不保持比例。

变量self.curPixmap保存了图片的原始副本,要缩放只需调用相应函数,返回缩放后的图片副本。变量self.pixRatio存储缩放系数,self.pixRatio = 1表示原始大小。

界面上的QLabel组件LabPicture显示图片,是通过使用QLabel.setPixmap(pixmap)函数显示一个QPixmap类对象pixmap存储的图片。

3.9.5 QDockWidget的操作

程序运行时,主窗体上的DockWidget组件可以被拖动,可在主窗体的左侧或右侧停靠,或在桌面上浮动。工具栏上“窗体浮动”和“窗体可见”两个按钮分别控制停靠区是否浮动、是否可见,其代码如下:

        @pyqtSlot(bool)    ##设置停靠区浮动性
        def on_actDockFloat_triggered(self, checked):
            self.ui.dockWidget.setFloating(checked)


        @pyqtSlot(bool)    ##设置停靠区可见性
        def on_actDockVisible_triggered(self, checked):
            self.ui.dockWidget.setVisible(checked)

当单击DockWidget组件标题栏的关闭按钮时,会隐藏停靠区并发射信号visibilityChanged (bool);当拖动DockWidget组件使其浮动或停靠时,会发射信号topLevelChanged(bool)。为这两个信号编写槽函数,可更新两个Action的使能状态,代码如下:

        @pyqtSlot(bool)    ##停靠区浮动性改变
        def on_dockWidget_topLevelChanged(self, topLevel):
            self.ui.actDockFloat.setChecked(topLevel)


        @pyqtSlot(bool)    ##停靠区可见性改变
        def on_dockWidget_visibilityChanged(self, visible):
            self.ui.actDockVisible.setChecked(visible)