PyQt5-QListView控件

MVC编程思想

  • 源数据由模型 (Model) 读取,然后在视图 (View) 组件上显示和编辑,在界面上编辑修改的数据又通过模型保存到源数据。
  • Model/View 结构将数据模型和用户界面分离开来,分别用不同的实现,是一种显示和编辑数据的有效结构,在处理大型数据时尤其明显。
  • 二者之间数据的交互则是通过委托(delegate)来实现。

常用Models

QT提供了一些现成的models用于处理数据项。

  • QStringListModel 用于存储简单的QString列表。
  • StandardItemModel 管理复杂的树型结构数据项,每项都可以包含任意数据。
  • QDirModel 提供本地文件系统中的文件与目录信息。
  • QSqlQueryModel, QSqlTableModel,QSqlRelationTableModel用来访问数据库。

QListView常用方法:

名称描述
setModel()用来设置View所关联的Model,可以使用Python原生的list作为数据源Model
setCurrentIndex()设置当前选中的项目
setViewMode()设置视图模式。(ListMode: 条目从上到下布局,小尺寸显示, IconMode: 条目从左到右布局,大尺寸显示)
listView.model().rowCount()ListView中的Item总数

QListView常用信号

clicked: 单击某项时,发射此信号;
doubleClicked: 双击击某项时,发射此信号;
activated: 当用户激活index指定的项目时,发射此信号;
entered: 当鼠标光标进入index指定的项目时,发射此信号;
iconSizeChanged: 在视图可见时设置此图标大小时,发射此信号;
indexesMoved: 在视图中移动索引时,发射此信号;
pressed: 按下鼠标按钮时,发射此信号;
viewportEntered: 当鼠标光标进入视图时,发射此信号。


步骤

  • QListView对象
  • 定义model,使用model的setStringList方法或者appendRow(item)给model填数。
  • QListView.setModel()方法关联二者。
  • 槽函数传递的参数是选中的index。
import os
import sys

from PyQt5.QtGui import QIcon, QStandardItem, QStandardItemModel
from PyQt5.QtWidgets import (QApplication, QWidget, QListView,
                             QLabel, QVBoxLayout)


class DemoListView(QWidget):
    def __init__(self, parent=None):
        super(DemoListView, self).__init__(parent)
        self.setWindowTitle("QListView 例子")
        self.resize(480, 360)
        self.init_ui()

    def init_ui(self):
        v_box = QVBoxLayout(self)
        l_view = QListView(self)
        s_model = QStandardItemModel()
        self.sports = [
            {'img': '/images/dog.png', 'title': '篮球'},
            {'img': '/images/boy.jpg', 'title': '足球'}]

        for sport in self.sports:
            item = QStandardItem(QIcon(os.path.dirname(__file__) + sport['img']), sport['title'])
            s_model.appendRow(item)

        l_view.setModel(s_model)
        l_view.clicked.connect(self.on_listview_clicked)
        self.label = QLabel(self)

        v_box.addWidget(l_view)
        v_box.addWidget(self.label)
        self.setLayout(v_box)

        l_view.setCurrentIndex(s_model.index(1, 0))  # 设置选中第一行
        self.label.setText('当前选择为: ' + self.sports[0]['title'])

    def on_listview_clicked(self, index):
        self.label.setText('当前选择为: ' + self.sports[index.row()]['title'])


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

from PyQt5.Qt import *
import sys


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

        l_view = QListView()                # 创建一个listview对象
        string_model = QStringListModel()   # 创建model
        self.qList = ['Item 1', 'Item 2', 'Item 3', 'Item 4']  # 添加的数组数据
        string_model.setStringList(self.qList)  # 将数据设置到model
        l_view.setModel(string_model)       #绑定 l_view 和 model
        l_view.clicked.connect(self.clickedlist)
        vbox.addWidget(l_view)
        self.setLayout(vbox)
        l_view.setCurrentIndex(string_model.index(2))  # 设置选中第一行
        print(l_view.model().rowCount())
        print(l_view.selectedIndexes())

    def clickedlist(self, index):
        QMessageBox.information(self, "QListView", "你选择了: " + self.qList[index.row()])
        print("点击的是:" + str(index.row()))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = ListViewDemo()
    win.show()
    sys.exit(app.exec_())
<think>我们正在讨论的是PyQt5中的QListView控件。根据用户的问题,他想要在QListView中添加控件,而不仅仅是文本或图标。在QListView中,默认情况下,每个项(item)是由文本或图标表示的。但是,如果我们想要在项中添加更复杂的控件(例如按钮、复选框等),我们需要使用自定义委托(Delegate)。 在Model/View架构中,视图(如QListView)负责显示模型数据,而委托(Delegate)负责渲染和编辑每个项。因此,为了在QListView的每个项中显示自定义控件,我们可以通过子类化QStyledItemDelegate并重写它的paint()和createEditor()等方法来实现。 步骤概述: 1. 创建一个模型(例如QStandardItemModel)并设置给QListView。 2. 创建一个自定义委托类,继承自QStyledItemDelegate。 3. 在自定义委托中,重写paint()方法来绘制我们想要的控件(但这通常用于只读显示,如果要交互,需要重写createEditor()等方法)。 4. 如果希望控件可交互(如按钮点击),还需要重写editorEvent()方法处理事件。 但是,请注意:在项中嵌入交互控件可能会带来一些复杂性,因为每个项实际上不是一个独立的控件,而是由委托动态绘制和编辑的。另一种方法是使用QListWidget(它是QListView的项视图类,提供了基于项的接口)并设置每个项的小部件,但QListWidget的性能不如QListView,特别是在大量数据时。 然而,用户明确使用的是QListView,所以我们采用自定义委托的方式。 下面是一个示例,展示如何在QListView的每个项中绘制一个按钮(这里为了简化,我们只绘制按钮,并处理按钮的点击事件): 步骤: 1. 创建自定义委托,重写paint()方法绘制按钮,重写editorEvent()处理鼠标事件。 2. 在模型中存储每个项的数据,同时我们可能需要存储按钮的状态(例如是否被点击)等。 注意:由于委托是用于绘制和编辑的,每个项并没有一个真正的按钮控件,而是绘制出来的,当用户点击时,我们判断点击的位置是否在按钮区域内,然后触发一个操作。 示例代码: 我们创建一个自定义委托,在每一项中绘制一个按钮。同时,当用户点击这个按钮时,我们发出一个信号(或者调用一个函数)。 由于委托不能直接发出信号(因为不是QObject的子类,除非我们使用多继承),我们可以通过委托的editorEvent()方法中触发一个自定义信号,但通常委托不是从QObject派生的(在PyQt中,我们可以使用QObject来多继承)。因此,我们让委托继承自QStyledItemDelegate和QObject(以支持信号)。 但是,请注意:在PyQt5中,QStyledItemDelegate本身是QObject的子类,所以我们可以直接定义信号。 具体实现如下: 首先,我们定义一个自定义委托,它包含一个按钮被点击的信号。在paint()方法中,我们绘制一个按钮(使用样式选项的绘图设备)。在editorEvent()中,我们检测鼠标事件,如果点击在按钮区域内,则发射信号。 然后,我们在主窗口中将这个委托设置给QListView。 但是,请注意:每个项的数据模型中,我们可能需要存储一些额外的数据(例如按钮的文本、状态等)。这里为了简单,我们假设每个项都有一个按钮,按钮的文本固定为“点击我”。 实际上,我们可以在模型的数据角色中存储每个项按钮的相关信息(例如在Qt.UserRole+1的角色中)。 下面是一个简单的示例代码: 注意:由于在委托中绘制按钮并处理事件相对复杂,我们只实现一个简单的版本。 代码结构: 1. 导入必要的模块。 2. 创建一个自定义委托类,继承自QStyledItemDelegate。 3. 在委托类中定义信号(按钮点击时发射,携带模型索引)。 4. 重写paint()方法,绘制按钮。 5. 重写editorEvent()方法,处理鼠标事件,判断按钮点击。 6. 创建主窗口,设置模型和视图,并将委托设置给视图。 由于在PyQt中,信号只能定义在QObject的子类中,而QStyledItemDelegate已经是QObject的子类,所以我们可以定义信号。 但是,请注意:在多个项的情况下,我们需要在委托中为每个项绘制按钮,并且每个按钮的位置需要根据项的位置计算。在editorEvent()中,我们也要根据点击的位置判断是否点击了某个项的按钮。 下面开始写代码:</think>在 PyQt5 的 `QListView` 中添加自定义控件需要结合 **Model/View 架构** 和 **自定义委托(Delegate)** 来实现。以下是详细步骤和代码示例: --- ### 解决方案步骤 1. **创建自定义委托类**(继承 `QStyledItemDelegate`) - 重写 `createEditor()` 方法创建自定义控件 - 重写 `setEditorData()` 和 `setModelData()` 实现数据绑定 - 重写 `updateEditorGeometry()` 调整控件位置 2. **设置模型和委托** - 使用 `QStandardItemModel` 存储数据 - 将自定义委托设置给 `QListView` 3. **添加带控件的项** - 创建 `QStandardItem` 并设置数据 - 通过 `setData()` 存储控件所需信息 --- ### 完整代码示例 ```python import sys from PyQt5.QtWidgets import * from PyQt5.QtCore import * class CustomDelegate(QStyledItemDelegate): def createEditor(self, parent, option, index): # 从模型获取控件类型 widget_type = index.data(Qt.UserRole + 1) if widget_type == "button": editor = QPushButton("点击", parent) editor.clicked.connect(lambda: self.handle_button_click(index)) return editor elif widget_type == "checkbox": editor = QCheckBox("选项", parent) return editor else: # 默认文本编辑器 return super().createEditor(parent, option, index) def setEditorData(self, editor, index): # 根据控件类型设置数据 if isinstance(editor, QPushButton): editor.setText(index.data()) elif isinstance(editor, QCheckBox): editor.setChecked(index.data(Qt.CheckStateRole) == Qt.Checked) else: super().setEditorData(editor, index) def setModelData(self, editor, model, index): # 保存控件数据回模型 if isinstance(editor, QCheckBox): model.setData(index, editor.isChecked(), Qt.CheckStateRole) else: super().setModelData(editor, model, index) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) def handle_button_click(self, index): print(f"按钮点击: 行 {index.row()}, 数据={index.data()}") class MainWindow(QWidget): def __init__(self): super().__init__() self.setWindowTitle("QListView自定义控件示例") self.resize(400, 300) # 创建ListView和模型 self.list_view = QListView() self.model = QStandardItemModel() self.list_view.setModel(self.model) # 设置自定义委托 self.list_view.setItemDelegate(CustomDelegate()) # 添加带控件的项 self.add_item("文本项", "text") self.add_item("按钮项", "button") self.add_item("复选框项", "checkbox") layout = QVBoxLayout() layout.addWidget(self.list_view) self.setLayout(layout) def add_item(self, text, widget_type): item = QStandardItem() item.setText(text) # 存储控件类型到UserRole+1 item.setData(widget_type, Qt.UserRole + 1) self.model.appendRow(item) if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) ``` --- ### 关键点说明 1. **数据角色(Data Roles)**: - `Qt.DisplayRole`:显示文本 - `Qt.UserRole + N`:存储自定义数据(如控件类型) - `Qt.CheckStateRole`:存储复选框状态 2. **委托工作流程**: ```mermaid graph LR A[视图需要显示] --> B[委托创建编辑器] B --> C[委托设置编辑器数据] C --> D[用户交互] D --> E[委托保存数据到模型] ``` 3. **控件交互**: - 按钮点击通过信号连接处理函数 - 复选框状态自动保存到模型 - 可通过 `index.data(Qt.UserRole + N)` 获取自定义数据 --- ### 相关问题 1. 如何在 `QListView` 中实现不同行显示不同类型的控件? **答**:在模型的 `setData()` 中存储控件类型标识(如字符串或枚举),在委托的 `createEditor()` 中根据标识创建对应控件。 2. 如何获取 `QListView` 中自定义控件的状态变化? **答**:通过委托的 `setModelData()` 将状态保存到模型,通过模型的 `dataChanged` 信号通知状态变更。 3. `QListView` 和 `QListWidget` 在添加控件时有何区别? **答**:`QListWidget` 可直接使用 `setItemWidget()` 添加控件但性能较低;`QListView` + 委托的方式更适合大数据量和动态内容[^1][^2]。 4. 如何实现自定义控件的大小调整? **答**:重写委托的 `sizeHint()` 方法返回固定大小,或在 `updateEditorGeometry()` 中动态计算控件尺寸。 [^1]: 第26天 | 28天学会PyQt5,高级控件QListView [^2]: PyQt5-QListView控件
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值