Визуализация QWidget в методе paint() QWidgetDelegate для QListView

у меня возникли трудности с реализацией пользовательского рендеринга виджета в QListView. В настоящее время у меня есть QListView, отображающий мою пользовательскую модель под названием PlayQueue на основе QAbstractListModel.

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

void QueueableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
    if (option.state & QStyle::State_Selected)
        painter->fillRect(option.rect, option.palette.highlight());
    QWidget *widget = new QPushButton("bonjour");
    widget->render(painter);
}

Фон выбора отображается правильно, но виджет не отображается. Я пробовал использовать простые команды QPainter, как в примерах Qt, и это работает нормально:

void QueueableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
    if (option.state & QStyle::State_Selected)
        painter->fillRect(option.rect, option.palette.highlight());
    if (option.state & QStyle::State_Selected)
        painter->setPen(option.palette.highlightedText().color());
    painter->setFont(QFont("Arial", 10));
    painter->drawText(option.rect, Qt::AlignCenter, "Custom drawing");
}

Итак, я попробовал некоторые изменения, такие как:

  • Замена QStyledItemDelegate на QItemDelegate
  • Добавление painter->save() и painter->restore() вокруг рендеринга
  • Установка геометрии виджета на доступный размер

Но сейчас я немного застрял, я некоторое время искал в Интернете, но не могу найти ни одного примера, делающего то, что я хочу, все они говорят о редактировании виджета (что намного проще) или пользовательском нарисованном элементе управления (предопределенные). , как индикаторы выполнения). Но здесь мне действительно нужен пользовательский виджет, который я создал, содержащий макет, метки и растровые изображения. Спасибо за вашу помощь!

Я использую Qt 4.7.3 для GCC на Ubuntu 11.04.


person Adrien Jarthon    schedule 23.06.2011    source источник


Ответы (3)


Для полноты картины: далее можно найти код для управления QWidget как элементом QListView с помощью делегатов.

Наконец-то я узнал, как заставить его работать в подклассе QStyledItemDelegate, используя его метод paint(...).

Это кажется более эффективным для производительности, чем предыдущее решение, но это утверждение необходимо проверить =), исследуя, что делает setIndexWidget() с созданным QWidget.

Итак, наконец, вот код:

class PackageListItemWidget: public QWidget

.....

class PackageListItemDelegate: public QStyledItemDelegate

.....

void PackageListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{

// here we have active painter provided by caller

// by the way - we can't use painter->save() and painter->restore()
// methods cause we have to call painter->end() method before painting
// the QWidget, and painter->end() method deletes
// the saved parameters of painter

// we have to save paint device of the provided painter to restore the painter
// after drawing QWidget
QPaintDevice* original_pdev_ptr = painter->device();

// example of simple drawing (selection) before widget
if (option.state & QStyle::State_Selected)
    painter->fillRect(option.rect, option.palette.highlight());

// creating local QWidget (that's why i think it should be fasted, cause we 
// don't touch the heap and don't deal with a QWidget except painting)
PackageListItemWidget item_widget;

// Setting some parameters for widget for example
    // spec. params
item_widget.SetPackageName(index.data(Qt::DisplayRole).toString());     
    // geometry
item_widget.setGeometry(option.rect);

// here we have to finish the painting of provided painter, cause
//     1) QWidget::render(QPainter *,...) doesn't work with provided external painter 
//          and we have to use QWidget::render(QPaintDevice *,...)
//          which creates its own painter
//     2) two painters can't work with the same QPaintDevice at the same time
painter->end(); 

// rendering of QWidget itself
item_widget.render(painter->device(), QPoint(option.rect.x(), option.rect.y()), QRegion(0, 0, option.rect.width(), option.rect.height()), QWidget::RenderFlag::DrawChildren);   

// starting (in fact just continuing) painting with external painter, provided
// by caller
painter->begin(original_pdev_ptr);  

// example of simple painting after widget
painter->drawEllipse(0,0, 10,10);   
};
person Physt    schedule 24.09.2013
comment
Немного повозившись с этим, я обнаружил, что мне пришлось вручную применить deviceTransform() к точке смещения, чтобы выровнять ее. Перед painter->end(); я создал QPoint mappedorigin = painter->deviceTransform().map(QPoint(option.rect.x(), option.rect.y())), а затем передал mappedorigin в качестве второго аргумента для QWidget::render(). Вероятно, в этом не было бы необходимости, если бы я создал виджет в этой функции, но я создал его как часть родительского виджета. - person Andrew Domaszek; 23.09.2017
comment
Это единственное, что у меня работает, спасибо. Мне пришлось добавить отображение от @AndrewDomaszek - person Kuba Beránek; 26.11.2017

Хорошо, я наконец понял, как сделать то, что я хотел. Вот что я сделал:

  1. Отбросьте класс делегата
  2. Вызовите QListView::setIndexWidget() в методе data() моей модели, чтобы установить виджет
  3. Убедитесь, что виджет уже не присутствует при настройке, отметив QListView::indexWidget()
  4. Обработайте роль Qt::SizeHintRole, чтобы вернуть подсказку о размере виджета.
  5. Вернуть пустой QVariant для роли Qt::DisplayRole

Таким образом, мои пользовательские виджеты отображаются в QListView, и они должным образом лениво загружаются (поэтому я использовал шаблон модель/представление). Но я не вижу, как я могу их выгрузить, когда они не отображаются, ну, это еще одна проблема.

person Adrien Jarthon    schedule 01.07.2011
comment
Вы когда-нибудь придумывали, как лучше сделать разгрузку? - person lyschoening; 18.03.2013

Вот пример для вас. Кажется, вам нужно использовать QStylePainter, но это только для рисования, насколько я понимаю, он не действует как настоящая кнопка.

person O.C.    schedule 23.06.2011
comment
Да, я видел этот метод, но он работает только для рисования предопределенного элемента управления, а не пользовательского виджета. Кстати, мне не нужна работающая кнопка, это было просто для упрощения примера, мой виджет довольно статичен, поэтому просто нарисовать его было бы неплохо, если это возможно. - person Adrien Jarthon; 24.06.2011