Utilizar un modelo como fuente para un QMenu
-
01-10-2019 - |
Pregunta
He creado un modelo que enumeran las configuraciones existentes (digamos que enumera "archivos", ya que en realidad no importa aquí). Hasta el momento, funciona bien cuando está conectado a un QListView
.
Ejemplo:
--- ListView ---
- file #1 -
- file #2 -
- file #3 -
- file #4 -
----------------
¿Es posible utilizar el mismo modelo para un QMenu
actualizada dinámicamente?
Algo así como:
Menu
-> Submenu #1
-> Submenu #2
-> File-submenu
-> file #1
-> file #2
-> file #3
-> file #4
-> Submenu #3
En resumen:? ¿Hay alguna manera de crear una lista de QAction
s dynamicaly actualizadas (agrupados en el mismo QMenu
) en función de un modelo (derivado de QAbstractListModel
)
Solución
Si su objetivo es sólo para actualizar sus Actons menú con el elemento de texto que están disponibles en el QAbstractListModel
, entonces la respuesta es Sí.
Esta es una manera ..
índice de elemento individual se puede obtener mediante el uso de la siguiente función.
QModelIndex QAbstractListModel::index ( int row, int column = 0,
const QModelIndex & parent = QModelIndex() ) const [virtual]
Con el índice obtenido, los datos pueden ser obtenidos por,
QVariant QModelIndex::data ( int role = Qt::DisplayRole ) const
A continuación, el availalble texto en el índice se puede obtener mediante el uso de,
QString QVariant::toString () const
Ahora con los obtenidos QString se puede añadir una acción al menú.
QAction * QMenu::addAction ( const QString & text )
Lo que usted tiene que asegurarse de que es, debe ser capaz de recorrer a través de todos los elementos en el modelo, por lo que se puede obtener el índice de la cada elemento. Esperamos que ayude ..
Otros consejos
Desafortunadamente no existe una clase QMenuView
pero he encontrado esta aplicación prometedor en la red: QMenuView
( qmenuview.h , qmenuview.cpp ).
Para responder a su pregunta corta, sí, lo hay. Sin embargo, usted tiene que escribir por sí mismo.
La parte fácil sería crear una subclase de QAbstractListModel.
La parte difícil sería al crear su propio punto de vista. Qt le permitirá crear su propio punto de vista, al igual que si tuviera que crear su propio modelo, pero sería convertido en mucho más complejo, ya que tienes a mango todo a ti mismo.
Es totalmente factible para un propósito específico, pero también es mucho más trabajo que creo que quieres. Por lo tanto, al igual que Gianni estaba diciendo, marco modelo-vista de Qt no está destinado a ser utilizado de esta manera.
No. Modelos sólo pueden ser utilizados con Vistas, según la Modelo-Vista marco que utiliza Qt.
Se puede crear un elemento de menú y poner QListView
en ella usando QWidgetAction
. Por supuesto, este menú no puede tener submenús. El siguiente ejemplo es en Python, pero espero que no importa en este 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()