2
votes

I am developing a GUI for a SQLite database in Qt5. I use QSqlQueryModel and QTableView for storing and displaying the data.

I then created a custom delegate to replace the numeric values of certain columns with their literals in the table view (e.g. 1 = "Hello", 2 = "World") using a switch statement.

The delegate displays the data as it should and works functionally. However, the columns that the custom delegate paints over have a different format compared to the default paint method of QStyledItemDelegate. The values are up in the top left rather than centre left, the altered column no longer automatically expands the column to display the full values, and the cells in column do not turn blue or have the dotted outline when selected.

I created this example program:

#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();
}

This fixes the text alignment by adding options.displayAlignment to painter->drawText(); I have also added additional code in the if(option.state & QStyle::State_Selected) statement that paints the cell according to its selection state. So if it isn't selected the text is black, if it is the text turns white and the background blue. However I still cannot get the columns to expand to fit the cells' content or add a dotted line around the outside of the cell as it does with the standard delegate.

Is there a simple way to maintain the default style of the table view when using my custom paint method?

1
Can you provide code of your delegate?Kirill Chernikov
There isn't much code in it. It overrides the paint method for delegates, within the method body is: if (index.column() ==3) {// switch statement // painter->drawText(option.rect, literalStr);} else { return QStyledItemDelegate::paint(painter, option, index)mrwolf
If all you want to do is to replace numeric values with string values for display, have you considered keeping the default delegate and instead interposing a proxy model? You can subclass QIdentityProxyModel and override its data() method, which may be all you need.Toby Speight
I didn't consider proxy, no. I'm new to Qt5 and saw that the star delegate example was vaguely similar to what I wanted to do (change the display of a model's data in a table) so I used it as my starting point. Basically, my delegate changes ints to QStrings, like above, and sets the number of decimals for doubles using QString.setNum(double, 'f', decimals) . From your comment it sounds like a proxy model could do the same thing but maintain the standard delegate. Is this correct? If so, could you point me towards an example of overriding a proxy model's data() method?mrwolf

1 Answers

0
votes

The delegate is a rather circuitous and unnecessary way of going about it. We already have a view that paints the elements perfectly fine, no need to redo that. We only need to pass modified data to the view. Thus we insert a QIdentityProxyModel viewmodel between the source and the view.

// https://github.com/KubaO/stackoverflown/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) :
        QIdentityProxyModel{parent}, m_column{column} {}
    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 app{argc, argv};
    QStandardItemModel model{2,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 proxy{0};
    proxy.setSourceModel(&model);
    proxy.addReadMapping(1, "Hello");
    proxy.addReadMapping(2, "World");

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