Qt5 子类化 QStyledItemDelegate 格式

Posted

技术标签:

【中文标题】Qt5 子类化 QStyledItemDelegate 格式【英文标题】:Qt5 Subclassing QStyledItemDelegate Formatting 【发布时间】:2016-08-31 08:20:32 【问题描述】:

我正在为 Qt5 中的 SQLite 数据库开发 GUI。我使用QSqlQueryModelQTableView 来存储和显示数据。

然后我创建了一个自定义委托,使用 switch 语句将某些列的数值替换为表格视图中的文字(例如 1 = "Hello", 2 = "World")。

委托按应有的方式显示数据并正常工作。但是,自定义委托绘制的列与QStyledItemDelegate 的默认绘制方法相比具有不同的格式。值在左上角而不是左中,更改后的列不再自动扩展列以显示完整值,并且列中的单元格在选择时不会变成蓝色或具有虚线轮廓。

我创建了这个示例程序:

#include <QApplication>
#include <QModelIndex>
#include <QPainter>
#include <QStandardItemModel>
#include <QStyledItemDelegate>
#include <QTableView>


class TestDelegate: public QStyledItemDelegate 

    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index)
        const Q_DECL_OVERRIDE
    
        if (index.column() == 0) 
            int value = index.model()->data(index, Qt::DisplayRole).toInt();
            QString str;
            switch (value) 
            case 1:
                str = "Hello0000";
                break;
            case 2:
                str = "World0000";
                break;
            
            if (option.state.testFlag (QStyle::State_Selected)) 
                painter->fillRect(option.rect, option.palette.highlight());
                qApp->style()->drawItemText(painter, option.rect, option.displayAlignment, option.palette, true, str, QPalette::HighlightedText);
             else  
                painter->drawText(option.rect, option.displayAlignment, str);
             
         else 
            return QStyledItemDelegate::paint(painter, option, index);
        
    

;


int main(int argc, char **argv) 
    QApplication app(argc, argv);

    QStandardItemModel model(2, 2);
    model.setHorizontalHeaderItem(0, new QStandardItem(QString("A")));
    model.setHorizontalHeaderItem(1, new QStandardItem(QString("B")));
    model.setData(model.index(0, 0, QModelIndex()), 1);
    model.setData(model.index(1, 0, QModelIndex()), 2);
    model.setItem(0, 1, new QStandardItem(QString("Hello")));
    model.setItem(1, 1, new QStandardItem(QString("World0000")));

    QTableView view;
    view.setItemDelegate(new TestDelegate);
    view.setModel(&model);
    view.resizeColumnsToContents();

    view.show();
    app.exec();

这通过将options.displayAlignment 添加到painter-&gt;drawText() 来修复文本对齐;我还在if(option.state &amp; QStyle::State_Selected) 语句中添加了附加代码,该语句根据其选择状态绘制单元格。因此,如果未选中,则文本为黑色,如果选中,则文本变为白色,背景变为蓝色。但是,我仍然无法让列扩展以适应单元格的内容,或者在单元格外部添加一条虚线,就像使用标准委托一样。

在使用我的自定义绘制方法时,有没有一种简单的方法可以保持表格视图的默认样式?

【问题讨论】:

你能提供你委托的代码吗? 里面的代码不多。它覆盖了委托的绘制方法,在方法主体内是: if (index.column() ==3) // switch 语句 //painter->drawText(option.rect, literalStr); else return QStyledItemDelegate: :paint(painter, option, index) 如果您只想用字符串值替换数字值以进行显示,您是否考虑过保留默认委托并插入代理模型?您可以继承 QIdentityProxyModel 并覆盖其 data() 方法,这可能就是您所需要的。 我没有考虑代理,没有。我是 Qt5 的新手,看到星型委托示例与我想做的事情(更改模型数据在表格中的显示)有点相似,因此我将其用作起点。基本上,我的代表将ints 更改为QStrings,就像上面一样,并使用QString.setNum(double, 'f', decimals) 设置双打的小数位数。从您的评论看来,代理模型可以做同样的事情,但要保持标准委托。它是否正确?如果是这样,您能否指出一个覆盖代理模型的 data() 方法的示例? 【参考方案1】:

委托是一种相当迂回且不必要的处理方式。我们已经有了一个可以完美绘制元素的视图,无需重做。我们只需要将修改后的数据传递给视图。因此,我们在源和视图之间插入了一个QIdentityProxyModel 视图模型。

// https://github.com/KubaO/***n/tree/master/questions/proxy-reformat-39244309
#include <QtWidgets>

class RewriteProxy : public QIdentityProxyModel 
    QMap<QVariant, QVariant> m_read, m_write;
    int m_column;
public:
    RewriteProxy(int column, QObject * parent = nullptr) :
        QIdentityProxyModelparent, m_columncolumn 
    void addReadMapping(const QVariant & from, const QVariant & to) 
        m_read.insert(from, to);
        m_write.insert(to, from);
    
    QVariant data(const QModelIndex & index, int role) const override 
        auto val = QIdentityProxyModel::data(index, role);
        if (index.column() != m_column) return val;
        auto it = m_read.find(val);
        return it != m_read.end() ? it.value() : val;
    
    bool setData(const QModelIndex & index, const QVariant & value, int role) override 
        auto val = value;
        if (index.column() == m_column) 
            auto it = m_write.find(value);
            if (it != m_write.end()) val = it.value();
        
        return QIdentityProxyModel::setData(index, val, role);
    
;

int main(int argc, char ** argv) 
    QApplication appargc, argv;
    QStandardItemModel model2,2;
    model.setData(model.index(0, 0), 1);
    model.setData(model.index(1, 0), 2);
    model.setData(model.index(0, 1), "Zaphod");
    model.setData(model.index(1, 1), "Beeblebrox");

    RewriteProxy proxy0;
    proxy.setSourceModel(&model);
    proxy.addReadMapping(1, "Hello");
    proxy.addReadMapping(2, "World");

    QTableView ui;
    ui.setModel(&proxy);
    ui.show();
    return app.exec();

【讨论】:

解决方案不适用于 QSqlQueryModel。 “没有匹配函数调用'RewriteProxy::setSourceModel (QSqlQueryModel**)'”。 @Krippy_MD06 该解决方案绝对适用于任何型号。编译器会告诉你你做错了什么。您的模型不是一个值,而是一个指针,因此您不需要使用 &amp; 运算符获取它的地址。不要只是盲目地复制代码,试着去了解发生了什么,涉及的类型是什么,以及你的代码有什么不同。 我试过它返回“'key1 如果您要询问有关代码的问题,最好提供代码!代码按原样工作,无需更改。您可以从 github 下载该项目,在 Qt Creator 中打开它,然后运行它。如果你修改它并且它不再工作,你应该发布一个单独的问题。 我现在已经想出了如何实现它,尽管我废弃了它的 map 部分,而 data 方法完成了所有逻辑。谢谢。

以上是关于Qt5 子类化 QStyledItemDelegate 格式的主要内容,如果未能解决你的问题,请参考以下文章

QT5 插槽未在子类中调用

Delphi的子类化控件消息, 消息子类化

子类化一个 NSManagedObject 子类

QT5 QTreeView添加右键菜单

CAsyncSocket 没有子类化?

子类化导航控制器有啥用? [关闭]