Qt中项目视图的默认委托的机制是什么?

eri*_*ric 7 qt pyqt pyside

精简版

使用的默认委托是QTreeView什么?特别是我想找到它的paint()方法?

更长的版本

我是一个Python用户(Pyside/PyQt),并使用自定义委托来重新创建一些功能QTreeView.因此,我试图找到在a中使用的默认委托和绘制方法QTreeView.更好的是解释它是如何工作的.

交叉岗位

我在Qt中心发布了同样的问题(http://www.qtcentre.org/threads/64458-Finding-default-delegate-for-QTreeView?).

eri*_*ric 13

TL;博士

所有项目视图的默认委托是QStyledItemDelegate.它的paint()方法调用drawControl(),定义qcommonstyle.cpp,绘制每个项目.因此,仔细阅读qcommonstyle.cpp每个项目如何绘制的细节.


长篇答案

那些喜欢简洁的人应该阅读上面的tl; dr项目视图中样式文档.如果你仍然被困(许多Python用户可能会),那么这个答案的其余部分应该有所帮助.

I.默认委托是QStyledItemDelegate

QStyledItemDelegate是视图中项目的默认委托.这在Qt的模型/视图编程概述中清楚地说明:

从Qt 4.4开始,默认委托实现由QStyledItemDelegate提供,并且这被Qt的标准视图用作默认委托.

对文档QStyledItemDelegate提供更多的细节:

当在Qt项目视图中显示来自模型的数据时,例如QTableView,各个项目由代表绘制.此外,在编辑项目时,它会提供编辑器窗口小部件,在编辑过程中将其置于项目视图的顶部.QStyledItemDelegate是所有Qt项目视图的默认委托,并在创建它们时安装在它们上.

总而言之,如果您想了解任何项目视图的基础机制(不仅仅是树视图,还有表格和列表),请研究 QStyledItemDelegate.

paint()方法在脱氧代码库的第419行qstyleditemdleegate.cpp定义.让我们看看最后两行:

    QStyle *style = widget ? widget->style() : QApplication::style();
    style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
Run Code Online (Sandbox Code Playgroud)

这里发生了两件事.首先,设置样式 - 通常为QApplication.style().其次,drawControl()调用该样式的方法来绘制正在绘制的项目.就是这样.这就是最后一行QStyledItemDelegate.paint()!

因此,如果你真的想弄清楚默认委托是如何绘画的,我们实际上必须研究这种风格,即做所有真实的工作.这就是我们在本文档的其余部分中要做的事情.

II.QStyle:什么赋予代表其风格?

当使用Qt显示任何内容时,它会根据您在实例化时以特定于系统的方式选择的某种样式进行绘制QApplication.来自QStyle的文档:

QStyle类是一个抽象基类,它封装了GUI的外观.Qt包含一组QStyle子类,它们模拟Qt支持的不同平台的样式(QWindowsStyle,QMacStyle,QMotifStyle等).默认情况下,这些样式内置于QtGui库中.

在Qt中,您将找到样式N的源代码src/gui/styles/N.cpp.

每种样式都包含用于在GUI中绘制所有内容的基本操作的实现,从树视图到下拉菜单.标准样式,例如QWindowsStyle,继承了他们的大部分方法QCommonStyle.每种特定风格通常仅包括与该共同基础的微小偏差.因此,仔细研究qcommonstyle.cpp将揭示Qt开发人员发现用于绘制GUI所有部分的基本功能.它的重要性很难夸大.

在下文中,我们将检查与绘制视图项相关的部分.

III.QStyle.drawControl():对代表执行内窥镜检查

如上所述,理解绘制视图的基本机制需要检查drawControl()in qcommonstyle.cpp的实现,该实现从第1197行开始.请注意,当我在没有提及文件名的情况下引用行号时,按照惯例,我指的是到qcommonstyle.cpp在doxygenated代码库.

QStyle.drawControl()文档很有启发性:

QStyle.drawControl(元素,选项,画家)

参数:

  • element - QStyle.ControlElement

  • 选项 - QtGui.QStyleOption

  • 画家 - PySide.QtGui.QPainter

使用提供的画家使用选项指定的样式选项绘制给定元素.... option参数是指向QStyleOption对象的指针 ,包含绘制所需元素所需的所有信息.

调用者drawControl()通过传递一个QStyle.ControlElement标志来告诉它尝试绘制的元素类型.控件元素是窗口的高级组件,用于向用户显示信息:复选框,按钮和菜单项.这里列举了所有控件元素:

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

回想一下在调用中发送的控制元素QStyledItemDelegate.paint()CE_ItemViewItem,这只是一个要在项目视图中显示的项目.内QCommonStyle.drawControl(),CE_ItemViewItem案件从第2153行开始.让我们深入研究.

A. subElementRect():大小很重要

确保每个项目的大小和布局正确是关键.这是第一件事drawControl()计算.要获取此信息,它将调用subElementRect()(在第2313行定义,并在第2158行首次调用).例如,我们有:

QRect textRect = subElementRect(SE_ItemViewItemText, vopt, widget);
Run Code Online (Sandbox Code Playgroud)

QStyle.SubElement在这种情况下,第一个参数是一个标志SE_ItemViewItemText.样式子元素表示控制元素的组成部分.视图中的每个项目都有三个可能的子元素:复选框,图标和文本; 显然,SE_ItemViewItemText子元素代表文本.这里列举了所有可能的子元素:

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

subElementRect()方法包含子元素枚举中的所有案例:我们的SE_ItemViewItemText案例从第3015行开始.

请注意,subElementRect()返回a QRect,它使用整数精度定义平面中的矩形,并且可以使用四个整数(left,top,width,height)构造.例如r1 = QRect(100, 200, 11, 16).对于子元素,它为其大小以及将在视口中绘制的x,y位置指定.

subElementRect()实际调用viewItemLayout()(在第999行定义)来完成实际工作,这是一个两步过程.首先,使用viewItemLayout()计算子元素的高度和宽度viewItemSize().其次,它计算子元素的x和y位置.让我们依次考虑这些操作.

1. viewItemLayout():计算子元素的宽度和高度

从第1003行开始,viewItemLayout()调用viewItemSize()(在第838行定义),计算子元素所需的高度和宽度.

viewItemSize()获取标题栏高度等内容的默认数字在哪里?这是像素度量的省.像素度量是依赖于样式的大小,由单个像素值表示.例如,Style.PM_IndicatorWidth返回复选框指示符的宽度,并QStyle.PM_TitleBarHeight返回应用程序样式的标题栏高度.QStyle.PixelMetric这里列举了所有不同的s:

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

您可以使用QStyle.pixelMetric()检索给定像素度量的值,该值经常使用viewItemSize().第一个输入pixelMetric()QStyle.PixelMetric枚举中的s之一.在4367行qcommonstyle.cpppixelMetric()启动实施中.

例如,viewItemSize()使用以下内容计算复选框的宽度和高度(如果需要):

return QSize(proxyStyle->pixelMetric(QStyle::PM_IndicatorWidth, option, widget),
    proxyStyle->pixelMetric(QStyle::PM_IndicatorHeight, option, widget));
Run Code Online (Sandbox Code Playgroud)

请注意,pixelMetric()它不仅仅用于viewItemSize(),而且无处不在.它用于计算许多GUI元素的度量属性,从窗口边框到图标,并且贯穿始终qcommonstyle.cpp.基本上,每当您需要知道样式使用多少像素来处理大小不变的图形元素时(如复选框),样式将调用像素度量.

2. viewItemLayout():Rubik的Cube子元素获取x/y位置

第二部分viewItemLayout()致力于组织刚刚计算出宽度和高度的子元素的布局.也就是说,它需要找到它们的x和y位置来完成将值填充到QRects中textRect.子元素的组织方式取决于视图的一般设置(例如,是左右还是左右).因此,viewItemLayout()根据这些因素计算每个子元素的最终静止位置.

如果您需要有关如何sizeHint()在自定义委托中重新实现的线索,subElementRect()可能是一个有用的提示和技巧来源.特别是,viewItemSize()当您希望自定义视图与默认视图紧密匹配时,可能包含您可能想要查询的有用花絮和相关像素指标.

一旦QRect为文本,图标和复选框子元素计算了s,drawControl()继续前进,最后开始绘制.

B.为背景着色:变得原始

首先,为项目填充背景(第2163行):

drawPrimitive(PE_PanelItemViewItem, option, painter);
Run Code Online (Sandbox Code Playgroud)

这与调用非常相似QStyle.drawControl(),但第一个参数不是控制元素,而是原始元素(PE).就像drawControl()控制所有更高级别控制元素的绘制的大型蔓延方法一样,QStyle.drawPrimitive()在GUI中绘制大多数较低级别的原始图形元素(例如项目视图中的矩形背景).这些低级单元,即原始元素,在此列举:

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

文档说,"原始元素是一个常见的GUI元素,例如复选框指示符或按钮斜角." 例如,PE_PanelItemViewItem原始元素是"[T]项目视图中项目的背景."

每种风格都必须有一个实现drawPrimitive(),我们的开始在第140行.在那里,你可以非常详细地发现它如何执行其原始的绘画操作.对于如何paint()在自定义委托中实际使用命令,这是一个有用的提示源.

的重新实现QStyle.drawPrimitive()PE_PanelItemViewItem情况下,开始于线773它首先基于所述项的状态选择适当的背景颜色.它通过查询项目来完成此操作QStyle.StateFlag.该option.state包含描述当时的项目状态的状态标志(是启用,选择正在编辑等?).这些状态不只是在后端使用,但在重新实现QStyledItemDelegate.paint()自定义委托时,您可能需要使用它.你可以在QStyle.StateFlag这里找到s 的枚举:

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

选择正确的颜色后,drawPrimitive()然后使用QPainter.fillRect()该颜色填充适当的区域(第786行):

p->fillRect(vopt->rect, vopt->backgroundBrush);
Run Code Online (Sandbox Code Playgroud)

QPainter.fillRect() 在为自己实现自定义委托时,这是一种非常有用的方法.

在处理背景之后,drawControl()然后继续完成绘制项目,从复选框(第2165行)开始,然后是图标(第2185行),最后是文本(第2194行).我们不会详细讨论这个问题,但我将简要讨论如何绘制文本.

C.绘制文本:叛徒

首先,项目的状态用于指定文本的颜色.这使用QStyle.StateFlags刚才讨论过的.然后drawControl()将文本绘图职责踢出自定义方法viewItemDrawText()(在第921行定义):

viewItemDrawText(painter, vopt, textRect);
Run Code Online (Sandbox Code Playgroud)

此方法将上面描述的画家,选项和文本矩形(部分A)作为参数.请注意,option参数非常重要:它是一个QStyleOption类,通过该类在样式中传递内容(它包括图标,checkstate和文本属性).

从选项中提取文本后,将其合并到QtGui.QTextLine添加到a中的文本中QtGui.QTextLayout.最终,省略(即,使用省略号,取决于自动换行设置)文本由QPainter.drawText()(见第983行)绘制,这是Qt的原始绘画操作之一.

坦率地说,很多事情viewItemDrawText()都花在处理自动换行上.这是我们开始深入了解Qt的一些内容,没有任何Python用户可以看到,更不用说修补.例如,它使用QStackTextEngine该类.我鼓励你使用谷歌'qstacktextengine pyqt'来了解这对Python用户来说是多么不频繁.如果您对此感兴趣,请注意!

IV.摘要

如果你想访问底层涂装机械默认的委托,你最终将研究实施 QStyle.drawControl()qcommonstyle.cpp,一个文件的6000行兽.此练习对于确定用于计算大小的精确过程以及绘制项目包含的原始图形元素非常有用.然而,有时,这种野兽可能是彻头彻尾的恐怖和无益,就像处理自动换行一样.在这些情况下,您可能只需要为代理找出所需功能的自定义实现.

最后,既然我们已经看到了关于工作原理的全局视图,我们可以更好地理解文档的有用性QStyle,特别是项目视图中的样式部分.在那里,我们找到了以下启发性的爱情金块:

视图中的项目绘制由代理执行.Qt的默认委托,QStyledItemDelegate也用于计算项目(及其子元素)的边界矩形...当 QStyledItemDelegate绘制其项目时,它绘制CE_ItemViewItems...当实现样式以自定义项目视图的绘制时,您需要检查QCommonStyle(和您的样式继承的任何其他子类).这样,您可以找出已经绘制了其他样式元素的方式和方式,然后您可以重新实现应该以不同方式绘制的元素的绘制.

所以基本上原帖的回答是"他们说的是什么."