Render QWidget dalam metode paint() QWidgetDelegate untuk QListView

saya mengalami kesulitan dalam mengimplementasikan rendering widget khusus di QListView. Saat ini saya memiliki QListView yang menampilkan model khusus saya yang disebut PlayQueue berdasarkan QAbstractListModel.

Ini berfungsi baik dengan teks sederhana, tapi sekarang saya ingin menampilkan widget khusus untuk setiap elemen. Jadi saya membuat subkelas QStyledItemDelegate untuk mengimplementasikan metode paint seperti ini:

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);
}

Latar belakang pemilihan ditampilkan dengan benar tetapi tidak ada widget yang ditampilkan. Saya mencoba dengan perintah QPainter sederhana seperti pada contoh Qt, dan ini berfungsi dengan baik:

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");
}

Jadi saya mencoba beberapa perubahan seperti:

  • Mengubah QStyledItemDelegate menjadi QItemDelegate
  • Menambahkan painter->save() dan painter->restore() di sekitar rendering
  • Mengatur geometri widget ke ukuran yang tersedia

Tapi saya agak buntu sekarang, saya mencari sebentar di internet, tetapi tidak menemukan contoh melakukan apa yang saya inginkan, semuanya berbicara tentang mengedit widget (yang jauh lebih mudah) atau kontrol yang digambar khusus (yang sudah ditentukan sebelumnya) , seperti bilah kemajuan). Tapi disini saya sangat membutuhkan custom widget yang saya buat, berisi beberapa layout, label & pixmaps. Terima kasih atas bantuan Anda!

Saya menggunakan Qt 4.7.3 untuk GCC di Ubuntu 11.04.


person Adrien Jarthon    schedule 23.06.2011    source sumber


Jawaban (3)


Sekadar melengkapi gambaran keseluruhan: selanjutnya kita dapat menemukan kode untuk mengelola QWidget sebagai item QListView menggunakan delegasi.

Saya akhirnya menemukan cara membuatnya berfungsi dalam subkelas QStyledItemDelegate menggunakan metode paint(...).

Tampaknya lebih efektif untuk kinerja daripada solusi sebelumnya, tetapi pernyataan ini perlu diverifikasi =) dengan menyelidiki apa yang dilakukan setIndexWidget() dengan QWidget yang dibuat.

Akhirnya, inilah kodenya:

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
Setelah sedikit mengutak-atik ini, saya menemukan bahwa saya harus menerapkan deviceTransform() secara manual ke titik offset agar sejajar. Sebelum painter->end(); saya membuat QPoint mappedorigin = painter->deviceTransform().map(QPoint(option.rect.x(), option.rect.y())), lalu meneruskan mappedorigin sebagai argumen ke-2 untuk QWidget::render(). Ini mungkin tidak diperlukan jika saya membuat widget dalam fungsi ini, tetapi saya membuatnya sebagai bagian dari widget induk. - person Andrew Domaszek; 23.09.2017
comment
Ini adalah satu-satunya hal yang berhasil bagi saya, terima kasih. Saya harus menambahkan pemetaan dari @AndrewDomaszek - person Kuba Beránek; 26.11.2017

Oke, saya akhirnya menemukan cara untuk melakukan apa yang saya inginkan. Inilah yang saya lakukan:

  1. Jatuhkan kelas delegasi
  2. Panggil QListView::setIndexWidget() dalam metode data() model saya untuk mengatur widget
  3. Pastikan tidak ada widget yang ada saat pengaturan dengan mencentang QListView::indexWidget()
  4. Tangani peran Qt::SizeHintRole untuk mengembalikan petunjuk ukuran widget
  5. Kembalikan QVariant kosong untuk peran Qt::DisplayRole

Dengan cara ini widget khusus saya ditampilkan di QListView, dan widget tersebut dimuat dengan lambat (itulah sebabnya saya menggunakan pola model/tampilan). Tapi saya tidak mengerti bagaimana cara membongkarnya jika tidak ditampilkan, itu masalah lain.

person Adrien Jarthon    schedule 01.07.2011
comment
Pernahkah Anda memikirkan cara terbaik untuk melakukan pembongkaran? - person lyschoening; 18.03.2013

Berikut adalah contoh untuk Anda. Tampaknya Anda perlu menggunakan QStylePainter tetapi sejauh yang saya mengerti ini hanya untuk menggambar, ini tidak berfungsi seperti tombol sebenarnya.

person O.C.    schedule 23.06.2011
comment
Ya, saya melihat metode ini, tetapi hanya berfungsi untuk menggambar kontrol yang telah ditentukan sebelumnya, bukan widget khusus. Ngomong-ngomong, saya tidak memerlukan tombol tekan yang berfungsi, ini hanya untuk menyederhanakan contoh, widget saya cukup statis, jadi menggambar saja sudah cukup jika memungkinkan. - person Adrien Jarthon; 24.06.2011