เรนเดอร์ 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() รอบการเรนเดอร์
  • การตั้งค่าเรขาคณิตของวิดเจ็ตเป็นขนาดที่ใช้ได้

แต่ตอนนี้ฉันติดขัดนิดหน่อย ฉันค้นหาบนอินเทอร์เน็ตมาระยะหนึ่งแล้ว แต่ไม่พบตัวอย่างใด ๆ ที่ทำในสิ่งที่ฉันต้องการ พวกเขาล้วนพูดถึงวิดเจ็ตการแก้ไข (ซึ่งง่ายกว่ามาก) หรือการควบคุมการวาดแบบกำหนดเอง (อันที่กำหนดไว้ล่วงหน้า เช่น แถบความคืบหน้า) แต่ที่นี่ ฉันต้องการวิดเจ็ตแบบกำหนดเองที่ฉันสร้างขึ้นจริงๆ ซึ่งประกอบด้วยเลย์เอาต์ ป้ายกำกับ และ pixmap ขอบคุณสำหรับความช่วยเหลือของคุณ!

ฉันใช้ 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 เป็นอาร์กิวเมนต์ที่ 2 สำหรับ 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 และวิดเจ็ตเหล่านั้นโหลดแบบ Lazy Load อย่างเหมาะสม (นั่นคือสาเหตุที่ฉันใช้โมเดล/รูปแบบมุมมอง) แต่ฉันไม่เห็นว่าจะยกเลิกการโหลดได้อย่างไรเมื่อไม่ได้แสดง นั่นก็เป็นอีกปัญหาหนึ่ง

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