Model View alter display text
-
I have a model (
QStandardItemModel-derived) and a view (QTableView-derived). I wish to alter the displayed text for certain rows which hold a numeric to show as percentage, e.g.30->30%. (The principle here would apply to any other transformation, e.g.$30or(30)for accountancy negative numbers, and is not uncommon in tables displaying, say, numeric values)I used to accomplish this by overriding
data(const QModelIndex &index, int role = Qt::DisplayRole) consthaving it go essentially:if (role == Qt::DisplayRole) if (index.row() == percentRow) { int val = Base::data(index, role).toInt(); return QString("%1%").arg(val); }i.e. making it return the string I wanted instead of the numeric. This was easy and I could do it in 30 seconds.
I was told in no uncertain terms by experts here that this was wicked, I must leave the data type returned as-was for all sorts of reasons, not convert to string here. I should use
QStyledItemDelegatefor this. Fair enough.I come to do that now and was assuming I could use
QString QStyledItemDelegate::displayText(const QVariant &value, const QLocale &locale) const, which would not have been too bad. However I now see that does not convey theQModelIndex, so I cannot select by row/column.Which means I seem to be left with overriding
void QStyledItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const, and doing quite a bit of work there (converting to string and dealing with painters).Is this really what I have to do to alter the string displayed for a number? Seems crazy to me to be going down into painters :( There isn't anything I can do via the
QStyleOptionViewItem &option, is there? Nor can I get adisplayText()override to know about theQModelIndex, can I? -
initStyleOptionis also virtual so you can do the chage directly there with minimal effort.class ListviewDelegate : public QStyledItemDelegate{ Q_OBJECT Q_DISABLE_COPY_MOVE(ListviewDelegate) public: explicit ListviewDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {} protected: void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override{ QStyledItemDelegate::initStyleOption(option,index); if (index.row() == percentRow) option->text += QChar('%'); } };if you need to do anything more fancy to the text you can just use
option->text = doSomethingFancy(displayText(index.data(Qt::DisplayRole), option->locale)); -
@Bonnie
I wish! I got beyond shouted out (nicely) by expert people here saying I should be boiled in oil :) Not quite, but they were very firm on "don't change the return type ofQt::DisplayRole, it isn't intended for this at all, and the only time it should differ fromQt::EditRoleis as an example where you are doing a spreadsheet and you have the display role showing a calculated result number while the edit role is the text of a formula". A quite different type of situation. For output like this the way to do is through a item delegate, only. Model code must not concern with output format. I posted an explicit question about just this and gracefully accepted their answers, and promised I would adhere to it next time I wrote code... :)Lemme see if I can find it:
-
Hi
Its not that much painter code to append %void ListviewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem nonConstOption(option); initStyleOption(&nonConstOption, index); int val = 66;// get from model nonConstOption.text = QString("%1%").arg(val); QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &nonConstOption, painter, nullptr); } -
Hi
Its not that much painter code to append %void ListviewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem nonConstOption(option); initStyleOption(&nonConstOption, index); int val = 66;// get from model nonConstOption.text = QString("%1%").arg(val); QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &nonConstOption, painter, nullptr); }@mrjj
Thank you for supplying this. Looks to me like some damn hairy code to append a%to a number :)I need to look through the docs for this tomorrow to understand what is going on. Since I won't want to interfere with whatever
paint()does or does not do for all other cases, I guess I'll want something like:if (index.row() == percentRow) { QVariant v = data(index); if (v.somethingAboutTypeIsNumber()) { ( your code, using QString("%1%").arg(v);, but I don't think that accepts a QVariant ) return; } } QStyledItemDelegate::paint(painter, option, index);Good grief, this is getting hard for one darned
%character...! :( -
initStyleOptionis also virtual so you can do the chage directly there with minimal effort.class ListviewDelegate : public QStyledItemDelegate{ Q_OBJECT Q_DISABLE_COPY_MOVE(ListviewDelegate) public: explicit ListviewDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {} protected: void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override{ QStyledItemDelegate::initStyleOption(option,index); if (index.row() == percentRow) option->text += QChar('%'); } };if you need to do anything more fancy to the text you can just use
option->text = doSomethingFancy(displayText(index.data(Qt::DisplayRole), option->locale)); -
initStyleOptionis also virtual so you can do the chage directly there with minimal effort.class ListviewDelegate : public QStyledItemDelegate{ Q_OBJECT Q_DISABLE_COPY_MOVE(ListviewDelegate) public: explicit ListviewDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {} protected: void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override{ QStyledItemDelegate::initStyleOption(option,index); if (index.row() == percentRow) option->text += QChar('%'); } };if you need to do anything more fancy to the text you can just use
option->text = doSomethingFancy(displayText(index.data(Qt::DisplayRole), option->locale));