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) 
相关推荐
Hello.Reader1 分钟前
REALITY 技术深度解析从 TLS 伪装、抗主动探测到 VLESS + XTLS Vision 完整实践
开发语言·php
不正经学生9 分钟前
C语言字符串函数进阶:安全版函数 + 错误处理 + 子串查找
c语言·开发语言·c++·算法·面试
Zane199413 分钟前
钻石继承调用哪个方法?一文讲透 MRO 与 C3 线性化算法
后端·python
ITKEY_20 分钟前
neovim基于nvim-jdtls 开发java报错
java·开发语言
kevinnett21 分钟前
图片生成跑到一半“失踪”了:我重新设计了异步任务状态机
python
無限進步D23 分钟前
Java 代码块
java·开发语言
吃好睡好便好23 分钟前
MATLAB仿真框图4
开发语言·matlab·仿真·分段函数
小白勇闯网安圈27 分钟前
Django 模板复用、ORM 查询与多对多关系
数据库·python·django
TheBestRucy28 分钟前
基于Dify的旅游攻略&王者荣耀攻略智能助手项目
服务器·开发语言·人工智能·python·算法·旅游
一水32 分钟前
Pi :内置四工具源码拆解——文件锁、变更快照与 bash 隔离
开发语言·chrome·ai·bash