Utilisez un modèle comme source pour un QMenu
-
01-10-2019 - |
Question
Je créé un modèle qui liste les configurations existantes (Répétons le liste des « fichiers », car cela fait ici pas vraiment). Jusqu'à présent, cela fonctionne bien lorsqu'il est attaché à un QListView
.
Exemple:
--- ListView ---
- file #1 -
- file #2 -
- file #3 -
- file #4 -
----------------
Est-il possible d'utiliser le même modèle pour un QMenu
mis à jour dynamiquement?
Quelque chose comme:
Menu
-> Submenu #1
-> Submenu #2
-> File-submenu
-> file #1
-> file #2
-> file #3
-> file #4
-> Submenu #3
En bref: est-il possible de créer une liste des mises à jour (QAction
s dynamiquement regroupés dans la même QMenu
) en fonction d'un modèle (dérivé de QAbstractListModel
)
La solution
Si votre objectif est simplement de mettre à jour votre menu actons avec le texte de l'élément qui sont disponibles dans le QAbstractListModel
, alors la réponse est Oui.
Voici une façon ..
L'indice de l'article individuel peut être obtenue en utilisant la fonction suivante.
QModelIndex QAbstractListModel::index ( int row, int column = 0,
const QModelIndex & parent = QModelIndex() ) const [virtual]
Avec l'indice obtenu, les données peuvent être obtenues par,
QVariant QModelIndex::data ( int role = Qt::DisplayRole ) const
Ensuite, on peut obtenir le availalble texte dans l'index à l'aide,
QString QVariant::toString () const
Maintenant, avec l'QString obtenu, vous pouvez ajouter une action au menu.
QAction * QMenu::addAction ( const QString & text )
La chose que vous devez faire est certain que, vous devriez être en mesure de traverser à travers tous les éléments du modèle, de sorte que vous pouvez obtenir l'indice de chaque élément. Hope it helps ..
Autres conseils
Malheureusement, il n'y a pas de classe QMenuView
mais je trouve cette mise en œuvre prometteuse sur le net: QMenuView
( qmenuview.h , qmenuview.cpp ).
Pour répondre à votre question courte, oui, il y a. Mais vous devrez écrire vous-même.
La partie la plus facile serait de créer une sous-classe de QAbstractListModel.
Le plus dur serait lorsque vous créez votre propre point de vue. Qt vous permettra de créer votre propre point de vue, tout comme si vous deviez créer votre propre modèle, mais il deviendrait beaucoup plus complexe, puisque vous avez à poignée tout vous.
Il est tout à fait faisable dans un but spécifique, mais il est aussi beaucoup plus de travail que je pense que vous voulez. Alors, comme Gianni a dit, le cadre modèle de vue de Qt est pas destiné à être utilisé de cette façon.
. Les modèles ne peuvent être utilisés avec des vues, selon le Model-View cadre des utilisations Qt.
Vous pouvez créer un élément de menu et de mettre en QListView
à l'aide QWidgetAction
. Bien sûr, ce menu ne peut pas avoir des sous-menus. L'exemple ci-dessous est en Python, mais j'espère que cela n'a pas d'importance dans ce cas.
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import Qt
class QListViewMenu(QtWidgets.QMenu):
"""
QMenu with QListView.
Supports `activated`, `clicked`, `doubleClicked`. `setModel`.
"""
max_visible_items = 16
def __init__(self, parent=None):
super().__init__(parent)
self.listview = lv = QtWidgets.QListView()
lv.setFrameShape(lv.NoFrame)
lv.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
pal = lv.palette()
pal.setColor(pal.Base, self.palette().color(pal.Window))
lv.setPalette(pal)
lv.setEditTriggers(lv.NoEditTriggers) # disable edit on doubleclick
act_wgt = QtWidgets.QWidgetAction(self)
act_wgt.setDefaultWidget(lv)
self.addAction(act_wgt)
self.activated = lv.activated
self.clicked = lv.clicked
self.doubleClicked = lv.doubleClicked
self.setModel = lv.setModel
lv.sizeHint = self.size_hint
lv.minimumSizeHint = self.size_hint
lv.mousePressEvent = lambda event: None # skip
lv.mouseMoveEvent = lambda event: None # skip
lv.mouseReleaseEvent = self.mouse_release_event
def size_hint(self):
lv = self.listview
width = lv.sizeHintForColumn(0)
width += lv.verticalScrollBar().sizeHint().width()
if isinstance(self.parent(), QtWidgets.QToolButton):
width = max(width, self.parent().width())
visible_rows = min(self.max_visible_items, lv.model().rowCount())
return QtCore.QSize(width, visible_rows * lv.sizeHintForRow(0))
def mouse_release_event(self, event):
if event.button() == Qt.LeftButton:
idx = self.listview.indexAt(event.pos())
if idx.isValid():
self.clicked.emit(idx)
self.close()
super(QtWidgets.QListView, self.listview).mouseReleaseEvent(event)
class Form(QtWidgets.QDialog):
def __init__(self):
super().__init__()
words = "ability able about above accept according account across"
model = QtCore.QStringListModel(words.split())
# fake icons to take space
def data(index, role):
if role == Qt.DecorationRole:
pixm = QtGui.QPixmap(40, 40)
pixm.fill(Qt.transparent)
return QtGui.QIcon(pixm)
return QtCore.QStringListModel.data(model, index, role)
model.data = data
self.btn = btn = QtWidgets.QToolButton(self)
btn.setText("QListView menu")
btn.setPopupMode(btn.MenuButtonPopup)
root_menu = QtWidgets.QMenu(btn)
menu = QListViewMenu(btn)
menu.setTitle('submenu')
menu.setModel(model)
menu.clicked.connect(self.item_clicked)
root_menu.addMenu(menu)
btn.setMenu(root_menu)
def item_clicked(self, index):
self.btn.menu().hide()
print(index.data())
app = QtWidgets.QApplication([])
f = Form()
f.show()
app.exec()