Utilizzare un modello come una fonte per una QMenu
-
01-10-2019 - |
Domanda
Ho creato un modello che elenca le configurazioni esistenti (diciamo che elenca "file", in quanto questo non importa qui). Finora, funziona bene se collegato a un QListView
.
Esempio:
--- ListView ---
- file #1 -
- file #2 -
- file #3 -
- file #4 -
----------------
E 'possibile utilizzare lo stesso modello per un QMenu
aggiornato dinamicamente?
Qualcosa di simile:
Menu
-> Submenu #1
-> Submenu #2
-> File-submenu
-> file #1
-> file #2
-> file #3
-> file #4
-> Submenu #3
In breve: esiste un modo per creare un elenco di QAction
s dynamicaly aggiornati (raggruppati nella stessa QMenu
) in funzione di un modello (derivato da QAbstractListModel
)
Soluzione
Se il vostro obiettivo è solo quello di aggiornare i Actons menu con il testo della voce che sono disponibili nel QAbstractListModel
, allora la risposta è Sì.
Ecco un modo ..
indice dell'elemento Persona può essere ottenuta utilizzando la seguente funzione.
QModelIndex QAbstractListModel::index ( int row, int column = 0,
const QModelIndex & parent = QModelIndex() ) const [virtual]
Con l'indice ottenuto, i dati possono essere ottenuti,
QVariant QModelIndex::data ( int role = Qt::DisplayRole ) const
Poi l'availalble testo l'indice può essere ottenuto utilizzando,
QString QVariant::toString () const
Ora, con l'ottenuto QString è possibile aggiungere un'azione al menu.
QAction * QMenu::addAction ( const QString & text )
La cosa che dovete fare in modo che sia, si dovrebbe essere in grado di attraversare attraverso tutti gli elementi del modello, in modo da poter ottenere l'indice del ogni singolo elemento. Speranza che aiuta ..
Altri suggerimenti
Purtroppo non esiste una categoria QMenuView
ma ho trovato questa implementazione promettendo in rete: QMenuView
( qmenuview.h , qmenuview.cpp ).
Per rispondere alla tua domanda breve, sì, c'è. Ma dovrete scrivere voi stessi.
La parte più semplice sarebbe quella di creare una sottoclasse di QAbstractListModel.
La parte più difficile sarebbe quando si crea il proprio punto di vista. Qt vi permetterà di creare il vostro punto di vista, proprio come se si dovesse creare il proprio modello, ma sarebbe diventato così molto più complessa, dal momento che hai avuto modo di maniglia tutto da soli.
E 'del tutto fattibile per uno scopo specifico, ma è anche molto più lavoro di quanto Penso che si desidera. Così, come Gianni stava dicendo, quadro model-view di Qt non è pensato per essere utilizzato in questo modo.
No. I modelli possono essere utilizzati solo con Vista, come da Model-View un'infrastruttura che utilizza Qt.
È possibile creare una voce di menu e metterlo QListView
in esso utilizzando QWidgetAction
. Naturalmente questo menu non può avere sottomenu. L'esempio che segue è in Python, ma spero che non importa in questo caso.
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()