pyqt5 QListView setModel QAbstractListModel

代码:

# todict
# https://github.com/SiggiGue/qdictionarytree

# 本文
# https://www.learnpyqt.com/courses/model-views/modelview-architecture/

import sys
import json
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from PyQt5.QtCore import *
import sys
import sys
from PyQt5.QtWidgets import QWidget, QToolTip, QApplication
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys

# There is interesting way
# no need to compile...
# I am wondering if this works to exe file..since it's make file less

tick = QtGui.QImage('tick2.png')

class TodoModel(QtCore.QAbstractListModel):
    def __init__(self, *args, todos=None, **kwargs):
        super(TodoModel, self).__init__(*args, **kwargs)
        self.todos = todos or []

    def data(self, index, role):
        if role == Qt.DisplayRole:
            _, text = self.todos[index.row()]
            return text

        if role == Qt.DecorationRole:
            status, _ = self.todos[index.row()]
            if status:
                return tick

    def rowCount(self, index):
        return len(self.todos)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):

        QtWidgets.QMainWindow.__init__(self)

        self.setObjectName("MainWindow")
        self.resize(461, 476)

        self.centralwidget = QtWidgets.QWidget(self)
        self.top_layout = QVBoxLayout()
        self.centralwidget.setLayout(self.top_layout)

        self.completeButton = QtWidgets.QPushButton("Complete")
        self.deleteButton = QtWidgets.QPushButton("Delete")
        self.addButton = QtWidgets.QPushButton("Add Todo")
        self.todoEdit = QtWidgets.QLineEdit()


        self.todoView = QtWidgets.QListView()

        self.top_layout.addWidget(self.todoView)
        self.mid_layout = QHBoxLayout()
        self.mid_layout.addWidget(self.completeButton)
        self.mid_layout.addWidget(self.deleteButton)
        self.top_layout.addLayout(self.mid_layout)
        self.top_layout.addWidget(self.todoEdit)
        self.top_layout.addWidget(self.addButton)

        self.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(self)
        self.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(self)

        self.setStatusBar(self.statusbar)


        self.setWindowTitle("Todo")


        self.model = TodoModel()
        self.load()
        self.todoView.setModel(self.model)
        self.addButton.pressed.connect(self.add)
        self.deleteButton.pressed.connect(self.delete)
        self.completeButton.pressed.connect(self.complete)

    def add(self):
        """
        Add an item to our todo list, getting the text from the QLineEdit .todoEdit
        and then clearing it.
        """
        text = self.todoEdit.text()
        if text: # Don't add empty strings.
            # Access the list via the model.
            self.model.todos.append((False, text))
            # Trigger refresh.
            self.model.layoutChanged.emit()
            # Empty the input
            self.todoEdit.setText("")
            self.save()

    def delete(self):
        # use .selectedIndexes to get index, which is a list with one elt
        indexes = self.todoView.selectedIndexes()
        if indexes:
            # Indexes is a list of a single item in single-select mode.
            index = indexes[0]
            # Remove the item and refresh.
            del self.model.todos[index.row()]
            self.model.layoutChanged.emit()
            # Clear the selection (as it is no longer valid).
            self.todoView.clearSelection()
            self.save()

    def complete(self):
        indexes = self.todoView.selectedIndexes()
        if indexes:
            index = indexes[0]
            row = index.row()
            status, text = self.model.todos[row]
            self.model.todos[row] = (True, text)
            # .dataChanged takes top-left and bottom right, which are equal
            # for a single selection.
            self.model.dataChanged.emit(index, index)
            # Clear the selection (as it is no longer valid).
            self.todoView.clearSelection()
            self.save()

    # how you persist the data
    def load(self):
        try:
            with open('data.json', 'r') as f:
                self.model.todos = json.load(f)
        except Exception:
            pass

    def save(self):
        with open('data.json', 'w') as f:
            data = json.dump(self.model.todos, f)


app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

tick2.png 图形
在这里插入图片描述

data.json 的 内容为:

[[true, "aa"], [true, "bb"], [false, "cc"]]

演示效果:
在这里插入图片描述
class TodoModel(QtCore.QAbstractListModel):
里面的def data(self, index, role): 函数

在这里插入图片描述

<think>我们正在讨论PyQt5中的QListView模型使用方法。根据引用[1][2][3],QListView是基于模型/视图(Model/View)架构的组件,它通过模型来管理数据,视图来展示数据。与QListWidget不同,QListView不直接存储数据,而是依赖于一个模型(如QStringListModel、QStandardItemModel或自定义模型)来提供数据。 下面将详细介绍QListView的使用方法,包括: 1. 使用QStringListModel显示简单列表 2. 使用QStandardItemModel显示复杂数据 3. 自定义模型(如果需要) 4. 处理视图中的选择 5. 编辑列表项 6. 自定义视图外观 ## 1. 使用QStringListModel显示简单列表 这是最简单的用法,适用于只显示字符串列表的情况。 ```python import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QListView, QMessageBox from PyQt5.QtCore import QStringListModel class ListViewDemo(QWidget): def __init__(self, parent=None): super(ListViewDemo, self).__init__(parent) self.setWindowTitle("QListView 例子") self.resize(300, 270) layout = QVBoxLayout() # 创建列表视图 listview = QListView() # 创建字符串列表模型 listModel = QStringListModel() self.list = ["列表项1", "列表项2", "列表项3", "列表项4"] listModel.setStringList(self.list) # 将模型设置到视图 listview.setModel(listModel) # 连接点击信号 listview.clicked.connect(self.onClickedListView) layout.addWidget(listview) self.setLayout(layout) def onClickedListView(self, index): # 通过索引获取数据 item_text = self.list[index.row()] QMessageBox.information(self, "QListView", "您选择了:" + item_text) if __name__ == "__main__": app = QApplication(sys.argv) win = ListViewDemo() win.show() sys.exit(app.exec_()) ``` ## 2. 使用QStandardItemModel显示复杂数据 当需要显示除文本以外的其他数据(如图标、复选框等)时,可以使用QStandardItemModel。 ```python import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QListView, QMessageBox from PyQt5.QtGui import QStandardItemModel, QStandardItem, QIcon from PyQt5.QtCore import Qt class StandardItemModelDemo(QWidget): def __init__(self, parent=None): super(StandardItemModelDemo, self).__init__(parent) self.setWindowTitle("QStandardItemModel 例子") self.resize(300, 270) layout = QVBoxLayout() # 创建列表视图 listview = QListView() # 创建标准项模型 self.model = QStandardItemModel() # 添加复杂项(带图标) item1 = QStandardItem(QIcon("icon1.png"), "项目1") item2 = QStandardItem(QIcon("icon2.png"), "项目2") item3 = QStandardItem("项目3") item3.setCheckable(True) # 可勾选 item3.setCheckState(Qt.Checked) # 默认勾选 self.model.appendRow(item1) self.model.appendRow(item2) self.model.appendRow(item3) # 将模型设置到视图 listview.setModel(self.model) # 连接点击信号 listview.clicked.connect(self.onClickedListView) layout.addWidget(listview) self.setLayout(layout) def onClickedListView(self, index): item = self.model.itemFromIndex(index) text = item.text() state = "已勾选" if item.checkState() == Qt.Checked else "未勾选" QMessageBox.information(self, "QListView", f"您选择了:{text},状态:{state}") if __name__ == "__main__": app = QApplication(sys.argv) win = StandardItemModelDemo() win.show() sys.exit(app.exec_()) ``` ## 3. 自定义模型(高级用法) 当数据量非常大或数据结构复杂时,可以自定义模型以提高效率。这里以自定义列表模型为例: ```python from PyQt5.QtCore import QAbstractListModel, Qt, QModelIndex class CustomListModel(QAbstractListModel): def __init__(self, data=None, parent=None): super(CustomListModel, self).__init__(parent) self.data_list = data or [] def rowCount(self, parent=QModelIndex()): return len(self.data_list) def data(self, index, role=Qt.DisplayRole): if not index.isValid() or not (0 <= index.row() < len(self.data_list)): return None item = self.data_list[index.row()] if role == Qt.DisplayRole: return item.get('text', '') elif role == Qt.DecorationRole: # 返回图标 return QIcon(item.get('icon', '')) elif role == Qt.CheckStateRole and item.get('checkable', False): # 返回勾选状态 return Qt.Checked if item.get('checked', False) else Qt.Unchecked return None def flags(self, index): flags = super().flags(index) if index.isValid(): item = self.data_list[index.row()] if item.get('checkable', False): flags |= Qt.ItemIsUserCheckable return flags def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or role != Qt.CheckStateRole: return False row = index.row() if 0 <= row < len(self.data_list): if self.data_list[row].get('checkable', False): self.data_list[row]['checked'] = value == Qt.Checked self.dataChanged.emit(index, index, [role]) return True return False ``` 使用自定义模型: ```python # 在窗口类中 model = CustomListModel([ {'text': '项目1', 'icon': 'icon1.png', 'checkable': True, 'checked': True}, {'text': '项目2', 'icon': 'icon2.png'}, {'text': '项目3', 'checkable': True} ]) listview.setModel(model) ``` ## 4. 处理视图中的选择 要获取用户选择的项,可以使用QItemSelectionModel。 ```python # 获取选择模型 selection_model = listview.selectionModel() selection_model.selectionChanged.connect(self.handle_selection) def handle_selection(self, selected, deselected): # 获取当前选中的索引 indexes = selected.indexes() if indexes: index = indexes[0] # 通过模型获取数据 data = index.data(Qt.DisplayRole) print(f"选中的项目: {data}") ``` ## 5. 编辑列表项 默认情况下,列表项不可编辑。要使列表项可编辑,需要设置模型项的flags,并实现setData方法(在自定义模型中)或使用QStandardItem的setEditable。 对于QStandardItemModel: ```python item = QStandardItem("可编辑项") item.setEditable(True) # 设置为可编辑 ``` 在自定义模型中,需要重写flags和setData方法: ```python def flags(self, index): flags = super().flags(index) if index.isValid(): # 添加可编辑标志 flags |= Qt.ItemIsEditable return flags def setData(self, index, value, role=Qt.EditRole): if role == Qt.EditRole: row = index.row() if 0 <= row < len(self.data_list): self.data_list[row]['text'] = value self.dataChanged.emit(index, index, [role]) return True return False ``` ## 6. 自定义视图外观 可以通过委托(Delegate)来自定义列表项的绘制和编辑。 ```python from PyQt5.QtWidgets import QStyledItemDelegate from PyQt5.QtGui import QPainter class CustomDelegate(QStyledItemDelegate): def paint(self, painter, option, index): # 自定义绘制 painter.save() # 设置背景色 if option.state & QStyle.State_Selected: painter.fillRect(option.rect, QColor(200, 200, 255)) # 绘制文本 text = index.data(Qt.DisplayRole) or "" painter.drawText(option.rect, Qt.AlignLeft, text) painter.restore() # 设置委托 listview.setItemDelegate(CustomDelegate()) ``` ## 总结 - **QListView** 是基于模型/视图架构的列表组件,适合展示数据列表。 - 使用 **QStringListModel** 可以快速显示字符串列表(引用[1][3])。 - 使用 **QStandardItemModel** 可以显示带图标、复选框等复杂项(引用[2])。 - 对于特殊需求,可以自定义模型(继承自QAbstractListModel)或自定义委托(继承自QStyledItemDelegate)来实现。 - 通过模型索引(QModelIndex)来访问和操作数据,通过选择模型(QItemSelectionModel)来处理选择。 §§相关问题§§ 1. 如何在QListView中实现拖放排序功能? 2. QListView和QListWidget在性能上有何差异?如何选择? 3. 如何在QListView中实现树形结构? 4. 如何实现QListView与数据库的实时同步?</think># PyQt5 QListView 模型使用详解 在 PyQt5 中,`QListView` 是基于模型/视图(Model/View)架构的列表组件,它通过模型来管理数据,通过视图来展示数据。相比 `QListWidget`,`QListView` 提供了更灵活的数据管理方式,特别适合处理大型数据集或需要复杂数据操作的情况。 ## 基本概念:模型/视图架构 PyQt5 的模型/视图架构包含三个核心组件: - **模型(Model)**:管理数据,提供数据访问接口 - **视图(View)**:显示数据,提供用户交互界面 - **委托(Delegate)**:控制数据项的显示和编辑方式 ## QListView 基本用法 ### 1. 使用 QStringListModel 创建简单列表 ```python import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QListView, QMessageBox from PyQt5.QtCore import QStringListModel class ListViewDemo(QWidget): def __init__(self, parent=None): super(ListViewDemo, self).__init__(parent) self.setWindowTitle("QListView 基本示例") self.resize(300, 270) layout = QVBoxLayout() # 创建列表视图 self.listview = QListView() # 创建字符串列表模型 self.model = QStringListModel() self.data_list = ["Python", "Java", "C++", "JavaScript", "Go"] self.model.setStringList(self.data_list) # 设置模型到视图 self.listview.setModel(self.model) # 连接点击信号 self.listview.clicked.connect(self.on_item_clicked) layout.addWidget(self.listview) self.setLayout(layout) def on_item_clicked(self, index): """处理项目点击事件""" selected_item = self.data_list[index.row()] QMessageBox.information(self, "选择信息", f"您选择了:{selected_item}") if __name__ == '__main__': app = QApplication(sys.argv) win = ListViewDemo() win.show() sys.exit(app.exec_()) ``` ## 高级模型使用 ### 2. 使用 QStandardItemModel 创建复杂列表 ```python from PyQt5.QtGui import QStandardItemModel, QStandardItem from PyQt5.QtCore import Qt class AdvancedListView(QWidget): def __init__(self): super().__init__() self.setWindowTitle("高级 QListView 示例") self.setGeometry(100, 100, 500, 400) layout = QVBoxLayout() # 创建列表视图 self.listview = QListView() # 创建标准项模型 self.model = QStandardItemModel() # 添加复杂项目 self.add_items() # 设置模型到视图 self.listview.setModel(self.model) # 连接信号 self.listview.doubleClicked.connect(self.on_double_click) layout.addWidget(self.listview) self.setLayout(layout) def add_items(self): """添加带图标和工具提示的项目""" items = [ ("Python", "高级编程语言", "python_icon.png"), ("Java", "面向对象编程语言", "java_icon.png"), ("C++", "系统级编程语言", "cpp_icon.png"), ("JavaScript", "Web开发语言", "js_icon.png") ] for text, tooltip, icon_path in items: item = QStandardItem(QIcon(icon_path), text) item.setToolTip(tooltip) item.setCheckable(True) # 可勾选 item.setEditable(True) # 可编辑 self.model.appendRow(item) def on_double_click(self, index): """处理双击事件""" item = self.model.itemFromIndex(index) status = "已选中" if item.checkState() == Qt.Checked else "未选中" QMessageBox.information( self, "项目详情", f"名称: {item.text()}\n状态: {status}\n提示: {item.toolTip()}" ) ``` ## 自定义模型实现 对于更复杂的需求,可以创建自定义模型: ```python from PyQt5.QtCore import QAbstractListModel, Qt, QModelIndex class CustomListModel(QAbstractListModel): def __init__(self, data=None, parent=None): super().__init__(parent) self.data_list = data or [] def rowCount(self, parent=QModelIndex()): """返回数据行数""" return len(self.data_list) def data(self, index, role=Qt.DisplayRole): """根据角色返回数据""" if not index.isValid() or not (0 <= index.row() < len(self.data_list)): return None item = self.data_list[index.row()] if role == Qt.DisplayRole: return item.get('name', '') elif role == Qt.DecorationRole: return QIcon(item.get('icon', '')) elif role == Qt.ToolTipRole: return item.get('description', '') elif role == Qt.CheckStateRole: return Qt.Checked if item.get('selected', False) else Qt.Unchecked return None def flags(self, index): """设置项目标志""" flags = super().flags(index) if index.isValid(): flags |= Qt.ItemIsUserCheckable | Qt.ItemIsEditable return flags def setData(self, index, value, role=Qt.EditRole): """设置数据""" if not index.isValid() or role not in (Qt.EditRole, Qt.CheckStateRole): return False row = index.row() if 0 <= row < len(self.data_list): if role == Qt.EditRole: self.data_list[row]['name'] = value elif role == Qt.CheckStateRole: self.data_list[row]['selected'] = value == Qt.Checked self.dataChanged.emit(index, index, [role]) return True return False ``` ### 使用自定义模型 ```python # 在窗口类中使用自定义模型 def setup_custom_model(self): """设置自定义模型""" data = [ {'name': 'Python', 'description': '高级编程语言', 'icon': 'python.png', 'selected': True}, {'name': 'Java', 'description': '面向对象编程语言', 'icon': 'java.png', 'selected': False}, {'name': 'C++', 'description': '系统级编程语言', 'icon': 'cpp.png', 'selected': True} ] self.model = CustomListModel(data) self.listview.setModel(self.model) ``` ## 模型操作技巧 ### 1. 添加和删除项目 ```python # 添加项目 def add_item(self, name, description, icon_path): """添加新项目到模型""" row = self.model.rowCount() self.model.insertRow(row) index = self.model.index(row) self.model.setData(index, name, Qt.DisplayRole) self.model.setData(index, description, Qt.ToolTipRole) self.model.setData(index, QIcon(icon_path), Qt.DecorationRole) # 删除项目 def remove_selected(self): """删除选中的项目""" indexes = self.listview.selectedIndexes() if indexes: index = indexes[0] self.model.removeRow(index.row()) ``` ### 2. 排序和过滤 ```python # 排序 def sort_items(self, ascending=True): """排序项目""" self.model.sort(0, Qt.AscendingOrder if ascending else Qt.DescendingOrder) # 过滤 def filter_items(self, text): """根据文本过滤项目""" proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(self.model) proxy_model.setFilterRegExp(text) proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.listview.setModel(proxy_model) ``` ### 3. 拖放功能 ```python def enable_drag_drop(self): """启用拖放功能""" self.listview.setDragEnabled(True) self.listview.setAcceptDrops(True) self.listview.setDropIndicatorShown(True) self.listview.setDragDropMode(QAbstractItemView.InternalMove) self.model.rowsMoved.connect(self.on_rows_moved) def on_rows_moved(self, parent, start, end, destination, row): """处理行移动事件""" print(f"项目从位置 {start}-{end} 移动到 {row}") ``` ## 性能优化建议 1. **批量操作**: ```python # 开始重置模型 self.model.beginResetModel() # 执行大量数据修改... self.model.endResetModel() ``` 2. **使用代理模型**: 对于大型数据集,使用 `QSortFilterProxyModel` 处理排序和过滤,避免修改原始数据模型[^2]。 3. **延迟加载**: 对于超大型数据集,实现自定义模型只加载可见项: ```python class LazyLoadModel(QAbstractListModel): def data(self, index, role=Qt.DisplayRole): if not index.isValid(): return None # 仅当项目在可见范围内时才加载数据 if self.is_visible(index.row()): return self.load_data(index.row(), role) return None ``` ## QListView 与 QListWidget 比较 | 特性 | QListView | QListWidget | |------|-----------|-------------| | **架构** | 模型/视图 | 自带模型 | | **数据管理** | 通过外部模型 | 内部管理 | | **灵活性** | 高 | 中 | | **性能** | 大型数据集更优 | 小型数据集更简单 | | **自定义** | 支持自定义模型和委托 | 有限的自定义 | | **使用场景** | 大型/动态数据集 | 小型静态列表 | ## 总结 QListView 的核心优势在于其模型/视图分离架构: 1. 使用 `QStringListModel` 处理简单字符串列表[^1][^3] 2. 使用 `QStandardItemModel` 处理带图标、复选框等复杂项[^2] 3. 自定义模型处理特殊数据结构或大型数据集 4. 通过代理模型实现排序和过滤功能 5. 使用委托自定义项目的外观和编辑行为
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值