QListView/QListWidget with custom items and custom item widgets

I'm writing a PyQt application and am having some trouble creating a custom list view. I'd like the list to contain arbitrary widgets (one custom widget in particular). How would I go about this?

It seems that the alternative would be to create a table or grid view wrapped in a scrollbar. However, I'd like to be able to take advantage of the model/view approach as well as the nesting (tree-view) support the built-ins handle.

To clarify, the custom widgets are interactive (contain buttons), so the solution requires more than painting a widget.

Answers


I think you need to subclass QItemDelegate.

QItemDelegate can be used to provide custom display features and editor widgets for item views based on QAbstractItemView subclasses. Using a delegate for this purpose allows the display and editing mechanisms to be customized and developed independently from the model and view.

This code is taken from Qt's examples, the torrent application.

class TorrentViewDelegate : public QItemDelegate
{
    Q_OBJECT
public:
    inline TorrentViewDelegate(MainWindow *mainWindow) : QItemDelegate(mainWindow) {}

    inline void paint(QPainter *painter, const QStyleOptionViewItem &option,
                      const QModelIndex &index ) const
    {
        if (index.column() != 2) {
            QItemDelegate::paint(painter, option, index);
            return;
        }

        // Set up a QStyleOptionProgressBar to precisely mimic the
        // environment of a progress bar.
        QStyleOptionProgressBar progressBarOption;
        progressBarOption.state = QStyle::State_Enabled;
        progressBarOption.direction = QApplication::layoutDirection();
        progressBarOption.rect = option.rect;
        progressBarOption.fontMetrics = QApplication::fontMetrics();
        progressBarOption.minimum = 0;
        progressBarOption.maximum = 100;
        progressBarOption.textAlignment = Qt::AlignCenter;
        progressBarOption.textVisible = true;

        // Set the progress and text values of the style option.
        int progress = qobject_cast<MainWindow *>(parent())->clientForRow(index.row())->progress();
        progressBarOption.progress = progress < 0 ? 0 : progress;
        progressBarOption.text = QString().sprintf("%d%%", progressBarOption.progress);

        // Draw the progress bar onto the view.
        QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
    }
};

Basically as you can see it checks if the column to be painted is of a specific index, if so it paints a progress bar. I think you could tweak it a little and instead of using a QStyleOption you could use your own widget.

edit: don't forget to setup your item delegate with your QListView using setItemDelegate.

While investigating your question I've stumbled upon this thread, which elaborates how to paint a custom widget using a QItemDelegate, I believe it has all the info you might need.


If I understand your question correctly, you want something like this:

where each row contains a custom set of widgets.

To achieve this, two steps.

Implement the row with a custom Widget

First, implement a custom widget that contains all the widgets needed per list row.

Here I am using a label and two buttons per row, with an horizontal layout.

class MyCustomWidget(QWidget):
    def __init__(self, name, parent=None):
        super(MyCustomWidget, self).__init__(parent)

        self.row = QHBoxLayout()

        self.row.addWidget(QLabel(name))
        self.row.addWidget(QPushButton("view"))
        self.row.addWidget(QPushButton("select"))

        self.setLayout(self.row)

Add rows to the list

Instanciating multiple rows is then just a matter of creating a widget item, and associate the custom widget to the item's row.

# Create the list
mylist = QListWidget()

# Add to list a new item (item is simply an entry in your list)
item = QListWidgetItem(mylist)
mylist.addItem(item)

# Instanciate a custom widget 
row = MyCustomWidget()
item.setSizeHint(row.minimumSizeHint())

# Associate the custom widget to the list entry
mylist.setItemWidget(item, row)

@Idan's answer works well, but I'll post a simpler example I came up with. This item delegate just draws a black rectangle for each item.

class ItemDelegate : public QItemDelegate
{
public:
    explicit ItemDelegate(QObject *parent = 0) : QItemDelegate(parent) {}
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        painter->fillRect(option.rect.adjusted(1, 1, -1, -1), Qt::SolidPattern);
    }
};

And then you just need to set it for the list widget:

ui->listWidget->setItemDelegate(new ItemDelegate(ui->listWidget));

Assist says, that:

void QTableWidget::setCellWidget (int row, int column, QWidget * widget)  

Sets the given widget to be displayed in the cell in the given row and column, passing the ownership of the widget to the table. If cell widget A is replaced with cell widget B, cell widget A will be deleted.

And there is analogs to this method in the most of QAbstractItemView descendants.

You have to subclass Q***Delegate only when you want editor widget to appear in View only when you hit EditTrigger, then vanish and let delegate render the view item in some way.

If I correct, you wanted to see control in item view all the time and be able to hit controls without the need to enter editing mode and wait while delegate creates editor and set its state, so you do not need to make specific delegate, just set widget into the view's item.


another way you can add custom items in listWidget. for this purpose you need to add new class. name it as you want i just give it "myList" i hope you know how to add new items in project. :)

then in this new frame add your control as many as u want. like QLabels,QlineEdit etc

then in main class u have to add a listWidget name list or as you want and the write the following code

MyList *myList = new MyList();
QListWidgetItem *item = new QListWidgetItem();
ui->list->insertItem(ui->list->size().height(),item);
item->setSizeHint(QSize(50,30));
ui->list->setItemWidget(item,myList);

latter u can also change items properties using signals/slots ..

hope its help you ..!


Need Your Help

Previously added items are added again when saving an ArrayList using SharedPreferences

android android-listview arraylist sharedpreferences

In my app I have 3 activities A,B and C. In Activity A when a button is pressed it starts Activity B and an empty arraylist is passed to it using putExtra. In Activity B, if the arraylist is empty it

Can the adp2 device be flashed with 2.0?

android operating-system adp

Can the adp2 that I just bought through the Android Market be flashed with Android 2.0? I can't find anything anywhere. The HTC developer support only has system images for the adp1 and adp2 phon...