PyQt5 系统化学习: 表格与树

17 表格与树

《PyQt5快速开发与实战》学习笔记。

表格与树解决的问题是如何在一个控件中有规律地呈现更多的数据。PyQt5 提供了两种控件类用于解决该问题,其中一种是表格结构的控件类;另一种是树形结构的控件类。

17.1 QTableView

在通常情况下,一个应用需要和一批数据(比如数组、列表)进行交互,然后以表格的形式输出这些信息,这时就要用到 QTableView 类了。在 QtableView 中可以使用自定义的数据模型来显示内容,通过 setModel 来绑定数据源。

QTableWidget 继承自 QTableView,主要区别是 QTableView 可以使用自定义的数据模型来显示内容(先要通过 setModel 来绑定数据源),而 QTableWidget 只能使用标准的数据模型,并且其单元格数据是通过QTableWidgetItem 对象来实现的。通常使用 QTableWidget 就能够满足我们的要求。

QTableView 控件可以绑定一个模型数据用来更新控件上的内容,可用的模式如表所示。

PyQt5 系统化学习: 表格与树_第1张图片
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys


class Table(QWidget):
    def __init__(self, arg=None):
        super(Table, self).__init__(arg)
        self.setWindowTitle("QTableView表格视图控件的例子")
        self.resize(500, 300)
        self.model = QStandardItemModel(4, 4)
        self.model.setHorizontalHeaderLabels(['标题1', '标题2', '标题3', '标题4'])

        for row in range(4):
            for column in range(4):
                item = QStandardItem("row %s, column %s" % (row, column))
                self.model.setItem(row, column, item)

        self.tableView = QTableView()
        self.tableView.setModel(self.model)
        #下面代码让表格100填满窗口
        #self.tableView.horizontalHeader().setStretchLastSection(True)
        #self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        dlgLayout = QVBoxLayout()
        dlgLayout.addWidget(self.tableView)
        self.setLayout(dlgLayout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    table = Table()
    table.show()
    sys.exit(app.exec_())

效果:

PyQt5 系统化学习: 表格与树_第2张图片

17.2 QListView

QListView 类用于展示数据,它的子类是 QListWidget。QListView 是基于模型(Model)的,需要程序来建立模型,然后再保存数据。

QListWidget 是一个升级版本的 QListView,它已经建立了一个数据存储模型(QListWidgetItem),直接调用addItem()函数,就可以添加条目(Item)。

PyQt5 系统化学习: 表格与树_第3张图片
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QListView, QMessageBox
from PyQt5.QtCore import QStringListModel
import sys


class ListViewDemo(QWidget):
    def __init__(self, parent=None):
        super(ListViewDemo, self).__init__(parent)
        self.setWindowTitle("QListView 例子")
        self.resize(300, 270)
        layout = QVBoxLayout()

        listView = QListView()
        slm = QStringListModel()
        self.qList = ['Item 1', 'Item 2', 'Item 3', 'Item 4']
        slm.setStringList(self.qList)
        listView.setModel(slm)
        listView.clicked.connect(self.clicked)
        layout.addWidget(listView)
        self.setLayout(layout)

    def clicked(self, qModelIndex):
        QMessageBox.information(self, "QListView", "你选择了: " +
                                self.qList[qModelIndex.row()])


if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = ListViewDemo()
    win.show()
    sys.exit(app.exec_())

效果:

PyQt5 系统化学习: 表格与树_第4张图片

17.3 QListWidget

QListWidet 类是一个基于条目的接口,用于从列表中添加或删除条目。列表中的每个条目都是一个QListWidgetItem 对象。QListWidget 可以设置为多重选择。

PyQt5 系统化学习: 表格与树_第5张图片
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class ListWidget(QListWidget):
    def clicked(self, item):
        QMessageBox.information(self, "ListWidget", "你选择了: "+item.text())


if __name__ == '__main__':
    app = QApplication(sys.argv)
    listWidget = ListWidget()
    listWidget.resize(300, 120)
    listWidget.addItem("Item 1")
    listWidget.addItem("Item 2")
    listWidget.addItem("Item 3")
    listWidget.addItem("Item 4")
    listWidget.setWindowTitle('QListwidget 例子')
    listWidget.itemClicked.connect(listWidget.clicked)
    listWidget.show()
    sys.exit(app.exec_())

更复杂的例子:

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


class StackedExample(QWidget):
    def __init__(self):
        super(StackedExample, self).__init__()
        self.setGeometry(300, 50, 10, 10)
        self.setWindowTitle('StackedWidget 例子')

        self.leftlist = QListWidget()
        self.leftlist.insertItem(0, '联系方式')
        self.leftlist.insertItem(1, '个人信息')
        self.leftlist.insertItem(2, '教育程度')
        self.stack1 = QWidget()
        self.stack2 = QWidget()
        self.stack3 = QWidget()
        self.stack1UI()
        self.stack2UI()
        self.stack3UI()
        self.Stack = QStackedWidget(self)
        self.Stack.addWidget(self.stack1)
        self.Stack.addWidget(self.stack2)
        self.Stack.addWidget(self.stack3)
        hbox = QHBoxLayout(self)
        hbox.addWidget(self.leftlist)
        hbox.addWidget(self.Stack)
        self.setLayout(hbox)
        self.leftlist.currentRowChanged.connect(self.display)

    def stack1UI(self):
        layout = QFormLayout()
        layout.addRow("姓名", QLineEdit())
        layout.addRow("地址", QLineEdit())
        self.stack1.setLayout(layout)

    def stack2UI(self):
        layout = QFormLayout()
        sex = QHBoxLayout()
        sex.addWidget(QRadioButton("男"))
        sex.addWidget(QRadioButton("女"))
        layout.addRow(QLabel("性别"), sex)
        layout.addRow("生日", QLineEdit())
        self.stack2.setLayout(layout)

    def stack3UI(self):
        layout = QHBoxLayout()
        layout.addWidget(QLabel("科目"))
        layout.addWidget(QCheckBox("物理"))
        layout.addWidget(QCheckBox("高数"))
        self.stack3.setLayout(layout)

    def display(self, i):
        self.Stack.setCurrentIndex(i)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = StackedExample()
    demo.show()
    sys.exit(app.exec_())
PyQt5 系统化学习: 表格与树_第6张图片

17.4 QTableWidget

QTableWidget 是 Qt 程序中常用的显示数据表格的空间,类似于 C# 中的 DataGrid。QTableWidget 是QTableView 的子类,它使用标准的数据模型,并且其单元格数据是通过 QTableWidgetItem 对象来实现的。使用 QTableWidget 时就需要 QTableWidgetItem,用来表示表格中的一个单元格,整个表格就是用各单元格构建起来的。

QTableWidget类中的常用方法如表:

PyQt5 系统化学习: 表格与树_第7张图片
PyQt5 系统化学习: 表格与树_第8张图片

如果要设置水平和垂直对齐方式,比如在表格空间内上下、左右居中对齐,那么只要使用 Qt.AlignHCenter 和Qt.AlignVCenter 即可。

17.4.1 基本用法

本例主要介绍基本表格的用法。在表格控件中显示的数据是可编辑的。在 QTableWidget 表格中具体单元格就是 QTableWidgetItem 类。其完整代码如下:

import sys
from PyQt5.QtWidgets import (
    QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem, QAbstractItemView)


class Table(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QTableWidget 例子")
        self.resize(430, 230)
        conLayout = QHBoxLayout()
        tableWidget = QTableWidget()
        tableWidget.setRowCount(4)
        tableWidget.setColumnCount(3)
        conLayout.addWidget(tableWidget)

        tableWidget.setHorizontalHeaderLabels(['姓名', '性别', '体重(kg)'])

        newItem = QTableWidgetItem("张三")
        tableWidget.setItem(0, 0, newItem)

        newItem = QTableWidgetItem("男")
        tableWidget.setItem(0, 1, newItem)

        newItem = QTableWidgetItem("160")
        tableWidget.setItem(0, 2, newItem)

        # 将表格变为禁止编辑
        #tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)

        # 设置表格为整行选择
        #tableWidget.setSelectionBehavior( QAbstractItemView.SelectRows)

        # 将行和列的大小设为与内容相匹配
        #tableWidget.resizeColumnsToContents()
        #tableWidget.resizeRowsToContents()

        #表格表头的显示与隐藏
        #tableWidget.verticalHeader().setVisible(False)
        #tableWidget.horizontalHeader().setVisible(False)

        # 不显示表格单元格的分割线
        #tableWidget.setShowGrid(False)
        # 不显示垂直表头
        tableWidget.verticalHeader().setVisible(False)

        self.setLayout(conLayout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = Table()
    example.show()
    sys.exit(app.exec_())

效果:

PyQt5 系统化学习: 表格与树_第9张图片
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
          
class TabDemo(QTabWidget):
    def __init__(self, parent=None):
        super(TabDemo, self).__init__(parent)   
        self.tab1 = QWidget()
        self.tab2 = QWidget()
        self.tab3 = QWidget()
        self.addTab(self.tab1,"Tab 1")
        self.addTab(self.tab2,"Tab 2")
        self.addTab(self.tab3,"Tab 3")
        self.tab1UI()
        self.tab2UI()
        self.tab3UI()
        self.setWindowTitle("Tab 例子")
        
    def tab1UI(self):
        layout = QFormLayout()
        layout.addRow("姓名",QLineEdit())
        layout.addRow("地址",QLineEdit())
        self.setTabText(0,"联系方式")
        self.tab1.setLayout(layout)
        
    def tab2UI(self):
        layout = QFormLayout()
        sex = QHBoxLayout()
        sex.addWidget(QRadioButton("男"))    
        sex.addWidget(QRadioButton("女"))
        layout.addRow(QLabel("性别"),sex)
        layout.addRow("生日",QLineEdit())
        self.setTabText(1,"个人详细信息")
        self.tab2.setLayout(layout)
        
    def tab3UI(self):
        layout=QHBoxLayout()
        layout.addWidget(QLabel("科目"))
        layout.addWidget(QCheckBox("物理"))
        layout.addWidget(QCheckBox("高数"))
        self.setTabText(2,"教育程度")
        self.tab3.setLayout(layout)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = TabDemo()
    demo.show()
    sys.exit(app.exec_())

效果:

PyQt5 系统化学习: 表格与树_第10张图片

17.4.2 在表格中快速定位到指定行

当 tableWidget 表格的行数很多时,可以通过输入行号进行直接定位并显示,比如输入 10,就直接显示第 10行:

import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
from PyQt5.QtGui import QColor, QBrush


class Table(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QTableWidget 例子")
        self.resize(600, 800)
        conLayout = QHBoxLayout()
        tableWidget = QTableWidget()
        tableWidget.setRowCount(30)
        tableWidget.setColumnCount(4)
        conLayout.addWidget(tableWidget)

        for i in range(30):
            for j in range(4):
                itemContent = '(%d,%d)' % (i, j)
                tableWidget.setItem(i, j, QTableWidgetItem(itemContent))
        self.setLayout(conLayout)

        #遍历表查找对应的item
        text = "(10,1)"
        items = tableWidget.findItems(text, QtCore.Qt.MatchExactly)
        item = items[0]
        # 选中单元格
        #item.setSelected( True)
        # 设置单元格的背景颜色为红色
        item.setForeground(QBrush(QColor(255, 0, 0)))

        row = item.row()
        #滚轮定位过去,快速定位到第17行
        tableWidget.verticalScrollBar().setSliderPosition(row)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = Table()
    example.show()
    sys.exit(app.exec_())

效果:

PyQt5 系统化学习: 表格与树_第11张图片

17.4.3 设置单元格文本颜色

将表格第一行中三个单元格的文本颜色设置为红色:

import sys
from PyQt5.QtWidgets import (
    QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem)
from PyQt5.QtGui import QBrush,  QColor,  QFont


class Table(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QTableWidget 例子")
        self.resize(430, 230)
        conLayout = QHBoxLayout()
        tableWidget = QTableWidget()
        tableWidget.setRowCount(4)
        tableWidget.setColumnCount(3)
        conLayout.addWidget(tableWidget)

        tableWidget.setHorizontalHeaderLabels(['姓名', '性别', '体重(kg)'])

        newItem = QTableWidgetItem("张三")
        newItem.setForeground(QBrush(QColor(255, 0, 0)))
        tableWidget.setItem(0, 0, newItem)

        newItem = QTableWidgetItem("男")
        newItem.setForeground(QBrush(QColor(255, 0, 0)))
        tableWidget.setItem(0, 1, newItem)

        newItem = QTableWidgetItem("160")
        newItem.setForeground(QBrush(QColor(255, 0, 0)))
        tableWidget.setItem(0, 2, newItem)

        self.setLayout(conLayout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = Table()
    example.show()
    sys.exit(app.exec_())

效果:

PyQt5 系统化学习: 表格与树_第12张图片

17.4.4 将字体加粗

import sys
from PyQt5.QtWidgets import (
    QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem)
from PyQt5.QtGui import QBrush,  QColor,  QFont


class Table(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QTableWidget 例子")
        self.resize(430, 230)
        conLayout = QHBoxLayout()
        tableWidget = QTableWidget()
        tableWidget.setRowCount(4)
        tableWidget.setColumnCount(3)
        conLayout.addWidget(tableWidget)

        tableWidget.setHorizontalHeaderLabels(['姓名', '性别', '体重(kg)'])

        newItem = QTableWidgetItem("张三")
        newItem.setFont(QFont("Times", 12, QFont.Black))
        tableWidget.setItem(0, 0, newItem)

        newItem = QTableWidgetItem("男")
        newItem.setFont(QFont("Times", 12, QFont.Black))
        tableWidget.setItem(0, 1, newItem)

        newItem = QTableWidgetItem("160")
        newItem.setFont(QFont("Times", 12, QFont.Black))
        tableWidget.setItem(0, 2, newItem)

        self.setLayout(conLayout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = Table()
    example.show()
    sys.exit(app.exec_())

效果:

PyQt5 系统化学习: 表格与树_第13张图片

17.4.5 设置单元格的排序方式

查看 Qt 的开发文档(https://doc.qt.io/qt-5/qt.xhtml#details),可以看到使用 Qt.DescendingOrder 表示在单元格内降序排列,使用 Qt.AscendingOrder 表示在单元格内升序排列。但是需要使用以下语句从PyQt5.QtCore 模块导入 Qt 类。

from PyQt5.QtCore import Qt 

演示在表格中按照体重进行降序排列显示,其代码如下:

# Qt.DescendingOrder 降序 
# Qt.AscendingOrder 升序 
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import (
    QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem)
from PyQt5.QtCore import Qt


class Table(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QTableWidget 例子")
        self.resize(430, 230)
        conLayout = QHBoxLayout()
        tableWidget = QTableWidget()
        tableWidget.setRowCount(4)
        tableWidget.setColumnCount(3)
        conLayout.addWidget(tableWidget)

        tableWidget.setHorizontalHeaderLabels(['姓名', '性别', '体重(kg)'])

        newItem = QTableWidgetItem("张三")
        tableWidget.setItem(0, 0, newItem)

        newItem = QTableWidgetItem("男")
        tableWidget.setItem(0, 1, newItem)

        newItem = QTableWidgetItem("160")
        tableWidget.setItem(0, 2, newItem)

        newItem = QTableWidgetItem("李四")
        tableWidget.setItem(1, 0, newItem)

        newItem = QTableWidgetItem("女")
        tableWidget.setItem(1, 1, newItem)

        newItem = QTableWidgetItem("155")
        tableWidget.setItem(1, 2, newItem)

        newItem = QTableWidgetItem("王五")
        tableWidget.setItem(2, 0, newItem)

        newItem = QTableWidgetItem("男")
        tableWidget.setItem(2, 1, newItem)

        newItem = QTableWidgetItem("170")
        tableWidget.setItem(2, 2, newItem)

        # Qt.DescendingOrder 降序
        # Qt.AscendingOrder 升序
        tableWidget.sortItems(2,  Qt.DescendingOrder)

        self.setLayout(conLayout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = Table()
    example.show()
    sys.exit(app.exec_())

效果:

PyQt5 系统化学习: 表格与树_第14张图片

17.4.6 设置单元格文本的对齐方式

使用 QTableWidgetItem.setTextAlignment(int) 函数设置单元格文本的对齐方式,该函数的参数为对齐方式。

查看Qt的开发文档(https://doc.qt.io/qt-5/qt.xhtml#details),可以看到水平和垂直方向上的对齐方式。Qt 的文本对齐方式同样也可以应用在 PyQt5 中。

演示第一行第一列的单元格内容右对齐并与底部对齐。其代码如下:

import sys
from PyQt5.QtWidgets import (
    QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem)
from PyQt5.QtCore import Qt


class Table(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QTableWidget 例子")
        self.resize(430, 230)
        conLayout = QHBoxLayout()
        tableWidget = QTableWidget()
        tableWidget.setRowCount(4)
        tableWidget.setColumnCount(3)
        conLayout.addWidget(tableWidget)

        tableWidget.setHorizontalHeaderLabels(['姓名', '性别', '体重(kg)'])
        newItem = QTableWidgetItem("张三")

        newItem.setTextAlignment(Qt.AlignRight | Qt.AlignBottom)
        tableWidget.setItem(0, 0, newItem)

        newItem = QTableWidgetItem("男")
        tableWidget.setItem(0, 1, newItem)

        newItem = QTableWidgetItem("160")
        tableWidget.setItem(0, 2, newItem)

        self.setLayout(conLayout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = Table()
    example.show()
    sys.exit(app.exec_())

效果:

PyQt5 系统化学习: 表格与树_第15张图片

17.4.7 合并单元格效果的实现

比如,将表格中第一行第一列的单元格,更改为占据 3 行 1 列。代码如下:

# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import (
    QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem)


class Table(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QTableWidget 例子")
        self.resize(430, 230)
        conLayout = QHBoxLayout()
        tableWidget = QTableWidget()
        tableWidget.setRowCount(4)
        tableWidget.setColumnCount(3)
        conLayout.addWidget(tableWidget)

        tableWidget.setHorizontalHeaderLabels(['姓名', '性别', '体重(kg)'])
        tableWidget.setSpan(0, 0, 3, 1)

        newItem = QTableWidgetItem("张三")
        tableWidget.setItem(0, 0, newItem)

        newItem = QTableWidgetItem("男")
        tableWidget.setItem(0, 1, newItem)

        newItem = QTableWidgetItem("160")
        tableWidget.setItem(0, 2, newItem)

        self.setLayout(conLayout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = Table()
    example.show()
    sys.exit(app.exec_())

效果:

PyQt5 系统化学习: 表格与树_第16张图片

17.4.8 设置单元格的大小

演示将第一列的单元格宽度设置为 150,将第一行的单元格高度设置为120:

import sys
from PyQt5.QtWidgets import (
    QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem)


class Table(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QTableWidget 例子")
        self.resize(530, 300)
        conLayout = QHBoxLayout()
        tableWidget = QTableWidget()
        tableWidget.setRowCount(4)
        tableWidget.setColumnCount(3)
        conLayout.addWidget(tableWidget)

        tableWidget.setHorizontalHeaderLabels(['姓名', '性别', '体重(kg)'])

        newItem = QTableWidgetItem("张三")
        tableWidget.setItem(0, 0, newItem)

        #将第1列的单元格,设置成150宽度
        tableWidget.setColumnWidth(0, 150)
        #将第1行的单元格,设置成120的高度
        tableWidget.setRowHeight(0, 120)

        newItem = QTableWidgetItem("男")
        tableWidget.setItem(0, 1, newItem)

        newItem = QTableWidgetItem("160")
        tableWidget.setItem(0, 2, newItem)

        self.setLayout(conLayout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = Table()
    example.show()
    sys.exit(app.exec_())

效果如下:

PyQt5 系统化学习: 表格与树_第17张图片

17.4.9 在表格中不显示分割线

QTableWidget 类的 setShowGrid()函数是从 QTableView 类继承的,用来设置是否显示表格的分割线,默认显示分割线。使用以下代码,则不显示分割线。

PyQt5 系统化学习: 表格与树_第18张图片

17.4.10 为单元格添加图片

还可以在单元格内添加图片,并显示图片的描述信息。

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


class Table(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QTableWidget 例子")
        self.resize(500, 300)
        conLayout = QHBoxLayout()
        self.tableWidget = QTableWidget()
        self.tableWidget.setRowCount(5)
        self.tableWidget.setColumnCount(4)
        conLayout.addWidget(self.tableWidget)

        self.tableWidget.setHorizontalHeaderLabels(['姓名', '性别', '体重', '显示图片'])
        self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

        newItem = QTableWidgetItem("张三")
        self.tableWidget.setItem(0, 0, newItem)

        newItem = QTableWidgetItem("男")
        self.tableWidget.setItem(0, 1, newItem)

        newItem = QTableWidgetItem("160")
        self.tableWidget.setItem(0, 2, newItem)

        newItem = QTableWidgetItem(QIcon("./images/bao1.png"), "背包")
        self.tableWidget.setItem(0, 3, newItem)
        self.setLayout(conLayout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = Table()
    example.show()
    sys.exit(app.exec_())

效果如下:

PyQt5 系统化学习: 表格与树_第19张图片

17.4.11 改变单元格中显示的图片大小

使用 QTableWidget 默认处理 QTableWidgetItem 对象,在每个单元格中放置图片。

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


class Table(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QTableWidget 例子")
        self.resize(1000, 900)
        conLayout = QHBoxLayout()

        table = QTableWidget()
        table.setColumnCount(3)
        table.setRowCount(5)

        table.setHorizontalHeaderLabels(['图片1', '图片2', '图片3'])

        table.setEditTriggers(QAbstractItemView.NoEditTriggers)

        table.setIconSize(QSize(300, 200))

        for i in range(3):   # 让列宽和图片相同
           table.setColumnWidth(i, 300)
        for i in range(5):   # 让行高和图片相同
            table.setRowHeight(i, 200)

        for k in range(15):  # 27 examples of DDA
            i = k/3
            j = k % 3
            item = QTableWidgetItem()
            item.setFlags(Qt.ItemIsEnabled)  # 用户点击时表格时,图片被选中
            icon = QIcon(r'.\images\bao%d.png' % k)
            item.setIcon(QIcon(icon))

            print('e/icons/%d.png i=%d  j=%d' % (k, i, j))
            table.setItem(i, j, item)

        conLayout.addWidget(table)
        self.setLayout(conLayout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = Table()
    example.show()
    sys.exit(app.exec_())

效果如下:

PyQt5 系统化学习: 表格与树_第20张图片

17.4.12 获得单元格的内容

通过实现 itemClicked (QTableWidgetItem *) 信号的 slot 函数,可以获得所点击的单元格的引用,进而获得其中的内容。以下代码将 itemClicked 信号与 getItem() 函数进行绑定:

tableWidget.itemClicked.connect( self.handleItemClick ) 
def getItem(self,item): 
print('you selected=﹥ '+ item.text()) 

17.4.13 支持右键菜单

选中某个单元格后,单击鼠标右键,从弹出的快捷菜单:

import sys
from PyQt5.QtWidgets import (QMenu, QPushButton,  QWidget, QTableWidget,
                             QHBoxLayout, QApplication, QTableWidgetItem, QHeaderView)
from PyQt5.QtCore import QObject, Qt


class Table(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QTableWidget 例子")
        self.resize(500, 300)
        conLayout = QHBoxLayout()
        self.tableWidget = QTableWidget()
        self.tableWidget.setRowCount(5)
        self.tableWidget.setColumnCount(3)
        conLayout.addWidget(self.tableWidget)

        self.tableWidget.setHorizontalHeaderLabels(['姓名', '性别', '体重'])
        self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

        newItem = QTableWidgetItem("张三")
        self.tableWidget.setItem(0, 0, newItem)

        newItem = QTableWidgetItem("男")
        self.tableWidget.setItem(0, 1, newItem)

        newItem = QTableWidgetItem("160")
        self.tableWidget.setItem(0, 2, newItem)
        #表格中第二行记录
        newItem = QTableWidgetItem("李四")
        self.tableWidget.setItem(1, 0, newItem)

        newItem = QTableWidgetItem("女")
        self.tableWidget.setItem(1, 1, newItem)

        newItem = QTableWidgetItem("170")
        self.tableWidget.setItem(1, 2, newItem)

        self.tableWidget.setContextMenuPolicy(Qt.CustomContextMenu)  # 允许右键产生子菜单
        self.tableWidget.customContextMenuRequested.connect(
            self.generateMenu)  # 右键菜单
        self.setLayout(conLayout)

    def generateMenu(self, pos):
        #rint( pos)
        row_num = -1
        for i in self.tableWidget.selectionModel().selection().indexes():
            row_num = i.row()

        if row_num < 2:
            menu = QMenu()
            item1 = menu.addAction(u"选项一")
            item2 = menu.addAction(u"选项二")
            item3 = menu.addAction(u"选项三")
            action = menu.exec_(self.tableWidget.mapToGlobal(pos))
            if action == item1:
                print('您选了选项一,当前行文字内容是:', self.tableWidget.item(row_num, 0).text(
                ), self.tableWidget.item(row_num, 1).text(), self.tableWidget.item(row_num, 2).text())

            elif action == item2:
                print('您选了选项二,当前行文字内容是:', self.tableWidget.item(row_num, 0).text(
                ), self.tableWidget.item(row_num, 1).text(), self.tableWidget.item(row_num, 2).text())

            elif action == item3:
                print('您选了选项三,当前行文字内容是:', self.tableWidget.item(row_num, 0).text(
                ), self.tableWidget.item(row_num, 1).text(), self.tableWidget.item(row_num, 2).text())
            else:
                return


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = Table()
    example.show()
    sys.exit(app.exec_())

效果:

PyQt5 系统化学习: 表格与树_第21张图片

17.5 QTreeView

QTreeWidget类实现了树形结构。

PyQt5 系统化学习: 表格与树_第22张图片

17.5.1 树形结构的实现

树形结构是通过 QTreeWidget 和 QTreeWidgetItem 类实现的,其中 QTreeWidgetItem 类实现了节点的添加。

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon,  QBrush, QColor
from PyQt5.QtCore import Qt


class TreeWidgetDemo(QMainWindow):
    def __init__(self, parent=None):
        super(TreeWidgetDemo, self).__init__(parent)
        self.setWindowTitle('TreeWidget 例子')
        self.tree = QTreeWidget()
        # 设置列数
        self.tree.setColumnCount(2)
        # 设置头的标题
        self.tree.setHeaderLabels(['Key', 'Value'])
        # 设置根节点
        root = QTreeWidgetItem(self.tree)
        root.setText(0, 'root')
        root.setIcon(0, QIcon("./images/root.png"))
        # 设置列宽
        self.tree.setColumnWidth(0, 160)

        ### 设置节点的背景颜色
        #brush_red = QBrush(Qt.red)
        #root.setBackground(0, brush_red)
        #brush_green = QBrush(Qt.green)
        #root.setBackground(1, brush_green)

        # 设置子节点1
        child1 = QTreeWidgetItem(root)
        child1.setText(0, 'child1')
        child1.setText(1, 'ios')
        child1.setIcon(0, QIcon("./images/IOS.png"))
        child1.setCheckState(0, Qt.Checked)

        # 设置子节点2
        child2 = QTreeWidgetItem(root)
        child2.setText(0, 'child2')
        child2.setText(1, '')
        child2.setIcon(0, QIcon("./images/android.png"))

        # 设置子节点3
        child3 = QTreeWidgetItem(child2)
        child3.setText(0, 'child3')
        child3.setText(1, 'android')
        child3.setIcon(0, QIcon("./images/music.png"))

        self.tree.addTopLevelItem(root)
        # 结点全部展开
        self.tree.expandAll()

        self.setCentralWidget(self.tree)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    tree = TreeWidgetDemo()
    tree.show()
    sys.exit(app.exec_())

效果:

PyQt5 系统化学习: 表格与树_第23张图片
PyQt5 系统化学习: 表格与树_第24张图片
PyQt5 系统化学习: 表格与树_第25张图片

17.5.2 给节点添加响应事件

演示当单击树形控件时,触发树形控件节点的响应事件。其完整代码如下:

from PyQt5.QtWidgets import *
import sys


class TreeWidgetDemo(QMainWindow):
    def __init__(self, parent=None):
        super(TreeWidgetDemo, self).__init__(parent)
        self.setWindowTitle('TreeWidget 例子')
        self.tree = QTreeWidget()
        # 设置列数
        self.tree.setColumnCount(2)
        # 设置头的标题
        self.tree.setHeaderLabels(['Key', 'Value'])
        root = QTreeWidgetItem(self.tree)
        root.setText(0, 'root')
        root.setText(1, '0')

        child1 = QTreeWidgetItem(root)
        child1.setText(0, 'child1')
        child1.setText(1, '1')

        child2 = QTreeWidgetItem(root)
        child2.setText(0, 'child2')
        child2.setText(1, '2')

        child3 = QTreeWidgetItem(root)
        child3.setText(0, 'child3')
        child3.setText(1, '3')

        child4 = QTreeWidgetItem(child3)
        child4.setText(0, 'child4')
        child4.setText(1, '4')

        child5 = QTreeWidgetItem(child3)
        child5.setText(0, 'child5')
        child5.setText(1, '5')

        self.tree.addTopLevelItem(root)
        self.tree.clicked.connect(self.onTreeClicked)

        self.setCentralWidget(self.tree)

    def onTreeClicked(self, qmodelindex):
        item = self.tree.currentItem()
        print("key=%s ,value=%s" % (item.text(0), item.text(1)))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    tree = TreeWidgetDemo()
    tree.show()
    sys.exit(app.exec_())

效果如下:

PyQt5 系统化学习: 表格与树_第26张图片

17.5.3 系统定制模式

在上面的例子中,QTreeWidgetItem 类的节点是一个个添加的,这样做有时很不方便,特别是当窗口中产生比较复杂的树形结构时,一般都是通过 QTreeView 类来实现的,而不是 QTreeWidget 类。QTreeView 类与 QTreeWidget 类最大的区别就是,QTreeView 类可以使用操作系统提供的定制模式,比如文件系统盘的树列表。

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

if __name__ == '__main__':
    app = QApplication(sys.argv)
    #Window系统提供的模式
    model = QDirModel()
    #创建一个QtreeView部件
    tree = QTreeView()
    #为部件添加模式
    tree.setModel(model)
    tree.setWindowTitle("QTreeView 例子")
    tree.resize(640, 480)
    tree.show()
    sys.exit(app.exec_())

效果如下:

PyQt5 系统化学习: 表格与树_第27张图片

你可能感兴趣的:(PyQt5 系统化学习: 表格与树)