龙空技术网

Python学习笔记 | 图形界面Tree Widget和Table Widget组件的实例解析

知者不惑FYK 3335

前言:

当前看官们对“python37界面”可能比较着重,朋友们都需要剖析一些“python37界面”的相关资讯。那么小编在网络上汇集了一些对于“python37界面””的相关知识,希望同学们能喜欢,咱们快快来了解一下吧!

在PySide6图形界面开发模块中,树Tree Widget和表格Table Widget组件的使用方法相对比较复杂,属性的设置和方法的调用都显得很分散、凌乱,短时间内不容易融会贯通。而这两个组件在图形界面开发过程中又要经常用到,尤其是Table Widget表格组件,在进行数据库应用程序开发时又是显示数据的不二选择,因此本篇笔记专门针对这两个难缠的组件进行细致解析。

一、Tree Widget

树形结构是通过根、节点和子节点的包含关系来展现数据的一种常见形式,用于制作图形界面左侧功能栏非常有效,让用户操作更加清晰直观,最典型的应用场景是Windows系统的文件资源管理器。下面对树形结构组件进行介绍:

(一)可视化创建树形结构

1、设置标题栏

在窗口内添加一个Tree Widget组件,然后在这个组件上双击鼠标左键或者按右键选择第一项——Edit Items...,就弹出了Edit Tree Widget设置窗口,窗口内的默认第一个标签页——Columns(列),就是用来设置标题栏的界面。

默认标题栏只有一列,名称是1。在列标题上双击就可以修改列标题文本,利用窗口下方的+-按钮可以添加和删除列标题(增删列),利用上下箭头↑↓按钮可以调整列的前后顺序,点击右下角Properties<<按钮打开标题属性设置窗口,可以分别对列标题的图标、字体、字号、颜色等属性进行设置。

演示视频如下:

视频加载中...

【注】在属性窗口里设置字体大小和颜色等属性,既繁琐又容易出现bug,所以一般都在主窗口的样式表中使用样式来统一调整这些属性。

2、设置项目

设置项目就是添加树的各个节点和子节点信息。在Tree Widget组件上双击打开编辑窗口,点击Items标签页,点击+按钮添加一个项目并填写文本信息,这样就创建了一个节点。这时,窗口下方的+-按钮中间又多了一个按钮,点击这个按钮就可以为当前节点添加子节点。同样,上下箭头↑↓按钮旁边也多了两个按钮,用来调节项目的从属关系。点击右下角Properties<<按钮打开项目属性设置窗口,可以对图标和文本格式进行更改。

演示视频如下:

视频加载中...

【注】在演示中可以看到,我们为了让树形结构界面更好看,手动为每个类别添加了图标,但是节点前面自带的三角符号一直都在,影响美观程度。因此,我们在窗口对象的样式表中添加了一段样式语句,用自定义图标替换了Tree Widget组件自带的三角符号。

替换Tree Widget组件自带三角符号图标的样式语句如下:

QTreeView::branch:has-children:!has-siblings:closed,QTreeView::branch:closed:has-children:has-siblings{image: url(../icon/r_arrow.png);}QTreeView::branch:open:has-children:!has-siblings,QTreeView::branch:open:has-children:has-siblings{ image: url(../icon/b_arrow.png);}

3、Tree Widget组件的重要属性

contextMenuPolicy:上下文菜单策略,这是所有组件的通用属性QWidget设置中的一项,基本上都是采用默认设置。但是,在Tree Widget组件上想弹出右键菜单,需要将这个属性设置为customContextMenu(定制上下文菜单),否则在Tree Widget组件上右键没有反应editTriggers:编辑触发器,就是什么情况触发信号,内容如下:

NoEditTriggers:没有编辑触发器,即把所有选项都清除选择CurrentChanged:当前项目被改变时触发DoubleClicked:双击触发SelectClicked:选择项目被单击,就是单击触发EditKeyPressed:编辑键被按下时触发,Windows操作系统默认编辑键是F2键,但是默认是给文件和文件夹重命名,貌似在Tree Widge中使用无效AnyKeyPressed:任意键按下时触发AllEditTriggers:所有编辑触发器,即将所有选项都变成选择状态
tabkeyNavigation:选项卡导航,是否允许使用Tab键切换项目showDropIndicator:出示放下指示器,当拖放一个项目到另外一个项目上时,在目标项目上显示指示器(目标项目显示一个外框),方便找准位置dragEnabled:是否允许拖拽dragDropOverwriteMode:拖放覆盖写入模式dragDropMode:拖放模式,选项如下:
NoDragDrop:不允许拖放DragOnly:只允许拖拽DropOnly:只允许放下DragDrop:拖放模式,既允许拖拽也允许放下InternalMove:内部移动
defaultDropAction:默认放下执行的动作,选项如下:
CopyAction:复制动作MoveAction:移动动作LinkAction:链接动作ActionMask:动作遮罩TargetMoveAction:目标移动动作IgnoreAction:忽视动作
alternatingRowColors:交替行颜色,即一行白色一行灰色背景selectionMode:选择模式,选项如下:
NoSelection:不允许选择SingleSelection:单行选择MultiSelection:多行选择ExtendedSelection:扩展选择ContiguousSelection:连续选择
selectionBehavior:选择行为,选项如下:
SelectItems:选择项目,即单击时选择一个项目SelectRows:选择行,即单击时选择一行SelectColumns:选择列,即单击时选择一列
rootlsDecorated:根装饰,即根节点上用来展开和收缩树结构的标志(三角符号图标),如果设置为真,则显示标志,点击这个标志可以展开和收缩树;如果设置为假,则不显示标志,展开和收缩时需要双击鼠标。这个根装饰图标可以通过样式表,更换成自己的图标uniformRowHeights:统一行高itemsExpandable:项目可扩展sortingEnable:允许排序,设置为真后,标题栏上会出现一个向上或向下的三角符号,点击则来回切换,分别按正序和倒序排列项目animated:生动的,项目展开时的效果allColumnsShowFocus:所有列显示焦点,当该属性为假时,单击一行,只有被点击列有焦点,其它列没有焦点;如果将该属性设置为真,那么点击一行时整行都有焦点headerHidden:标题栏隐藏expandsOnDoubleClick:双击展开columnCount:默认列数headerVisible:标题栏可见headerCascadingSectionResizes:标题栏分类部分重新调整大小的方式,为真时,拖动前一列标题栏调整宽度时,后边相邻标题栏宽度会随之相应改变,即交互调整;为假时,后面相邻标题栏宽度保持固定不变headerDefaultSectionSize:标题栏默认部分尺寸,用来设置节点名称即第一列的宽度headerHighLightSections:高亮显示标题栏分类,即点击某个节点后,标题栏的对应列高亮显示headerMinimumSectionSize:标题栏最小分类尺寸headerShowSortIndicator:标题栏显示排序指示器,三角符号headerStretchLashSection:标题栏最后的分类是否伸展,设置为真,则树的最后一列伸展补充到Tree Widget组件的宽度

(二)使用代码动态构建树形结构

在Qt设计大师里添加Tree Widget组件,不用设置任何属性,剩余工作都在代码里完成。演示代码如下:

from PySide6.QtWidgets import QApplication, QWidget, QTreeWidgetItemfrom PySide6.QtGui import Qt, QFont, QBrush, QColorfrom PySide6.QtCore import QSizefrom ui_test import Ui_Formclass MainWindow(QWidget):    def __init__(self):        super(MainWindow, self).__init__()        self.ui = Ui_Form()        self.ui.setupUi(self)        # 调用树的初始化方法        self.tree_init()    # 树的初始化方法    def tree_init(self):        self.ui.treeWidget.setColumnCount(2)  # 设置树形结构为2列        self.ui.treeWidget.setHeaderLabels(['诗歌名称', '作者'])  # 设置标题栏        font = QFont()  # 创建字体对象        font.setPointSize(11)  # 设置字体对象的文字大小        font.setBold(True)  # 设置粗体        self.ui.treeWidget.headerItem().setFont(0, font)  # 设置标题栏第一列字体        self.ui.treeWidget.headerItem().setFont(1, font)  # 设置标题栏第二列字体        brush = QBrush(QColor(255, 0, 0))  # 创建刷子对象,并将颜色设置为红色        brush_bg = QBrush(QColor(255, 255, 0))        self.ui.treeWidget.headerItem().setForeground(0, brush)  # 设置标题栏第一列前景色        self.ui.treeWidget.headerItem().setForeground(1, brush)  # 设置标题栏第二列前景色        self.ui.treeWidget.headerItem().setBackground(0, brush_bg)  # 设置标题栏第一列背景色        self.ui.treeWidget.headerItem().setBackground(1, brush_bg)  # 设置标题栏第二列背景色        self.ui.treeWidget.setColumnWidth(0, 100)  # 设置第一列宽度        self.ui.treeWidget.setColumnWidth(1, 80)  # 设置第二列宽度        self.ui.treeWidget.headerItem().setTextAlignment(0, Qt.AlignmentFlag.AlignCenter)  # 设置第一列对齐方式        self.ui.treeWidget.headerItem().setTextAlignment(1, Qt.AlignmentFlag.AlignCenter)  # 设置第二类对齐方式        self.ui.treeWidget.setAlternatingRowColors(True)  # 设置隔行颜色显示        # 调用添加树数据方法        self.addTreeData()    # 添加树数据方法    def addTreeData(self):        # 创建数据字典        data_dict = {            '周南': [('关雎', '佚名(先秦)'), ('樛木', '佚名(先秦)'), ('葛覃', '佚名(先秦)'), ('汉广', '佚名(先秦)')],            '召南': [('鹊巢', '佚名(先秦)'), ('采蘩', '佚名(先秦)'), ('草虫', '佚名(先秦)')],            '邶风': [('柏舟', '佚名(先秦)'), ('绿衣', '佚名(先秦)'), ('日月', '佚名(先秦)')]}        for key, value in data_dict.items():            root = QTreeWidgetItem(self.ui.treeWidget)  # 创建树形结构的顶级节点项目对象            root.setText(0, key)  # 设置顶级节点对象文本            root.setSizeHint(0, QSize(0, 30))  # 设置顶级节点对象行高            self.ui.treeWidget.addTopLevelItem(root)  # 在树中添加顶级节点            for item in value:                child = QTreeWidgetItem(root)  # 创建顶级节点的子节点对象                child.setText(0, item[0])  # 设置第一列文本                child.setText(1, item[1])  # 设置第二列文本                child.setSizeHint(0, QSize(0, 30))  # 设置子节点的行高                child.setTextAlignment(1, Qt.AlignmentFlag.AlignCenter)  # 设置子节点的对齐方式                self.ui.treeWidget.addTopLevelItem(child)  # 在顶级节点下添加子节点        font = QFont()  # 创建字体对象        font.setPointSize(10)  # 设置字号为10        self.ui.treeWidget.topLevelWidget().setFont(font)  # 设置节点字体if __name__ == '__main__':    app = QApplication([])    window = MainWindow()    window.show()    app.exec()

运行效果如图:

(三)完整实例

本实例界面由两个主要部分组成:左侧是Tree Widget组件,右侧是Text Browser组件。左侧的树形结构用来显示诗经中的分类和具体诗歌,支持弹出菜单,并且在分类和诗歌名称上按右键弹出的菜单是不同的。分类上的弹出菜单用来添加诗歌,诗歌名称上的弹出菜单用来删除诗歌,双击分类则展开包含的所有诗歌,双击诗歌则读取相应的文本文件,并将内容显示在右侧的文本浏览器上。并且,树中的项目支持拖放功能。

1、属性设置及效果演示

视频加载中...

2、实例代码

import osfrom PySide6.QtCore import QSizefrom PySide6.QtWidgets import QApplication, QWidget, QMessageBox, QMenu, QInputDialog, QTreeWidgetItemfrom PySide6.QtGui import QCursor, QAction, Qtfrom ui_tree import Ui_Formclass MainWindow(QWidget):    def __init__(self):        super(MainWindow, self).__init__()        self.ui = Ui_Form()        self.ui.setupUi(self)        # 创建两个菜单对象,一个是添加诗歌菜单,一个是删除诗歌菜单        self.myMenu_add = QMenu(self.ui.treeWidget)        self.myMenu_del = QMenu(self.ui.treeWidget)        # 创建动作对象        self.action_add = QAction(u'添加诗歌')        self.action_del = QAction(u'删除诗歌')        # 将相应动作对象添加到相应的菜单里        self.myMenu_add.addAction(self.action_add)        self.myMenu_del.addAction(self.action_del)        # 添加诗歌菜单项的触发信号和槽        self.action_add.triggered.connect(self.addContent)        # 删除诗歌菜单项的触发信号和槽        self.action_del.triggered.connect(self.delContent)        # 项目右键菜单信号和槽,将contextMenuPolicy属性要设置为:CustomContextMenu,否则右键无反应        self.ui.treeWidget.customContextMenuRequested.connect(self.pop_menu)        # treeWidget组件的双击信号连接到读取文件槽        self.ui.treeWidget.doubleClicked.connect(self.readFile)    # 右键弹出菜单    def pop_menu(self):        # 判读鼠标右键单击位置项是否有父节点,如果没有父节点,说明是诗歌的类别名称,在鼠标位置显示添加诗歌菜单        item = self.ui.treeWidget.currentItem().parent()        if item is None:            self.myMenu_add.move(QCursor().pos())            self.myMenu_add.show()        else:            self.myMenu_del.move(QCursor().pos())  # 如果存在父节点,说明是鼠标右键位置是诗歌名称项,显示删除诗歌菜单            self.myMenu_del.show()    # 添加诗歌菜单项对应的方法    def addContent(self):        # 利用输入对话框获取多行文本,得到新添加诗歌的内容        result = QInputDialog.getMultiLineText(self, '添加诗歌',                                               '请输入添加诗歌信息(第一行是诗歌名称,第二行是作者名称,以下为诗歌内容):')        # 从新添加的诗歌内容字符串开始位置搜索第一个换行符并获取索引,即诗歌名称后面的换行符的位置        index1 = result[0].find('\n')        # 从第一个换行符后面开始搜索第二个换行符并获取索引,即作者名称后面的换行符的位置        index2 = result[0].find('\n', index1 + 1)        # 在诗歌内容中截取出诗歌名称        name = result[0][:index1]        # 在诗歌内容中截取出作者名称        author = result[0][index1 + 1:index2 + 1]        if name == '':            return        # 如果以诗歌名称为文件名的txt文件不存在,则将新添加内容保存到文件        file = name + '.txt'        if not os.path.exists(file):            with open(file, 'w', encoding='utf-8') as w_file:                w_file.write(result[0])        # 获取当前项目,即当前节点        root = self.ui.treeWidget.currentItem()        child = QTreeWidgetItem(root)  # 创建一个QTreeWidgetItem实例对象        child.setText(0, name.strip())  # 设置新建项目的0列文本为诗歌名称        child.setText(1, author.strip())  # 设置新建项目的1列文本为作者名称        child.setSizeHint(1, QSize(0, 35))  # 设置行高        child.setTextAlignment(0, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)  # 设置0列水平居左,垂直居中        child.setTextAlignment(1, Qt.AlignmentFlag.AlignCenter)  # 设置1列的对齐方式为两个方向都居中        self.ui.treeWidget.addTopLevelItem(child)  # 将项目添加到树的节点上    # 删除诗歌菜单项对应的方法    def delContent(self):        item=self.ui.treeWidget.currentItem()  # 获取当前项目        parent=item.parent()  # 获取父节点        parent.removeChild(item)  # 移除父节点上的子节点    # 读取文件    def readFile(self):        # 获取当前项目的父节点        item = self.ui.treeWidget.currentItem().parent()        # 获取当前项目的文本内容        item_name = self.ui.treeWidget.currentItem().text(0)        # 当前项目的父节点不是None,则说明该项目是子节点,读取该项目名称对应的文本文件        if item is not None:            file = f'{item_name}.txt'            if os.path.exists(file):                with open(file, 'r', encoding='utf-8') as r_file:                    content = r_file.read()            else:                QMessageBox.information(self, '提示', f'《{item_name}》暂时未被收录!')            self.ui.textBrowser.setText(content)if __name__ == '__main__':    app = QApplication([])    window = MainWindow()    window.show()    app.exec()
二、Table Widget

表格组件,最适合用来展现数据,尤其是配合数据库使用。

(一)可视化创建表格

在Qt设计大师里,创建和设置表格组件的过程和方法与树形结构基本相同,详细情况可以参照上面的Tree Widget的内容,这里只做简单介绍。

1、设置水平标题栏

双击窗体中的Table Widget组件打开编辑窗口,第一个标签页——Columns(编辑列),就是用来设置水平标题的。点击+按钮添加列并设置标题文本信息,所有列标题都设置完毕后点击OK按钮,这样水平标题栏就设置完毕了。水平标题栏的列数决定了表格有多少列,标题栏高度、背景颜色和字体颜色等,统一用样式表来设置。水平标题蓝设置如图所示:

2、设置垂直标题栏

编辑表格组件窗口中的第二个标签页就是用来设置垂直标题的,也就是行标题。如果实际项目中的表格没有行标题,那么就将垂直标题栏设置成数字序列,可以在Table Widget组件属性中隐藏垂直标题栏(水平标题栏也可以隐藏,但是通常都是显示水平标题栏的)。垂直标题栏的行数决定了表格有多少行,行标题的高度随行高的设置而改变。垂直标题栏设置如图所示:

3、设置表格项目

编辑表格组件窗口中的第三个标签页就是用来设置表格项目的,也就是实际的显示内容。表格项目的设置非常简单,首先在表格项里单击或者双击鼠标左键,然后直接录入文本内容就可以了。项目的文字属性统一在Table Widget组件的字体属性中设置,行高也在组件属性中设置,每列的列宽需要单独设置时,需要在代码中完成。

4、Table Widget组件的重要属性

contextMenuPolicy:上下文菜单策略,在代码中需要自定义弹出菜单时,需要将这个属性设置为customContextMenu(定制上下文菜单),否则在Tree Widget组件上右键没有反应editTriggers:编辑触发器,可以设置成不允许编辑表格项目,那么在运行时就不能对表格项目进行更改。也可以设置成单击、双击和编辑键(F2)等方式进入编辑状态,这时可以对表格项目进行更改verticalHeaderDefaultSectionSize:垂直标题栏默认分类尺寸,就是设置行高horizontalHeaderCascadingSectionResizes:水平标题栏分类部分重新调整大小的方式,为真时,拖动前一列标题栏调整宽度时,后边相邻标题栏宽度会随之相应改变,即交互调整;为假时,后面相邻标题栏宽度保持固定不变horizontalHeaderHighLightSections:高亮显示水平标题栏分类,即点击某个项目后,水平标题栏的对应列高亮显示。如果选择行为设置成选择行模式,则点击一行后水平标题栏整体都变成高亮显示,有时会影响表格整体效果,可以关闭这个属性

5、设置样式表

表格的基本属性设置完毕后,如果对表格样式不满意,就需要使用样式表来调整了。使用样式表,还是统一的原则——就是统一在窗口对象的样式表里设置。以下是设置水平标题蓝的样式表,可以根据自己的需要继续扩充内容。样式表内容如下:

QHeaderView::section{color: rgb(182, 0, 0);background-color:rgb(225,225,225);height:30px;}

6、最终效果

(二)使用代码动态构建表格

下面我们完全使用代码,来构建一个表格。这段代码里几乎涵盖了Table Widget组件的所有方法,代码如下:

from PySide6.QtWidgets import QApplication, QMainWindow, QTableWidget, QAbstractItemView, QTableWidgetItemfrom PySide6.QtGui import Qtclass MainWindow(QMainWindow):    def __init__(self):        super(MainWindow, self).__init__()        # 创建QTableWidget实例对象        self.myTable = QTableWidget(self)        # 调用表格初始化方法        self.table_init()    # 表格初始化方法    def table_init(self):        self.myTable.move(15, 10)  # 设置表格位置        self.myTable.resize(567, 376)  # 设置表格尺寸        self.myTable.setColumnCount(7)  # 设置表格列数        # 设置每列列宽        self.myTable.setColumnWidth(0, 80)        self.myTable.setColumnWidth(1, 60)        self.myTable.setColumnWidth(2, 60)        self.myTable.setColumnWidth(3, 60)        self.myTable.setColumnWidth(4, 80)        self.myTable.setColumnWidth(5, 70)        self.myTable.setColumnWidth(6, 90)        # 设置最后一列充满表宽度        self.myTable.horizontalHeader().setStretchLastSection(True)        # 设置水平表头        header = ['姓名', '性别', '年龄', '民族', '籍贯', '学历', '职称']        self.myTable.setHorizontalHeaderLabels(header)        # 设置垂直表头不可见        self.myTable.verticalHeader().setVisible(False)        # 设置样式表        style = 'QHeaderView::section{color: rgb(182, 0, 0);background-color:rgb(225,225,225);height:30px;}'        self.setStyleSheet(style)        # 设置隔行区分颜色显示        self.myTable.setAlternatingRowColors(True)        # 设置垂直标题栏每行的尺寸,就是行高        self.myTable.verticalHeader().setDefaultSectionSize(30)        # 设置水平标题栏调整列宽的方式,为真,拖动调整一列的列宽时,后边相邻的一列的列宽随之相应变化        self.myTable.horizontalHeader().setCascadingSectionResizes(True)        # 关闭点击表项对应水平标题栏高亮显示选项        self.myTable.horizontalHeader().setHighlightSections(False)        # 设置选择方式,扩展选择        self.myTable.setSelectionMode(QAbstractItemView.ExtendedSelection)        # 设置选择行为,选择行        self.myTable.setSelectionBehavior(QAbstractItemView.SelectRows)        # 设置编辑触发器,双击或按编辑键时编辑表项        self.myTable.setEditTriggers(            QAbstractItemView.EditTrigger.DoubleClicked | QAbstractItemView.EditTrigger.EditKeyPressed)        # 设置表格项不可以拖放        self.myTable.setDragDropMode(QAbstractItemView.NoDragDrop)        # 调用添加表格项数据方法        self.add_data()    # 添加表格项数据方法    def add_data(self):        # 要显示的数据        data = [('张三', '男', '32', '汉', '山东', '大专', '助理工程师'),                ('李四', '男', '35', '汉', '吉林', '本科', '工程师'),                ('王五', '男', '42', '汉', '河北', '大专', '助理工程师'),                ('赵六', '男', '38', '汉', '辽宁', '本科', '助理工程师'),                ('钱七', '男', '32', '汉', '山西', '本科', '助理工程师'),                ('马八', '男', '30', '汉', '山东', '大专', '工程师'),                ('周九', '男', '37', '汉', '山东', '大专', '助理工程师'),                ('李六', '男', '45', '汉', '河北', '大专', '工程师'),                ('张九', '男', '29', '汉', '山东', '本科', '助理工程师')]        # 设置表格行数为11        self.myTable.setRowCount(11)        # 循环添加数据        for i in range(len(data)):            for j in range(len(data[i])):                # 在坐标位置添加QTableWidgetItem类数据                self.myTable.setItem(i, j, QTableWidgetItem(data[i][j]))                # 设置单元格对齐方式,需要使用Qt类的AlignmentFlag属性                self.myTable.item(i,j).setTextAlignment(Qt.AlignmentFlag.AlignCenter)if __name__ == '__main__':    # 创建Qt应用对象,加一个空列表参数    app = QApplication([])    # 创建主窗口对象    window = MainWindow()    # 修改窗口尺寸为600*400像素    window.resize(600, 400)    # 窗口左上角移动到屏幕上横向500像素纵向210像素处    window.move(500, 210)    # 设置窗口标题    window.setWindowTitle('使用代码动态创建表格')    # 显示窗口    window.show()    # 循环显示,直到点击窗口关闭按钮    app.exec()

运行效果如下:

标签: #python37界面