Qt代理委托实现

背景

Qt中的表格控件或树状图控件都提供了单元格基础的一些样式修改,可以通过qss设置,但是如果需要更加特性化的内容就需要实现代理QStyledItemDelegate

实现

继承QStyledItemDelegate,并按需要重写6个函数

def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex)

这个函数是用于绘制单元格的背景,可以通过painter绘制点线面,还可以绘制控件样式,可以根据index单独控制某个单元格的背景。像以下代码就是在(1, 1)绘制了一张图片,(2, 2)绘制了一个进度条,(3, 2)绘制了一个按钮。

python 复制代码
    def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex):
        # 绘制单元格背景,不单止可以绘制线条、图片、还可以绘制进度条、按钮等控件
        if index.row() == 1 and index.column() == 1:
            rect: QRectF = option.rect
            pixmap = QPixmap("ico.png")
            pixmap = pixmap.scaled(rect.width(), rect.height(), Qt.KeepAspectRatio)
            painter.drawPixmap(rect.x(), rect.y(), pixmap.width(), pixmap.height(), pixmap)
        elif index.row() == 2 and index.column() == 2:
            progressBarStyled = QStyleOptionProgressBar()
            progressBarStyled.rect = option.rect
            progressBarStyled.minimum = 0
            progressBarStyled.maximum = 100
            progressBarStyled.progress = int(index.data())
            progressBarStyled.text = f"{int(index.data())}%"
            progressBarStyled.state = option.state
            progressBarStyled.textVisible = True
            progressBarStyled.direction = option.direction
            QApplication.style().drawControl(QStyle.CE_ProgressBar, progressBarStyled, painter)
            return

        elif index.row() == 3 and index.column() == 2:
            btnStyled = QStyleOptionButton()
            btnStyled.rect = option.rect
            btnStyled.state = option.state
            btnStyled.text = f"click"
            btnStyled.direction = option.direction
            QApplication.style().drawControl(QStyle.CE_PushButton, btnStyled, painter)
            return
        
        return super().paint(painter, option, index)

def editorEvent(self, event: QEvent, model, option, index)

那么绘制出来的按钮如何触发点击事件呢,就是重写这个editorEvent函数,像以下代码就是判断了当这个单元格被按下时触发clicked信号发出

python 复制代码
    def editorEvent(self, event: QEvent, model, option, index):
        # 单元格事件处理
        if index.row() == 3 and index.column() == 2:
            if event.type() == QEvent.MouseButtonPress:
                model.setData(index, "click")
                self.clicked.emit("click")
                return True
        return super().editorEvent(event, model, option, index)

def createEditor(self, parent, option, index)

那么如果需要双击进入编辑单元格模式时显示控件编辑,即有可能需要是下拉框选择内容,就可以通过重写这个函数实现

python 复制代码
    def createEditor(self, parent, option, index):
        # 创建单元格编辑器,即双击单元格编辑时创建的编辑器
        if index.row() == 0 and index.column() == 0:
            spinBox = QDoubleSpinBox(parent)
            spinBox.setSingleStep(0.1)
            spinBox.setRange(0, 100)
            return spinBox
        if index.row() == 4 and index.column() == 4:
            combobox = QComboBox(parent)
            combobox.addItems(["None", "Left", "Right"])
            return combobox
        return super().createEditor(parent, option, index)

def updateEditorGeometry(self, editor, option, index)

如果需要修改由createEditor创建出来的控件的位置大小,可以通过重写这个函数实现,代码实现的是将(4, 4)内的下拉框位置大小修改为单元格的一半

python 复制代码
    def updateEditorGeometry(self, editor, option, index):
        # 更新单元格编辑器的几何形状,即设置编辑器的位置和大小
        if index.row() == 4 and index.column() == 4:
            rect = option.rect
            editor.setGeometry(rect.x(), rect.y(), rect.width(), rect.height() / 2)
            return
        return super().updateEditorGeometry(editor, option, index)

def setEditorData(self, editor, index)

Qt原本已经实现了单元格的数据添加到单元格编辑控件里面了,即如果双击单元格后显示的是一个doubleSpinBox的话,单元格显示的是80,那么SpinBox里显示的数据也是80。如果需要自定义值,例如一定要保证这个SpinBox的值一直是80,那么就可以重写这个函数实现。

python 复制代码
    def setEditorData(self, editor, index):
        # 设置单元格数据编辑器的数据
        if index.row() == 0 and index.column() == 0:
            editor.setValue(50)
            return
        return super().setEditorData(editor, index)

一直是保持50

def setModelData(self, editor, model, index)

Qt原本也实现了编辑器控件修改后的值会直接添加到单元格中。即双击后显示的SpinBox值修改成100后,单元格恢复成查看模式时显示的就是100。如果需要保证单元格显示的值一直不变,那么就可以通过重写这个函数实现

python 复制代码
    def setModelData(self, editor, model, index):
        # 通过编辑器修改值后,设置显示到单元格中的数据
        if index.row() == 0 and index.column() == 0:
            model.setData(index, 80)
            return
        return super().setModelData(editor, model, index) 

代码就是实现了不管编辑成什么数值,显示的都是80.

所以最后两个函数可以按需求确认是否需要重新实现

完整代码

python 复制代码
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

class StyledItemDelegate(QStyledItemDelegate):

    clicked = pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)

    def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex):
        # 绘制单元格背景,不单止可以绘制线条、图片、还可以绘制进度条、按钮等控件
        if index.row() == 1 and index.column() == 1:
            rect: QRectF = option.rect
            pixmap = QPixmap("ico.png")
            pixmap = pixmap.scaled(rect.width(), rect.height(), Qt.KeepAspectRatio)
            painter.drawPixmap(rect.x(), rect.y(), pixmap.width(), pixmap.height(), pixmap)
        elif index.row() == 2 and index.column() == 2:
            progressBarStyled = QStyleOptionProgressBar()
            progressBarStyled.rect = option.rect
            progressBarStyled.minimum = 0
            progressBarStyled.maximum = 100
            progressBarStyled.progress = int(index.data())
            progressBarStyled.text = f"{int(index.data())}%"
            progressBarStyled.state = option.state
            progressBarStyled.textVisible = True
            progressBarStyled.direction = option.direction
            QApplication.style().drawControl(QStyle.CE_ProgressBar, progressBarStyled, painter)
            return

        elif index.row() == 3 and index.column() == 2:
            btnStyled = QStyleOptionButton()
            btnStyled.rect = option.rect
            btnStyled.state = option.state
            btnStyled.text = f"click"
            btnStyled.direction = option.direction
            QApplication.style().drawControl(QStyle.CE_PushButton, btnStyled, painter)
            return
        
        return super().paint(painter, option, index)
    
    def editorEvent(self, event: QEvent, model, option, index):
        # 单元格事件处理
        if index.row() == 3 and index.column() == 2:
            if event.type() == QEvent.MouseButtonPress:
                model.setData(index, "click")
                self.clicked.emit("click")
                return True
        return super().editorEvent(event, model, option, index)
    
    def createEditor(self, parent, option, index):
        # 创建单元格编辑器,即双击单元格编辑时创建的编辑器
        if index.row() == 0 and index.column() == 0:
            spinBox = QDoubleSpinBox(parent)
            spinBox.setSingleStep(0.1)
            spinBox.setRange(0, 100)
            return spinBox
        if index.row() == 4 and index.column() == 4:
            combobox = QComboBox(parent)
            combobox.addItems(["None", "Left", "Right"])
            return combobox
        return super().createEditor(parent, option, index)
    
    def updateEditorGeometry(self, editor, option, index):
        # 更新单元格编辑器的几何形状,即设置编辑器的位置和大小
        if index.row() == 4 and index.column() == 4:
            rect = option.rect
            editor.setGeometry(rect.x(), rect.y(), rect.width(), rect.height() / 2)
            return
        return super().updateEditorGeometry(editor, option, index)

    def setEditorData(self, editor, index):
        # 设置单元格数据编辑器的数据
        if index.row() == 0 and index.column() == 0:
            editor.setValue(50)
            return
        return super().setEditorData(editor, index)

    def setModelData(self, editor, model, index):
        # 通过编辑器修改值后,设置显示到单元格中的数据
        if index.row() == 0 and index.column() == 0:
            model.setData(index, 80)
            return
        return super().setModelData(editor, model, index) 
相关推荐
布吉岛的石头11 小时前
Java 程序员第 18 阶段:实战Agent工作流:Java搭建自动化业务智能体
java·python·自动化
Jurio.11 小时前
使用.py脚本下载并加载开源大模型LLMs
python·ai·llama
张哈大11 小时前
解密Function Calling:AI Agent工具调用的标准化核心
人工智能·python·ai
安妮的小熊呢11 小时前
CRMEB开源商城系统 & 标准版系统(PHP)开发规范
开发语言·javascript·php
子榆.11 小时前
CANN ATC编译器:模型从Python到达芬奇指令走了多远
开发语言·python·neo4j
lookaroundd11 小时前
llm-compressor 普通量化调用链分析
python·算法
Loo国昌11 小时前
从 Agent 编排到 Skill Runtime:企业 AI 工程化的下一层抽象
大数据·人工智能·后端·python·自然语言处理
Dontla12 小时前
Multi-Agent多智能体项目如何从MVP过渡到生产项目?
开发语言
编码者卢布12 小时前
【Azure Service Bus】Azure Service Bus Java SDK 中 Token 刷新异常的排查思路
java·python·azure
兰令水12 小时前
topcode【随机算法题】【2026.5.20打卡-java版本】
java·开发语言·算法