PDA

View Full Version : Finding default delegate for QTreeView?



neuronet
29th November 2015, 21:07
tl;dr
I am a PyQt user (Python binding of Qt), and am trying to figure out how Qt natively draws trees in QTreeView, as I am trying to recreate it using a custom delegate (QStyledItemDelegate applied to my view) just for instructive purposes. More specifically, I am viewing a QStandardItemModel using a QTreeView, and want to see the guts of the delegate that is used by default, but am having trouble finding the default 'paint' method. Is there a link to it?


Details
I am not very good at C++ (I'm a Python guy), and am having trouble figuring it out: I have been looking over the code at qtreeview.cpp (https://github.com/radekp/qt/blob/master/src/gui/itemviews/qtreeview.cpp) but haven't actually found anything explicit where, for instance, the text is drawn corresponding to a particular item.

Line 1389 has the `drawTree` method, which calls drawRow on line 1442:

drawRow(painter, option, viewItems.at(i).index);
drawRow is defined starting on line 1473 (https://github.com/radekp/qt/blob/master/src/gui/itemviews/qtreeview.cpp#L1473). Therein, I think we are reaching the guts, e.g., line 1679 (https://github.com/radekp/qt/blob/master/src/gui/itemviews/qtreeview.cpp#L1679):

d->delegateForIndex(modelIndex)->paint(painter, opt, modelIndex);
This suggests to me that that the delegate for the item associated with `modelIndex` is defined within `delegateForIndex`. But I have now hit the limits of my sleuthing, and am having trouble finding where this delegate, and its paint operation in particular, is defined.

Any hints?

============
Cross posted:
http://stackoverflow.com/questions/33990029/what-is-the-default-delegate-of-qtreeview-in-qt

d_stranz
30th November 2015, 02:53
The actual default delegate and its painting methods are defined by the style in use; if you look in your qtbase/src/widgets/styles directory, you'll see files for all sorts of styles. You'll need to pick one that looks like it might be the style used by your platform, and search for "CE_ItemViewItem". You should find at least one occurrence in a drawControl method for the style (or its base class).

I do not think this is the code that draws decorations on the tree, though - the node indicators, lines, etc. It's just the code that draws the index items themselves.

You'll no doubt find the code ugly and confusing, but that's the nature of the beast when you are writing a platform-independent user interface toolkit.

neuronet
3rd December 2015, 05:30
It seems the following snippet from qcommonstyle.cpp (cep.xray.aps.anl.gov/software/qt4-x11-4.8.6-browser/d6/d68/qcommonstyle_8cpp_source.html) will repay careful study. What do you c++/qt gurus think? Is this the crux of the answer to my question?


#ifndef QT_NO_ITEMVIEWS
case CE_ItemViewItem:
if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(opt)) {
p->save();
p->setClipRect(opt->rect);

QRect checkRect = subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget);
QRect iconRect = subElementRect(SE_ItemViewItemDecoration, vopt, widget);
QRect textRect = subElementRect(SE_ItemViewItemText, vopt, widget);

// draw the background
proxy()->drawPrimitive(PE_PanelItemViewItem, opt, p, widget);

// draw the check mark
if (vopt->features & QStyleOptionViewItemV2::HasCheckIndicator) {
QStyleOptionViewItemV4 option(*vopt);
option.rect = checkRect;
option.state = option.state & ~QStyle::State_HasFocus;

switch (vopt->checkState) {
case Qt::Unchecked:
option.state |= QStyle::State_Off;
break;
case Qt::PartiallyChecked:
option.state |= QStyle::State_NoChange;
break;
case Qt::Checked:
option.state |= QStyle::State_On;
break;
}
proxy()->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &option, p, widget);
}

// draw the icon
QIcon::Mode mode = QIcon::Normal;
if (!(vopt->state & QStyle::State_Enabled))
mode = QIcon::Disabled;
else if (vopt->state & QStyle::State_Selected)
mode = QIcon::Selected;
QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off;
vopt->icon.paint(p, iconRect, vopt->decorationAlignment, mode, state);

// draw the text
if (!vopt->text.isEmpty()) {
QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled
? QPalette::Normal : QPalette::Disabled;
if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active))
cg = QPalette::Inactive;

if (vopt->state & QStyle::State_Selected) {
p->setPen(vopt->palette.color(cg, QPalette::HighlightedText));
} else {
p->setPen(vopt->palette.color(cg, QPalette::Text));
}
if (vopt->state & QStyle::State_Editing) {
p->setPen(vopt->palette.color(cg, QPalette::Text));
p->drawRect(textRect.adjusted(0, 0, -1, -1));
}

d->viewItemDrawText(p, vopt, textRect);
}

// draw the focus rect
if (vopt->state & QStyle::State_HasFocus) {
QStyleOptionFocusRect o;
o.QStyleOption::operator=(*vopt);
o.rect = proxy()->subElementRect(SE_ItemViewItemFocusRect, vopt, widget);
o.state |= QStyle::State_KeyboardFocusChange;
o.state |= QStyle::State_Item;
QPalette::ColorGroup cg = (vopt->state & QStyle::State_Enabled)
? QPalette::Normal : QPalette::Disabled;
o.backgroundColor = vopt->palette.color(cg, (vopt->state & QStyle::State_Selected)
? QPalette::Highlight : QPalette::Window);
proxy()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, p, widget);
}

p->restore();
}
break;

Not exactly sure what 'd' is, but rawing the text, for instance, seems it would go as follows:


void QCommonStylePrivate::viewItemDrawText(QPainter *p, const QStyleOptionViewItemV4 *option, const QRect &rect) const
{
const QWidget *widget = option->widget;
const int textMargin = proxyStyle->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1;

QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding
const bool wrapText = option->features & QStyleOptionViewItemV2::WrapText;
QTextOption textOption;
textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap);
textOption.setTextDirection(option->direction);
textOption.setAlignment(QStyle::visualAlignment(op tion->direction, option->displayAlignment));
QTextLayout textLayout;
textLayout.setTextOption(textOption);
textLayout.setFont(option->font);
textLayout.setText(option->text);

viewItemTextLayout(textLayout, textRect.width());

QString elidedText;
qreal height = 0;
qreal width = 0;
int elidedIndex = -1;
const int lineCount = textLayout.lineCount();
for (int j = 0; j < lineCount; ++j) {
const QTextLine line = textLayout.lineAt(j);
if (j + 1 <= lineCount - 1) {
const QTextLine nextLine = textLayout.lineAt(j + 1);
if ((nextLine.y() + nextLine.height()) > textRect.height()) {
int start = line.textStart();
int length = line.textLength() + nextLine.textLength();
const QStackTextEngine engine(textLayout.text().mid(start, length), option->font);
elidedText = engine.elidedText(option->textElideMode, textRect.width());
height += line.height();
width = textRect.width();
elidedIndex = j;
break;
}
}
if (line.naturalTextWidth() > textRect.width()) {
int start = line.textStart();
int length = line.textLength();
const QStackTextEngine engine(textLayout.text().mid(start, length), option->font);
elidedText = engine.elidedText(option->textElideMode, textRect.width());
height += line.height();
width = textRect.width();
elidedIndex = j;
break;
}
width = qMax<qreal>(width, line.width());
height += line.height();
}

const QRect layoutRect = QStyle::alignedRect(option->direction, option->displayAlignment,
QSize(int(width), int(height)), textRect);
const QPointF position = layoutRect.topLeft();
for (int i = 0; i < lineCount; ++i) {
const QTextLine line = textLayout.lineAt(i);
if (i == elidedIndex) {
qreal x = position.x() + line.x();
qreal y = position.y() + line.y() + line.ascent();
p->save();
p->setFont(option->font);
p->drawText(QPointF(x, y), elidedText);
p->restore();
break;
}
line.draw(p, position);
}
}

Am I in the right ballpark, folks? I feel I am finally getting close after many days of sleuthing.

neuronet
7th December 2015, 21:00
am posting summary but still working on it sorry will post later...add it here....couldn't figure out how to delete this post, I had added it but didn't like it so cut it and will add back what I had later....

neuronet
8th December 2015, 03:46
So in sum, yes, it is all about qcommonstyle.cpp and the code snippets I posted above (with some additional stuff too). In some caseis it is not very useful to track the delegate through to its elementary painting operations, but in some cases it is, and the only way to know for sure is to do it for each subelement of interest.

I'll be posting a very long answer along those lines over at Stack Overflow at the link in OP.

neuronet
16th December 2015, 16:45
So, my answer ended up being about 6 pages long, and I posted it at STack Overflow:
http://stackoverflow.com/a/34215918/1886357


Here is the "summary" section of the answer:

If you want to access the underlying painting mechanics for the default delegate, you will end up studying the implementation of `QStyle.drawControl()` in `qcommonstyle.cpp`, a 6000-line beast of a file. This exercise can be very helpful for figuring out the exact procedures used to calculate sizes and draw the primitive graphical elements that items contain. Sometimes, however, this beast can be downright scary and unhelpful, like when it comes to dealing with word wrap. In those cases, you will probably just have to figure out a custom implementation of the desired functionality for your delegate.

Finally, now that we've seen a big-picture view of how things work, we are finally ready to appreciate how helpful the documentation for `QStyle` is, in particular the section Styles in Item Views. There, we find the following revelatory love nugget that nicely sums things up:

> The painting of items in views is performed by a delegate. Qt’s
> default delegate, `QStyledItemDelegate`, is also used for calculating
> bounding rectangles of items (and their sub-elements) …When
> `QStyledItemDelegate` paints its items, it draws `CE_ItemViewItems`…When
> implementing a style to customize drawing of item views, you need to
> check the implementation of `QCommonStyle` (and any other subclasses
> from which your style inherits). This way, you find out which and how
> other style elements are already painted, and you can then reimplement
> the painting of elements that should be drawn differently.

So basically the answer to the original post is, "What they said."

d_stranz
17th December 2015, 01:44
trying to figure out how Qt natively draws trees in QTreeView, as I am trying to recreate it using a custom delegate (QStyledItemDelegate applied to my view) just for instructive purposes.

So which do you feel, instructed or beaten into submission? :cool:

neuronet
17th December 2015, 14:50
So which do you feel, instructed or beaten into submission? :cool:

Yes. :) I learned more about Qt in the past two weeks than in the past six months, I think, but also feel like I got pulled through a woodchipper.