web-dev-qa-db-fra.com

QListView / QListWidget avec des éléments personnalisés et des widgets d'éléments personnalisés

J'écris une application PyQt et j'ai du mal à créer une vue de liste personnalisée. J'aimerais que la liste contienne des widgets arbitraires (un widget personnalisé en particulier). Comment pourrais-je m'y prendre?

Il semble que l'alternative serait de créer une vue de table ou de grille enveloppée dans une barre de défilement. Cependant, j'aimerais pouvoir profiter de l'approche modèle/vue ainsi que de l'imbrication (arborescence) prenant en charge la poignée intégrée.

Pour clarifier, les widgets personnalisés sont interactifs (contiennent des boutons), donc la solution nécessite plus que de peindre un widget.

30
Daniel Naab

Je pense que vous devez sous-classer QItemDelegate .

QItemDelegate peut être utilisé pour fournir des fonctionnalités d'affichage personnalisées et des widgets d'éditeur pour les vues d'élément basées sur les sous-classes QAbstractItemView. L'utilisation d'un délégué à cet effet permet aux mécanismes d'affichage et d'édition d'être personnalisés et développés indépendamment du modèle et de la vue.

Ce code est extrait des exemples de Qt, l'application torrent.

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

Fondamentalement, comme vous pouvez le voir, il vérifie si la colonne à peindre est d'un index spécifique, si c'est le cas, il peint une barre de progression. Je pense que vous pouvez le modifier un peu et au lieu d'utiliser un QStyleOption, vous pouvez utiliser votre propre widget.

modifier: n'oubliez pas de configurer votre délégué d'article avec votre QListView en utilisant setItemDelegate .

En enquêtant sur votre question, je suis tombé sur le fil this , qui explique comment peindre un widget personnalisé à l'aide d'un QItemDelegate, je pense qu'il contient toutes les informations dont vous pourriez avoir besoin.

28
Idan K

Si je comprends bien votre question, vous voulez quelque chose comme ceci:

enter image description here

où chaque ligne contient un ensemble personnalisé de widgets.

Pour y parvenir, deux étapes.

Implémentez la ligne avec un widget personnalisé

Tout d'abord, implémentez un widget personnalisé qui contient tous les widgets nécessaires par ligne de liste.

Ici, j'utilise une étiquette et deux boutons par ligne, avec une disposition horizontale.

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)

Ajouter des lignes à la liste

L'instanciation de plusieurs lignes consiste alors simplement à créer un élément de widget et à associer le widget personnalisé à la ligne de l'élément.

# 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)
4
Overdrivr

@ La réponse d'Idan fonctionne bien, mais je publierai un exemple plus simple que j'ai trouvé. Ce délégué d'article dessine juste un rectangle noir pour chaque article.

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

Et puis il vous suffit de le définir pour le widget de liste:

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

Assist dit que:

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

Définit le widget donné à afficher dans la cellule de la ligne et de la colonne données, en transmettant la propriété du widget à la table. Si le widget de cellule A est remplacé par le widget de cellule B, le widget de cellule A sera supprimé.

Et il existe des analogues de cette méthode dans la plupart des descendants de QAbstractItemView.

Vous devez sous-classer Q *** Delegate uniquement lorsque vous voulez que le widget de l'éditeur apparaisse dans View uniquement lorsque vous appuyez sur EditTrigger, puis disparaître et laisser délégué déléguer l'élément de vue d'une manière ou d'une autre.

Si je corrige, vous vouliez voir le contrôle dans la vue des éléments tout le temps et être en mesure d'appuyer sur les contrôles sans avoir besoin de passer en mode édition et d'attendre que le délégué crée l'éditeur et définisse son état, vous n'avez donc pas besoin de faire de délégué spécifique, juste définir un widget dans l'élément de la vue.

2
Maxim Popravko

d'une autre manière, vous pouvez ajouter des éléments personnalisés dans listWidget. à cet effet, vous devez ajouter une nouvelle classe. nommez-le comme vous voulez je lui donne juste "myList" j'espère que vous savez comment ajouter de nouveaux éléments dans le projet. :)

puis dans ce nouveau cadre ajoutez votre contrôle autant que vous le souhaitez. comme QLabels, QlineEdit etc.

puis dans la classe principale, vous devez ajouter une liste de noms listWidget ou comme vous le souhaitez et écrire le code suivant

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

ce dernier u peut également modifier les propriétés des éléments à l'aide de signaux/emplacements.

j'espère que son aide vous ..!

1
Muhammad Suleman