wd1566 2025-08-21 20:43 采纳率: 100%
浏览 7
已结题

QTableView对多列设置了下拉选择框代理,就会异常退出,只保留一列下拉选择就能正常运行,为什么?

使用PyQt5编写界面, 使用了QTableView控件, 并使用代理,在TableView的列绑定下拉选择框,问题是,只要同时对两个列绑定了下拉选择框就会异常退出,报(0xC0000005)错误,注销任何一列的下拉代理,就会正常运行,实在找不出哪里问题。

单选下拉框代理类代码:

class SingleSelectDelegate(QStyledItemDelegate):  # 导通和断开下拉选择
    def __init__(self, items, parent=None):  # #######################################################################################################
        super().__init__(parent)
        self.items = items
        self.parent_widget = parent

    def createEditor(self, parent, option, index):
        try:
            editor = QComboBox(parent)
            editor.addItems(self.items)
            return editor
        except Exception as e:
            QMessageBox.critical(self.parent_widget, "Error", f"创建编辑器失败: {str(e)}", QMessageBox.Ok)
            return None

    def setEditorData(self, editor, index):
        # value = index.data(Qt.EditRole)
        value = index.model().data(index, Qt.EditRole)
        idx = editor.findText(value)
        if idx >= 0:
            editor.setCurrentIndex(idx)

    def setModelData(self, editor, model, index):
        model.setData(index, editor.currentText(), Qt.EditRole)

    def updateEditorGeometry(self, editor, option, index):
        editor.setGeometry(option.rect)


自定义多选下拉框和下拉框代理类:


```python
class MultiSelectComboBox(QComboBox):
    """支持多选的自定义QComboBox"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setEditable(True)
        self.lineEdit().setReadOnly(True)
        self.setModel(None)  # 先清空模型

        # 创建列表控件并添加复选框
        self.list_widget = QListWidget()
        self.setModel(self.list_widget.model())
        self.setView(self.list_widget)

        # 创建按钮容器
        self.button_widget = QWidget()
        button_layout = QHBoxLayout(self.button_widget)
        button_layout.setContentsMargins(5, 5, 5, 5)
        button_layout.setSpacing(5)

        # 全选按钮
        self.select_all_btn = QPushButton("全选")
        self.select_all_btn.setFixedHeight(25)
        self.select_all_btn.clicked.connect(self.selectAllItems)

        # 清除按钮
        self.clear_btn = QPushButton("清除")
        self.clear_btn.setFixedHeight(25)
        self.clear_btn.clicked.connect(self.clearAllItems)

        button_layout.addWidget(self.select_all_btn, alignment=Qt.AlignCenter)
        button_layout.addWidget(self.clear_btn, alignment=Qt.AlignCenter)

        # 将按钮添加到列表顶部
        button_item = QListWidgetItem()
        button_item.setSizeHint(QSize(0, 35))
        self.list_widget.addItem(button_item)
        self.list_widget.setItemWidget(button_item, self.button_widget)
        # 默认样式设置
        self.setStyleSheet("""
                    QComboBox {
                border: 1px solid #ccc;
                border-radius: 3px;
                padding: 1px 18px 1px 3px;
                min-width: 6em;
            }
            QComboBox::drop-down {
                border: 0px;
                width: 20px;
            }
            QListWidget {
                border: 1px solid #ccc;
                background: white;
                outline: none;
            }
            QCheckBox {
                padding: 3px;
                spacing: 5px;
            }
            QCheckBox::indicator {
                width: 16px;
                height: 16px;
            }
            QPushButton {
                border: 1px solid #ccc;
                border-radius: 3px;
                padding: 2px 8px;
                background: #f0f0f0;
            }
            QPushButton:hover {
                background: #e0e0e0;
            }
               """)

        # 点击下拉框时不关闭
        self.setStyleSheet("QComboBox::drop-down {border: 0px;}")
        self.lineEdit().installEventFilter(self)

    def selectAllItems(self):
        """全选所有项目"""
        for i in range(1, self.list_widget.count()):  # 跳过第一个按钮项
            item = self.list_widget.item(i)
            if item:
                widget = self.list_widget.itemWidget(item)
                if isinstance(widget, QCheckBox):
                    widget.setChecked(True)
        self.updateText()

    def clearAllItems(self):
        """清除所有选择"""
        for i in range(1, self.list_widget.count()):  # 跳过第一个按钮项
            item = self.list_widget.item(i)
            if item:
                widget = self.list_widget.itemWidget(item)
                if isinstance(widget, QCheckBox):
                    widget.setChecked(False)
        self.updateText()

    def setComboStyle(self, style_dict):
        """设置组合框样式"""
        style_sheet = []
        if 'border' in style_dict:
            style_sheet.append(f"QComboBox {{ border: {style_dict['border']}; }}")
        if 'border_radius' in style_dict:
            style_sheet.append(f"QComboBox {{ border-radius: {style_dict['border_radius']}px; }}")
        if 'padding' in style_dict:
            style_sheet.append(f"QComboBox {{ padding: {style_dict['padding']}; }}")
        if 'bg_color' in style_dict:
            style_sheet.append(f"QComboBox {{ background-color: {style_dict['bg_color']}; }}")

        if 'list_border' in style_dict:
            style_sheet.append(f"QListWidget {{ border: {style_dict['list_border']}; }}")
        if 'list_bg' in style_dict:
            style_sheet.append(f"QListWidget {{ background: {style_dict['list_bg']}; }}")

        if 'checkbox_spacing' in style_dict:
            style_sheet.append(f"QCheckBox {{ spacing: {style_dict['checkbox_spacing']}px; }}")

        self.setStyleSheet("\n".join(style_sheet))

    def setItemStyle(self, index, style_dict):
        """设置特定项目的样式"""
        if 0 <= index < self.list_widget.count():
            item = self.list_widget.item(index)
            widget = self.list_widget.itemWidget(item)
            if 'color' in style_dict:
                widget.setStyleSheet(f"color: {style_dict['color']};")
            if 'font_size' in style_dict:
                font = widget.font()
                font.setPointSize(style_dict['font_size'])
                widget.setFont(font)
            if 'bg_color' in style_dict:
                widget.setStyleSheet(f"background-color: {style_dict['bg_color']};")

    def addItems(self, items):
        """添加多选项目"""
        for item in items:
            self.addItem(item)

    def addItem(self, text, style=None):
        """添加单个项目"""
        item = QListWidgetItem(self.list_widget)
        checkbox = QCheckBox(text)
        checkbox.setCheckState(Qt.Unchecked)
        self.list_widget.addItem(item)
        self.list_widget.setItemWidget(item, checkbox)
        if style:
            self.setItemStyle(self.list_widget.count() - 1, style)

    def selectedItems(self):
        """获取选中的项目"""
        selected = []
        for i in range(1, self.list_widget.count()):  # 跳过第一个按钮
            item = self.list_widget.item(i)
            widget = self.list_widget.itemWidget(item)
            if widget and widget.isChecked():
                selected.append(widget.text())
        return selected

    def setSelectedItems(self, items):
        """设置选中项"""
        for i in range(1, self.list_widget.count()):  # 跳过第一个按钮
            item = self.list_widget.item(i)
            widget = self.list_widget.itemWidget(item)
            widget.setChecked(widget.text() in items)
        self.updateText()

    def updateText(self):
        """更新显示文本"""
        selected = self.selectedItems()
        self.lineEdit().setText(", ".join(selected) if selected else "未选择")


class MultiSelectDelegate(QStyledItemDelegate):
    """多选下拉框的表格列委托"""

    def __init__(self, items, parent=None, style=None):
        super().__init__(parent)
        self.items = items
        self.style = style or {}

    def createEditor(self, parent, option, index):
        """创建编辑器"""
        editor = MultiSelectComboBox(parent)
        editor.addItems(self.items)
        if self.style:  # 设置下来框的样式
            editor.setComboStyle(self.style)
        return editor

    def setEditorData(self, editor, index):
        """从模型设置编辑器数据"""
        value = index.model().data(index, Qt.DisplayRole)
        if value:
            editor.setSelectedItems(value.split(","))

    def setModelData(self, editor, model, index):
        """将编辑器数据保存到模型"""
        model.setData(index, ",".join(editor.selectedItems()), Qt.EditRole)

    def updateEditorGeometry(self, editor, option, index):
        """更新编辑器几何形状"""
        editor.setGeometry(option.rect)


主程序中的代理加载代码:


```python
# 创建表格模型
            self.model = QStandardItemModel()
            self.tableView.setModel(self.model)
            self.tableView.setSelectionMode(QAbstractItemView.SingleSelection)

            # 添加标题行
            self.model.setHorizontalHeaderLabels(headers)  # 添加列标题
            # 允许用户通过右键菜单插入行
            combo_style = {
                'border': '2px solid #4CAF50',
                'border_radius': 5,
                'padding': '2px 20px 2px 5px',
                'bg_color': '#f8f8f8',
                'list_border': '1px solid #4CAF50',
                'list_bg': '#f0fff0',
                'checkbox_spacing': 8
            }
            # 为第四列设置代理(索引从0开始)
            self.tableView.setItemDelegateForColumn(3, MultiSelectDelegate(switches, style=combo_style))
            self.tableView.setItemDelegateForColumn(4, SingleSelectDelegate(data))
            self.tableView.horizontalHeader().resizeSection(3, 500)  # 设置第四列宽度为500像素
            self.tableView.horizontalHeader().resizeSection(4, 100)  # 设置第四列宽度为500像素
            self.tableView.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.SelectedClicked) 

  • 写回答

4条回答 默认 最新

  • 宝码香车 优质创作者: JavaScript技术领域 2025-08-21 20:45
    关注

    让【宝码香车】来帮您解答,本回答参考DeepSeek编写,并整理提供。

    如果答案让您满意,请采纳、点赞、关注,非常感谢!
    .  
    每日一句正能量,让它带给您好心情:把平凡的日子,过出自己的滋味,就是伟大。
    .  

    问题分析:在QTableView中同时设置多列下拉选择框代理导致异常退出,通常是由于代理对象生命周期管理不当或编辑器创建/销毁时的资源冲突引起。

    主要可能原因及解决方案:

    1. 代理对象生命周期问题
    # 错误做法:在局部作用域创建代理,可能导致提前销毁
    delegate1 = MultiSelectDelegate(switches, style=combo_style)
    delegate2 = SingleSelectDelegate(data)
    self.tableView.setItemDelegateForColumn(3, delegate1)
    self.tableView.setItemDelegateForColumn(4, delegate2)
    
    
    # 正确做法:将代理作为实例变量保存
    self.delegates = {}
    self.delegates[3] = MultiSelectDelegate(switches, self.tableView, combo_style)
    self.delegates[4] = SingleSelectDelegate(data, self.tableView)
    self.tableView.setItemDelegateForColumn(3, self.delegates[3])
    self.tableView.setItemDelegateForColumn(4, self.delegates[4])
    
    1. MultiSelectComboBox中的模型设置问题
    # 修改MultiSelectComboBox的__init__方法
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setEditable(True)
        self.lineEdit().setReadOnly(True)
        
        # 移除这行有问题的代码
        # self.setModel(None)  # 这会导致问题
        
        # 创建列表控件
        self.list_widget = QListWidget()
        # 使用标准模型而不是清空
        self.setModel(QStandardItemModel())
        self.setView(self.list_widget)
    
    1. 修改代理类构造函数
    class MultiSelectDelegate(QStyledItemDelegate):
        def __init__(self, items, parent=None, style=None):
            # 确保传递正确的parent
            super().__init__(parent)
            self.items = items
            self.style = style or {}
            self.parent_widget = parent  # 保存父组件引用
    
    1. 检查编辑器创建时的异常处理
    def createEditor(self, parent, option, index):
        try:
            editor = MultiSelectComboBox(parent)
            editor.addItems(self.items)
            if self.style:
                editor.setComboStyle(self.style)
            return editor
        except Exception as e:
            print(f"创建编辑器失败: {e}")
            return super().createEditor(parent, option, index)  # 返回默认编辑器
    
    1. 确保正确的对象清理
    # 在窗口关闭时清理代理
    def closeEvent(self, event):
        for column in self.delegates:
            self.tableView.setItemDelegateForColumn(column, None)
        self.delegates.clear()
        super().closeEvent(event)
    

    建议的完整修复步骤:

    1. 将代理对象保存为实例变量
    2. 修复MultiSelectComboBox中的setModel(None)问题
    3. 确保所有代理都有正确的parent引用
    4. 添加适当的异常处理

    如果问题仍然存在,建议:

    • 使用调试工具检查内存访问错误
    • 简化MultiSelectComboBox的实现,逐步排查问题
    • 检查PyQt5版本兼容性

    这种多列代理冲突通常是由于Qt内部的对象管理和信号槽机制导致的资源竞争或重复释放引起的。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

问题事件

  • 系统已结题 8月29日
  • 已采纳回答 8月21日
  • 创建了问题 8月21日