PyQt5的QListView多选返回值 (转载)

本文介绍如何使用PyQt5中的QListView组件实现多选功能,并通过两种方法展示了如何获取已选项目的详细步骤。

https://ask.youkuaiyun.com/questions/715633

https://img-ask.youkuaiyun.com/upload/201812/03/1543834681_373537.gif
在这里插入图片描述

 import sys
from PyQt5.QtWidgets import QApplication,QWidget,QVBoxLayout,QListView,QAbstractItemView
from PyQt5.QtCore import QStringListModel
from PyQt5 import QtWidgets

class ListViewDemo(QWidget):
    def __init__(self,parent=None):
        super(ListViewDemo, self).__init__(parent)
        #设置初始大小与标题
        self.resize(300,270)
        self.setWindowTitle('QListView 多选问题')

        #垂直布局
        self.layout=QVBoxLayout()

        #实例化列表视图
        self.listview=QListView()

        #实例化列表模型,添加数据
        self.slm=QStringListModel()
        self.qList=['Item 1','Item 2','Item 3','Item 4','Item 5','Item 6','Item 7','Item 8','Item 9']

        #设置模型列表视图,加载数据列表
        self.slm.setStringList(self.qList)

        #设置列表视图的模型
        self.listview.setModel(self.slm)

        # 多选
        self.listview.setSelectionMode(QAbstractItemView.ExtendedSelection)
        # 不能对表格进行修改(双击重命名等)
        self.listview.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.label_dqxz = QtWidgets.QLabel()
        self.label_dqxz.setText("当前选择:-")

        #单击触发自定义的槽函数
        self.listview.clicked.connect(self.clicked)


        #设置窗口布局,加载控件
        self.layout.addWidget(self.listview)
        self.layout.addWidget(self.label_dqxz)
        self.setLayout(self.layout)

    def clicked(self,index):

        self.label_dqxz.setText('当前选择:'+self.qList[index.row()])


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

code

方法1:

import sys
from PyQt5.QtWidgets import QApplication,QWidget,QVBoxLayout,QListView,QAbstractItemView
from PyQt5.QtCore import QStringListModel
from PyQt5 import QtWidgets

class ListViewDemo(QWidget):
    def __init__(self,parent=None):
        super(ListViewDemo, self).__init__(parent)
        #设置初始大小与标题
        self.resize(300,270)
        self.setWindowTitle('QListView 多选问题')

        #垂直布局
        self.layout=QVBoxLayout()

        #实例化列表视图
        self.listview=QListView()

        #实例化列表模型,添加数据
        self.slm=QStringListModel()
        self.qList=['Item 1','Item 2','Item 3','Item 4','Item 5','Item 6','Item 7','Item 8','Item 9']

        #设置模型列表视图,加载数据列表
        self.slm.setStringList(self.qList)

        #设置列表视图的模型
        self.listview.setModel(self.slm)

        # 多选
        self.listview.setSelectionMode(QAbstractItemView.ExtendedSelection)
        # 不能对表格进行修改(双击重命名等)
        self.listview.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.label_dqxz = QtWidgets.QLabel()
        self.label_dqxz.setText("当前选择:-")

        #单击触发自定义的槽函数
        self.listview.clicked.connect(self.clicked)



        #设置窗口布局,加载控件
        self.layout.addWidget(self.listview)
        self.layout.addWidget(self.label_dqxz)
        self.setLayout(self.layout)

    def clicked(self):
        textlist=''
        for i in self.listview.selectedIndexes():
            text=i.data()
            textlist=textlist+' '+text
        self.label_dqxz.setText('当前选择:'+textlist)


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

方法2:

 import sys
from PyQt5.QtWidgets import QApplication,QWidget,QVBoxLayout,QAbstractItemView,QListWidget
from PyQt5 import QtWidgets

class ListViewDemo(QWidget):
    def __init__(self,parent=None):
        super(ListViewDemo, self).__init__(parent)
        #设置初始大小与标题
        self.resize(300,270)
        self.setWindowTitle('QListView 多选问题')

        #垂直布局
        self.layout=QVBoxLayout()

        self.qList=['Item 1','Item 2','Item 3','Item 4','Item 5','Item 6','Item 7','Item 8','Item 9','Item 6','Item 7','Item 8','Item 9','Item 6','Item 7','Item 8','Item 9']

        self.listWidget = QListWidget(self)
        self.listWidget.addItems(self.qList) #批量加

        self.listWidget.setSelectionMode(QAbstractItemView.ExtendedSelection)  # 按住CTRL可多选
        #self.listWidget.setSelectionMode(QAbstractItemView.SingleSelection)  #单选

        text_list = self.listWidget.selectedItems()
        text = [i.text() for i in list(text_list)]
        print(text)

        self.label_dqxz = QtWidgets.QLabel()
        self.label_dqxz.setText("当前选择:-")

        #单击触发自定义的槽函数
        self.listWidget.itemClicked.connect(self.clicked)

        #设置窗口布局,加载控件
        self.layout.addWidget(self.listWidget)
        self.layout.addWidget(self.label_dqxz)
        self.setLayout(self.layout)

    def clicked(self):
        text_list = self.listWidget.selectedItems()
        text = [i.text() for i in list(text_list)]
        text = '、'.join(text)  # text即多选项并以、隔开
        self.label_dqxz.setText('当前选择:' + text)

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、付费专栏及课程。

余额充值