Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
369 views
in Technique[技术] by (71.8m points)

pyqt - What are the mechanics of the default delegate for item views in Qt?

Short version

What is the default delegate used by QTreeView? In particular I am trying to find its paint() method?

Longer version

I am a Python user (Pyside/PyQt), and am using a custom delegate to recreate some of the functionality of QTreeView. Hence, I am trying to find the default delegate, and paint method, that is used in a QTreeView. Even better would be an explanation of how it works.

Cross post

I posted the same question at Qt Centre (http://www.qtcentre.org/threads/64458-Finding-default-delegate-for-QTreeView?).

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

tl;dr

The default delegate for all item views is the QStyledItemDelegate. Its paint() method invokes drawControl(), defined in qcommonstyle.cpp, to draw each item. Hence, peruse qcommonstyle.cpp for the nitty-gritty details about how each item is drawn.


Long-form answer

Those who prefer brevity should read the tl;dr above and the documentation on Styles in Item Views. If you are still stuck (and many Python users probably will be), the rest of this answer should help.

I. The default delegate is QStyledItemDelegate

The QStyledItemDelegate is the default delegate for items in views. This is stated clearly in Qt's Model/View Programming overview:

Since Qt 4.4, the default delegate implementation is provided by QStyledItemDelegate, and this is used as the default delegate by Qt's standard views.

The docs for QStyledItemDelegate provide a little more detail:

When displaying data from models in Qt item views, e.g., a QTableView, the individual items are drawn by a delegate. Also, when an item is edited, it provides an editor widget, which is placed on top of the item view while editing takes place. QStyledItemDelegate is the default delegate for all Qt item views, and is installed upon them when they are created.

In sum, if you want to understand the underlying mechanics of any of the item views (not just a tree view, but tables and lists too), study QStyledItemDelegate.

The paint() method in qstyleditemdleegate.cpp is defined on line 419 of the doxygenated code base. Let's look at the last two lines:

    QStyle *style = widget ? widget->style() : QApplication::style();
    style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);

Two things are happening here. First, the style is set -- typically to QApplication.style(). Second, that style's drawControl() method is invoked to draw the item being painted. And that's it. That's literally the final line of QStyledItemDelegate.paint()!

Hence, if you really want to figure out how the default delegate is painting things, we've actually got to study the style, which is doing all the real work. That's what we'll do in the rest of this document.

II. QStyle: what gives the delegate its style?

When anything is displayed using Qt, it is drawn according to some style that was chosen, in a system-specific way, when you instantiated QApplication. From the docs for QStyle:

The QStyle class is an abstract base class that encapsulates the look and feel of a GUI. Qt contains a set of QStyle subclasses that emulate the styles of the different platforms supported by Qt (QWindowsStyle, QMacStyle, QMotifStyle, etc.). By default, these styles are built into the QtGui library.

In Qt, you will find the source code for style N in src/gui/styles/N.cpp.

Each style contains the implementation of the elementary operations used for drawing everything in a GUI, from tree views to dropdown menus. The standard styles, such as QWindowsStyle, inherit most of their methods from QCommonStyle. Each particular style typically includes just minor deviations from that common base. Hence, a close study of qcommonstyle.cpp will reveal the base functionality that Qt developers found useful for painting all the parts of a GUI. Its importance is hard to overstate.

In what follows, we'll examine the parts relevant for drawing view items.

III. QStyle.drawControl(): performing endoscopy on the delegate

As mentioned above, understanding the basic mechanics of drawing a view requires examining the implementation of drawControl() in qcommonstyle.cpp, an implementation that starts on line 1197. Note in what follows, when I refer to a line number without mentioning a file name, by convention I am referring to qcommonstyle.cpp in the doxygenated code base.

The documentation for QStyle.drawControl() is instructive:

QStyle.drawControl(element, option, painter)

Parameters:

  • element – QStyle.ControlElement

  • option – QtGui.QStyleOption

  • painter – PySide.QtGui.QPainter

Draws the given element with the provided painter with the style options specified by option.... The option parameter is a pointer to a QStyleOption object and contains all the information required to draw the desired element.

The caller tells drawControl() what type of element it is trying to draw by passing it a QStyle.ControlElement flag. Control elements are higher-level components of a window that display information to the user: things like checkboxes, pushbuttons, and menu items. All of the control elements are enumerated here:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#ControlElement-enum

Recall the control element sent in the call to QStyledItemDelegate.paint() was CE_ItemViewItem, which is simply an item to be displayed inside an item view. Within QCommonStyle.drawControl(), the CE_ItemViewItem case starts on line 2153. Let's dig in.

A. subElementRect(): size matters

It is key to get the size and layout of each item right. This is the first thing drawControl() calculates. To get this information, it invokes subElementRect() (defined on line 2313, and first called on line 2158). For example, we have:

QRect textRect = subElementRect(SE_ItemViewItemText, vopt, widget);

The first argument is a QStyle.SubElement flag, in this case SE_ItemViewItemText. Style subelements represent constituent parts of control elements. Each item in a view has three possible subelements: the checkbox, the icon, and text; obviously, the SE_ItemViewItemText subelement represents the text. All possible subelements are enumerated here:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#SubElement-enum

The subElementRect() method contains all the cases in the subelement enumeration: our SE_ItemViewItemText case starts on line 3015.

Note that subElementRect() returns a QRect, which defines a rectangle in the plane using integer precision, and can be constructed with four integers (left, top, width, height). For instance r1 = QRect(100, 200, 11, 16). This specifies, for a subelement, its size as well as the x,y position where it will be painted in the viewport.

subElementRect() actually calls viewItemLayout() (defined on line 999) to do the real work, which is a two-step process. First, viewItemLayout() calculates the height and width of subelements using viewItemSize(). Second, it calculates the x and y position of the subelement. Let's consider each of these operations in turn.

1. viewItemLayout(): calculating the width and height of subelements

Starting on line 1003, viewItemLayout() calls viewItemSize() (defined on line 838), which calculates the height and width needed for a subelement.

Where does viewItemSize() get the default numbers for things like the height of the title bar? This is the province of the pixel metric. A pixel metric is a style-dependent size represented by a single pixel value. For instance, Style.PM_IndicatorWidth returns the width of a check box indicator, and QStyle.PM_TitleBarHeight returns the title bar height for your application's style. All the different QStyle.PixelMetrics are enumerated here:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#PixelMetric-enum

You can retrieve the value of a given pixel metric using QStyle.pixelMetric(), which is used a lot in viewItemSize(). The first input of pixelMetric() is one of the QStyle.PixelMetrics in the enumeration. In qcommonstyle.cpp the implementation of pixelMetric() starts on line 4367.

For instance, viewItemSize() calculates the width and height of the checkbox (if needed) using the following:

return QSize(proxyStyle->pixelMetric(QStyle::PM_IndicatorWidth, option, widget),
    proxyStyle->pixelMetric(QStyle::PM_IndicatorHeight, option, widget));

Note that pixelMetric() is not just used in viewItemSize(), but is ubiquitous. It is used to calculate metric properties of many GUI elements, from window borders to icons, and is peppered throughout qcommonstyle.cpp. Basically, whenever you need to know how many pixels your style uses for some graphical element whose size doesn't change (like a checkbox), the style will call on pixel metrics.

2. viewItemLayout(): Rubik's Cube the subelements to get x/y positions

The second part of viewItemLayout() is devoted to organizing the layout of the subelements whose width and height were just calculated. That is, it needs to find their x and y positions to finish filling the values into QRects such as textRect. The subelements will be organized differently depending on the general setup of the view (e.g., whether it is right-left or left-right oriented). Hence, viewItemLayout() calculates the final resting position of each subelement depending on such factors.

If you ever need clues about how to reimplement sizeHint() in a custom delegate, subElementRect() could be a useful source of tips and tricks. In particular, viewItemSize() is likely to contain useful tidbits and relevant pixel metrics you might want to query when you want a custom view to closely match the default.

Once the QRects are calculated for the text, icon, and checkbox subelements, drawControl() mov


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...