2009-06-04 9 views
28

J'écris une application PyQt et j'ai de la difficulté à 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?QListView/QListWidget avec des éléments personnalisés et des widgets d'éléments personnalisés

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) pour prendre 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.

Répondre

28

Je pense que vous devez sous-classe QItemDelegate.

QItemDelegate peut être utilisé pour fournir sur la base des sous-classes QAbstractItemView fonctions d'affichage personnalisé et éditeur widgets pour les vues de l'élément. 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 de manière indépendante à partir du modèle et de la vue.

Ce code provient 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); 
    } 
}; 

En fait, comme vous pouvez le voir vérifie si la colonne à peindre est d'un indice spécifique, le cas échéant, il peint une barre de progression. Je pense que vous pourriez le modifier un peu et au lieu d'utiliser un QStyleOption, vous pouvez utiliser votre propre widget. Edit: ne pas oublier de configurer votre délégué d'article avec votre QListView en utilisant setItemDelegate. En étudiant votre question, je suis tombé sur thread this, qui explique comment peindre un widget personnalisé en utilisant un QItemDelegate, je crois qu'il a toutes les informations dont vous pourriez avoir besoin.

+0

Merci pour le poste, je vais examiner plus demain. Pourriez-vous élaborer sur la façon dont je vais remplacer le dessin QStyleOption avec un widget personnalisé? –

+0

Eh bien, il suffit de peindre votre widget avec le QPainter fourni dans la méthode de peinture de QItemDelegate. J'ai modifié mon message pour inclure un lien utile. –

+0

Eh bien, je peux obtenir quelque chose de rude en utilisant cette technique. Mais le problème est l'aspect "widget arbitraire" - j'ai un widget composé qui contient des boutons, etc, qui sont interactifs - donc ils doivent être physiquement placés dans la liste ou l'utilisateur n'est pas capable de cliquer dessus. corrigez-moi si je me trompe, mais cela ne fera que dessiner un widget inactif. –

2

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ée, en passant 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 à cette méthode dans la plupart des descendants de QAbstractItemView.

Vous devez sous-classer Q *** Delegate uniquement lorsque vous voulez que le widget éditeur apparaisse dans View uniquement lorsque vous appuyez sur EditTrigger, puis disparaissez et laissez le délégué rendre 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 articles tout le temps et être capable de toucher les contrôles sans avoir à entrer en mode d'édition et attendre que le délégué crée un éditeur et définir son état, donc vous n'avez pas besoin de faire délégué spécifique, il suffit de définir le widget dans l'élément de la vue.

+0

Malheureusement, ces fonctions ne sont disponibles que pour QTableWidget, QTreeWidget et QListWidget. Ils ne sont pas disponibles lors de l'utilisation d'une vue avec un modèle personnalisé. –

+1

De la [docs] (http://doc.qt.io/qt-5/qlistwidget.html#setItemWidget) "Cette fonction ne doit être utilisée que pour afficher du contenu statique à la place d'un élément de widget de liste. Pour afficher un contenu dynamique personnalisé ou implémenter un widget d'éditeur personnalisé, utilisez plutôt QListView et la sous-classe QItemDelegate. " – Trilarion

1

Vous pouvez également ajouter des éléments personnalisés dans listWidget. Pour ce faire, vous devez ajouter une nouvelle classe. nommez-le comme vous voulez que 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 u devez ajouter une liste de noms listWidget ou que vous voulez 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); 

ci u peut également modifier les propriétés des objets en utilisant des signaux/slots.

espérons que son aide vous ..!

+0

Qui supprime myList sur l'élément destroy? Je ne vois aucun code lié à la propriété dans cet exemple. – Allex

+0

Eh bien, je suis développeur Java et JAVA prendre soin de la collecte des ordures :) c'était ma petite connaissance de C++ –

3

@ La réponse d'Idan fonctionne bien, mais je vais poster un exemple plus simple que j'ai trouvé. Ce délégué d'élément 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 configurer pour le widget liste:

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