Какова механика делегата по умолчанию для представлений элементов в Qt?

короткая версия

какой делегат по умолчанию используется QTreeView? В частности, я пытаюсь найти его paint() способ?

версия

Я пользователь Python (Pyside/PyQt) и использую пользовательский делегат для воссоздания некоторых функций QTreeView. Следовательно, я пытаюсь найти делегат по умолчанию и метод paint, который используется в QTreeView. Еще лучше было бы объяснение того, как это завод.

кросс-пост

Я отправил тот же вопрос в центр Qt (http://www.qtcentre.org/threads/64458-Finding-default-delegate-for-QTreeView?).

1 ответов


tl; dr

делегата по умолчанию для всех представлений элемент QStyledItemDelegate. Его paint() метод вызывает drawControl(), определена в qcommonstyle.cpp, чтобы нарисовать каждый элемент. Следовательно, внимательно qcommonstyle.cpp для подробностей о том, как каждый элемент рисуется.


подробный ответ!--106-->

те, кто предпочитает краткость, должны прочитать tl; dr, и документация по стилям в пункт Вид. Если вы все еще застряли (и многие пользователи Python, вероятно, будут), остальная часть этого ответа должна помочь.

I. делегат по умолчанию-QStyledItemDelegate

на QStyledItemDelegate делегат по умолчанию для элементов в представлениях. Это четко указано в обзор программирования модели/представления Qt:

начиная с Qt 4.4, реализация делегата по умолчанию по QStyledItemDelegate, и это используется в качестве делегата по умолчанию в Qt стандартный вид.

на документы на QStyledItemDelegate предоставьте немного больше деталей:

при отображении данных из моделей в представлениях элементов Qt, например, QTableView, отдельные элементы рисуются делегатом. Кроме того, когда элемент отредактированный, он предоставляет виджет редактора, который помещается поверх вид элемента во время редактирования. QStyledItemDelegate является делегат по умолчанию для всех видов элементов Qt, и устанавливается на них когда они будут созданы.

в общем, если вы хотите понять основную механику любого из представлений элементов (не только дерева, но и таблиц и списков), изучите QStyledItemDelegate.

на paint() метод qstyleditemdleegate.cpp определен в строке 419 базы доксигенированного кода. Давайте посмотрим на последние две строчки:

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

здесь происходят две вещи. Во-первых, стиль устанавливается -- обычно в QApplication.style(). Во-вторых, этот стиль drawControl() метод вызывается для рисования окрашиваемого элемента. И это все. Это буквально последняя строка QStyledItemDelegate.paint()!

следовательно, если вы действительно хотите выяснить, как делегат по умолчанию рисует вещи, мы на самом деле должны изучить стиль, который делает всю реальную работу. Вот что мы сделаем в остальной части этого документа.

II. QStyle: что дает делегату его стиль?

когда что-либо отображается с помощью Qt, оно рисуется в соответствии с некоторым стилем, который был выбран, специфичным для системы способом, когда вы создали экземпляр QApplication. От документы для QStyle:

класс QStyle является абстрактным базовым классом, который инкапсулирует внешний вид и почувствовать GUI. Qt содержит набор подклассов QStyle, которые эмулируют стили различных платформ, поддерживаемых Qt (QWindowsStyle, QMacStyle, QMotifStyle, etc.). По умолчанию эти стили встроены в в библиотеки QtGui.

в Qt вы найдете исходный код для стиля N в src/gui/styles/N.cpp.

каждый стиль содержит реализацию элементарных операций, используемых для рисования всего в GUI, от древовидных представлений до выпадающих меню. Стандартные стили, такие как QWindowsStyle, наследуют большинство своих методов от QCommonStyle. Каждый конкретный стиль обычно включает только незначительные отклонения от этого общая база. Следовательно, тщательное изучение qcommonstyle.cpp покажет базовую функциональность, которую разработчики Qt нашли полезной для рисования всех частей GUI. Его важность трудно переоценить.

ниже мы рассмотрим детали, относящиеся к элементам чертежного вида.

III. QStyle.drawControl (): выполнение эндоскопии на делегате

как упоминалось выше, понимание основной механики рисования вида требует изучения реализация drawControl() на qcommonstyle.cpp, реализация, которая начинается в строке 1197. Обратите внимание, что ниже, когда я ссылаюсь на номер строки без упоминания имени файла, по соглашению я имею в виду qcommonstyle.cpp в базе кода doxygenated.

на документация для QStyle.drawControl() поучительно:

класса qstyle.drawControl (элемент, опция, художник)

параметры:

  • элемент-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);

первый аргумент-это QStyle.SubElement флаг, в данном случае SE_ItemViewItemText. Элементы стиля представляют составные части элементов управления. Каждый элемент в представлении имеет три возможных подэлемента: флажок, значок и текст; очевидно,SE_ItemViewItemText элемент представляет текст. Все возможные подэлементы перечислены здесь:

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

в subElementRect() метод содержит все случаи в перечислении подэлементов: our SE_ItemViewItemText дело начинается на линии 3015.

обратите внимание, что subElementRect() возвращает QRect, который определяет прямоугольник в плоскости с использованием целочисленной точности и может быть построен с четырьмя целыми числами (слева, сверху, ширина, высота). Например,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.PixelMetrics перечислены здесь:

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

вы можете получить значение заданной пиксельной метрики, используя класса qstyle.pixelMetric (), который используется много в viewItemSize(). Первый ввод pixelMetric() один из QStyle.PixelMetrics в перечислении. В qcommonstyle.cpp реализация pixelMetric() начинается на линии 4367.

например, viewItemSize() вычисляет ширину и высоту установите флажок (при необходимости), используя следующее:

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

обратите внимание, что pixelMetric() используется не только в viewItemSize(), но является вездесущей. Он используется для расчета метрических свойств многих элементов GUI, от границ окна до значков, и перечеркивается по всему qcommonstyle.cpp. В принципе, всякий раз, когда вам нужно знать, сколько пикселей ваш стиль использует для какого-либо графического элемента, размер которого не меняется (например, флажок), стиль будет вызывать пиксельные метрики.

2. viewItemLayout (): кубик Рубика подэлементы, чтобы получить X/Y позиции

вторая часть viewItemLayout() посвящен организации компоновки подэлементов, ширина и высота которых только что были рассчитаны. То есть ему нужно найти свои позиции x и y, чтобы закончить заполнение значений в QRects, таких как textRect. Подэлементы будут организованы по-разному в зависимости от общей настройки вида (например, ориентирован ли он вправо-влево или влево-вправо). Следовательно, viewItemLayout() вычисляет конечное положение покоя каждого подэлемента в зависимости от таких факторов.

если вы когда-нибудь понадобится подсказки о том, как переопределить sizeHint() в пользовательский делегат, subElementRect() может быть полезным источником советов и трюков. В частности, viewItemSize() вероятно, содержит полезные лакомые кусочки и соответствующие пиксельные метрики, которые вы можете запросить, если хотите, чтобы пользовательское представление близко соответствовало умолчанию.

после QRects рассчитываются для текста, значка, и отметьте подэлементы,drawControl() движется дальше, и, наконец, начинает рисовать.

B. цвет фона: становится примитивным

сначала фон заполняется для элемента (строка 2163):

drawPrimitive(PE_PanelItemViewItem, option, painter);

это очень похоже на вызов 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. The option.state содержит государственные флаги, которые описывают состояние объекта в момент (она включена, выбирается, редактируется и т. д.?). Эти состояния не просто используются в задней части, но вам, вероятно, придется использовать его при повторном дополнении QStyledItemDelegate.paint() в пользовательских делегатов. Вы можете найти перечисление QStyle.StateFlags здесь:

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

после выбора правильного цвета, drawPrimitive() использует QPainter.fillRect() чтобы заполнить соответствующую область этим цветом (строка 786):

p->fillRect(vopt->rect, vopt->backgroundBrush);

QPainter.fillRect() является очень полезным методом при реализации пользовательских делегатов для себя.

после ухода за фоном,drawControl() затем переходит к завершению рисования элемента, начиная с флажок (строка 2165), затем значок (строка 2185) и, наконец, текст (строка 2194). Мы не будем вдаваться в подробности, но я закончу кратким обсуждением того, как он рисует текст.

C. рисование текста: going renegade

во-первых, состояние объекта используется для указания цвета текста. Это использует QStyle.StateFlags только что обсуждали. Тогда drawControl() пинает обязанности рисования текста в пользовательский метод viewItemDrawText() (определенными в соответствии 921):

viewItemDrawText(painter, vopt, textRect);

этот метод принимает в качестве параметров painter, option и текстовый прямоугольник, описанный выше (часть A). Обратите внимание, что параметр option очень важен: это QStyleOption класс, с помощью которого содержимое передается в пределах стиля (он включает в себя значок, checkstate и свойства текста).

после того, как текст извлекается из опции, он включается в QtGui.QTextLine это добавляется к QtGui.QTextLayout. Окончательный, elided (т. е. с эллипсами, в зависимости от настройки переноса слов) текст рисуется QPainter.drawText() (см. строку 983), которая является одной из примитивных операций рисования Qt.

честно говоря много viewItemDrawText() тратится на обертывание слов. Здесь мы начинаем проникать в некоторые внутренности Qt, которые не должен был видеть ни один пользователь Python, не говоря уже о том, чтобы возиться с ними. Например, он использует QStackTextEngine класса. Я призываю вас Google "qstacktextengine pyqt", чтобы увидеть, насколько редко это придумано для пользователей Python. Если это интересует вас, имейте это в виду!

раздел IV. Резюме

если вы хотите получить доступ к базовой механике рисования для делегата по умолчанию, вы в конечном итоге изучите реализацию QStyle.drawControl() на qcommonstyle.cpp, 6000-строчный зверь файла. Это упражнение может быть очень полезно для выяснения точных процедур, используемых для вычисления размеров и рисования примитивных графических элементов, которые содержат элементы. Иногда, однако, этот зверь может быть совершенно страшным и бесполезно, как когда дело доходит до обертывания слов. В этих случаях вам, вероятно, просто нужно будет выяснить пользовательскую реализацию желаемой функциональности для вашего делегата.

наконец, теперь, когда мы увидели общую картину того, как все работает, мы можем лучше оценить, насколько полезна документация для QStyle это, в частности, раздел стили в представлениях элементов. Там мы находим следующий откровенный любовный самородок:

рисование элементов в представлениях выполняется делегатом. В Qt делегат по умолчанию,QStyledItemDelegate, также используется для расчета ограничивающие прямоугольники элементов (и их составных элементов) ...когда QStyledItemDelegate рисует свои предметы, рисует CE_ItemViewItems...когда реализация стиля для настройки чертежа видов элементов, необходимо проверьте реализацию QCommonStyle (и любые другие подклассы от которого ваш стиль наследуется). Таким образом, вы узнаете, что и как другие элементы стиля уже роспись, а затем вы можете переопределить картина элементов, которые должны быть нарисованы по-разному.

таким образом, в основном ответ на исходный пост: "то, что они сказали."