python pyside2 List View(QListView)

介绍

QListView 是 PySide2 中用于展示列表数据的控件,遵循 MVC(模型-视图-控制器)设计模式,通过模型与数据交互,视图负责展示。常与 QStandardItemModel 配合使用,支持多种显示模式和交互操作。

应用场景

QListView 适合展示简单的列表数据

若需要更复杂的表格结构,可使用 QTableView

若需要树形结构,可使用 QTreeView。

例子

import sys
from PySide2.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
                               QListView, QPushButton, QLineEdit, QLabel, QMessageBox)
from PySide2.QtCore import Qt, QModelIndex
from PySide2.QtGui import QStandardItemModel, QStandardItem, QFont

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

    def initUI(self):
        # 设置窗口基本属性
        self.setWindowTitle("QListView 示例")
        self.setGeometry(300, 300, 500, 400)

        # 创建主布局
        main_layout = QVBoxLayout()

        # 1. 创建列表视图和数据模型
        self.list_view = QListView()

        # 创建标准项模型(用于存储列表数据)
        self.model = QStandardItemModel()
        self.list_view.setModel(self.model)

        # 设置列表视图属性
        self.list_view.setEditTriggers(QListView.NoEditTriggers)  # 禁止直接编辑
        self.list_view.setSelectionMode(QListView.SingleSelection)  # 单选模式
        self.list_view.setAlternatingRowColors(True)  # 交替行颜色
        self.list_view.setFont(QFont("Segoe UI", 9))  # 设置字体

        # 添加示例数据
        self.init_list_data()

        # 2. 创建控制区域
        control_layout = QVBoxLayout()

        # 添加项输入框和按钮
        add_layout = QHBoxLayout()
        self.item_input = QLineEdit()
        self.item_input.setPlaceholderText("输入要添加的项...")
        add_btn = QPushButton("添加")
        add_btn.clicked.connect(self.add_item)
        add_layout.addWidget(self.item_input)
        add_layout.addWidget(add_btn)

        # 操作按钮
        btn_layout = QHBoxLayout()
        self.remove_btn = QPushButton("删除选中项")
        self.remove_btn.clicked.connect(self.remove_selected)
        self.clear_btn = QPushButton("清空列表")
        self.clear_btn.clicked.connect(self.clear_list)
        btn_layout.addWidget(self.remove_btn)
        btn_layout.addWidget(self.clear_btn)

        # 状态显示
        self.status_label = QLabel("选中项: 无")
        self.status_label.setStyleSheet("color: #666;")

        # 将控制组件添加到布局
        control_layout.addLayout(add_layout)
        control_layout.addLayout(btn_layout)
        control_layout.addWidget(self.status_label)

        # 3. 组装主布局
        main_layout.addWidget(QLabel("列表内容:"))
        main_layout.addWidget(self.list_view)
        main_layout.addLayout(control_layout)

        self.setLayout(main_layout)

        # 连接列表选择变化信号
        self.list_view.selectionModel().selectionChanged.connect(self.on_selection_changed)

        self.show()

    def init_list_data(self):
        """初始化列表数据"""
        items = ["项目 1", "项目 2", "项目 3", "项目 4", "项目 5"]
        for item_text in items:
            item = QStandardItem(item_text)
            # 设置项的可选中、可启用
            item.setSelectable(True)
            item.setEnabled(True)
            self.model.appendRow(item)

    def add_item(self):
        """添加新项到列表"""
        text = self.item_input.text().strip()
        if text:
            item = QStandardItem(text)
            self.model.appendRow(item)
            self.item_input.clear()  # 清空输入框
        else:
            QMessageBox.warning(self, "警告", "请输入内容后再添加!")

    def remove_selected(self):
        """删除选中的项"""
        selected_indexes = self.list_view.selectedIndexes()
        if selected_indexes:
            # 从模型中移除选中项
            self.model.removeRow(selected_indexes[0].row())
        else:
            QMessageBox.information(self, "提示", "请先选中要删除的项!")

    def clear_list(self):
        """清空列表"""
        if self.model.rowCount() > 0:
            reply = QMessageBox.question(self, "确认", "确定要清空所有项吗?",
                                        QMessageBox.Yes | QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.model.clear()

    def on_selection_changed(self, selected, deselected):
        """处理选择变化事件"""
        indexes = selected.indexes()
        if indexes:
            # 获取选中项的文本
            selected_text = self.model.data(indexes[0])
            self.status_label.setText(f"选中项: {selected_text}")
        else:
            self.status_label.setText("选中项: 无")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = ListViewDemo()
    sys.exit(app.exec_())

核心用法解析

  1. 基本组成

    • QListView:负责列表的显示和交互
    • QStandardItemModel:管理列表数据(模型)
    • QStandardItem:单个列表项的数据载体
  2. 常用属性设置

    • 编辑模式:setEditTriggers() 控制是否允许直接编辑项(NoEditTriggers 为禁止编辑)
    • 选择模式:setSelectionMode() 支持单选(SingleSelection)、多选(MultiSelection)等
    • 外观设置:setAlternatingRowColors(True) 启用交替行颜色,提升可读性
  3. 数据操作

    • 添加项:model.appendRow(item)
    • 删除项:model.removeRow(row_index)
    • 清空列表:model.clear()
    • 获取项数据:model.data(index)
  4. 事件处理

    • 通过 selectionModel().selectionChanged 信号捕获选择变化
    • 可获取选中项的索引(selectedIndexes())和数据
  5. 进阶用法

    • 可设置为图标模式:list_view.setViewMode(QListView.IconMode)
    • 支持拖放功能:list_view.setDragEnabled(True) 和 list_view.setAcceptDrops(True)
    • 可自定义项的外观(通过 QItemDelegate)

高级用法- 隐藏数据项

在 QListView 中添加不需要显示但需要存储的数据(如ID、额外属性等),可以通过 QStandardItem 的 setData() 方法 来存储隐藏数据,这些数据不会在列表中显示,但可以在需要时通过 data() 方法获取。

适用场景:

  • 存储列表项对应的ID(如数据库主键)
  • 保存额外的业务数据(如用户详情、文件路径等)
  • 记录项目状态(如是否已选中、优先级等)

这种方式既保持了列表的简洁显示,又能在需要时获取完整数据,是 QListView 处理复杂数据的常用技巧。

核心实现原理:

  1. 自定义数据角色
    Qt 提供了 Qt.UserRole 及以上的角色值用于存储自定义数据,例如:

    self.HIDDEN_DATA_ROLE = Qt.UserRole + 1  # 定义一个唯一的角色标识
    

    不同角色可以存储不同类型的隐藏数据(如整数、字典、对象等)。

  2. 存储隐藏数据
    使用 QStandardItem 的 setData() 方法将数据与自定义角色绑定:

    item.setData(隐藏数据, 自定义角色)  # 隐藏数据不会显示在列表中
    

    支持存储多种类型数据(字符串、数字、字典、自定义对象等)。

  3. 获取隐藏数据
    通过 model.data(index, 自定义角色) 方法提取隐藏数据:

    hidden_data = self.model.data(index, self.HIDDEN_DATA_ROLE)
    

例子

import sys
from PySide2.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
                               QListView, QPushButton, QLabel, QMessageBox)
from PySide2.QtCore import Qt, QModelIndex
from PySide2.QtGui import QStandardItemModel, QStandardItem

class ListViewHiddenData(QWidget):
    def __init__(self):
        super().__init__()
        # 定义一个角色用于存储隐藏数据(Qt提供的UserRole可自定义数据)
        self.HIDDEN_DATA_ROLE = Qt.UserRole + 1  # 自定义角色值
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QListView 隐藏数据示例")
        self.setGeometry(300, 300, 500, 400)

        # 创建列表视图和模型
        self.list_view = QListView()
        self.model = QStandardItemModel()
        self.list_view.setModel(self.model)

        # 添加带隐藏数据的示例项
        self.add_items_with_hidden_data()

        # 按钮:查看选中项的隐藏数据
        self.show_data_btn = QPushButton("查看选中项的隐藏数据")
        self.show_data_btn.clicked.connect(self.show_hidden_data)

        # 布局
        layout = QVBoxLayout()
        layout.addWidget(QLabel("列表项(显示名称):"))
        layout.addWidget(self.list_view)
        layout.addWidget(self.show_data_btn)

        self.setLayout(layout)
        self.show()

    def add_items_with_hidden_data(self):
        """添加带隐藏数据的项目"""
        # 示例数据:显示名称 + 隐藏数据(如ID、详情等)
        items = [
            {"display": "用户A", "hidden": {"id": 101, "email": "a@example.com"}},
            {"display": "用户B", "hidden": {"id": 102, "email": "b@example.com"}},
            {"display": "用户C", "hidden": {"id": 103, "email": "c@example.com"}}
        ]

        for item_data in items:
            # 创建显示项(仅显示名称)
            item = QStandardItem(item_data["display"])

            # 存储隐藏数据(使用自定义角色)
            item.setData(item_data["hidden"], self.HIDDEN_DATA_ROLE)

            # 添加到模型
            self.model.appendRow(item)

    def show_hidden_data(self):
        """获取并显示选中项的隐藏数据"""
        selected_indexes = self.list_view.selectedIndexes()
        if not selected_indexes:
            QMessageBox.information(self, "提示", "请先选中一项")
            return

        # 获取选中项的索引
        index = selected_indexes[0]

        # 获取显示文本
        display_text = self.model.data(index)

        # 获取隐藏数据(通过自定义角色)
        hidden_data = self.model.data(index, self.HIDDEN_DATA_ROLE)

        # 显示结果
        info = f"显示名称: {display_text}\n"
        info += f"隐藏ID: {hidden_data['id']}\n"
        info += f"隐藏邮箱: {hidden_data['email']}"
        QMessageBox.information(self, "隐藏数据详情", info)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = ListViewHiddenData()
    sys.exit(app.exec_())

原文出处:/show_1GW1scJOxE0k.html