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) 
相关推荐
飞天狗11120 小时前
零基础JavaWeb入门——第2课:让网页“活”起来 —— JSP是什么?
java·开发语言·前端·后端·web
2601_9563198820 小时前
期货夜盘无人值守监控什么:断线、无成交与拒单信号
python·区块链
CTA终结者20 小时前
期货量化目标仓和净持仓对不齐:天勤 TargetPosTask 与 pos 偏差排查
python·区块链
科技林总20 小时前
解决vllm服务漏扫问题
python·安全
醇氧21 小时前
【Linux】Java 服务生产级部署指南:实现常驻后台、开机自启与系统服务化管理
java·开发语言
凡人叶枫21 小时前
Effective C++ 条款16:成对使用 new 和 delete 时要采取相同形式
开发语言·c++·effective c++
不吃土豆的马铃薯21 小时前
C++ 高性能网络缓冲区 Buffer 源码解析
linux·服务器·开发语言·网络·c++
数据法师21 小时前
QuickSay :基于 Qt 的轻量级快捷短语管理工具
开发语言·qt
caimouse1 天前
Reactos 第1章 概述
c语言·开发语言·架构
.千余1 天前
【C++】C++继承入门(下):友元、静态成员与菱形继承的底层逻辑
开发语言·c++·笔记·学习·其他