Use a model as a source for a QMenu
I created a model which list the existing configurations (let's say it lists "files", as this doesn't really matter here). So far, it works well when attached to a QListView
.
Example:
--- ListView ---
- file #1 -
- file #2 -
- file #3 -
- file #4 -
----------------
Is it possible to use the same model for a dynamically updated QMenu
?
Something like:
Menu
-> Submenu #1
-> Submenu #2
-> File-submenu
-> file #1
-> file #2
-> file #3
-> file #4
-> Submenu #3
I开发者_JS百科n short: is there any way to create a list of dynamicaly updated QAction
s (grouped into the same QMenu
) depending on a model (derived from QAbstractListModel
) ?
Unfortunately there is no QMenuView
class but I found this promising implementation on the net: QMenuView
(qmenuview.h, qmenuview.cpp).
If your objective is just to update your menu actons with the item text that are available in the QAbstractListModel
, then the answer is Yes.
Here is a way..
Individual item's index can be obtained by using the following function.
QModelIndex QAbstractListModel::index ( int row, int column = 0,
const QModelIndex & parent = QModelIndex() ) const [virtual]
With the obtained index, the data can be obtained by,
QVariant QModelIndex::data ( int role = Qt::DisplayRole ) const
Then the text availalble in the index can be obtained by using,
QString QVariant::toString () const
Now with the obtained QString you can add an action to the menu.
QAction * QMenu::addAction ( const QString & text )
The thing you have to make sure is that, you should be able to traverse through all the items in the Model, so that you can obtain the index of the each and every item. Hope it helps..
To answer your short question, yes, there is. But you'll have to write it yourself.
The easy part would be to create a subclass of QAbstractListModel.
The hard part would be when you create your own view. Qt will let you create your own view, just like if you were to create your own model, but it would become so much more complex, since you've got to handle everything yourself.
It's entirely doable for a specific purpose, but it's also much more work than I think you want. So, like Gianni was saying, Qt's model-view framework isn't meant to be used this way.
No. Models can only be used with Views, as per the Model-View framework that Qt uses.
You can create a menu item and put QListView
into it using QWidgetAction
. Of course this menu cannot have submenus. The example below is in Python, but I hope it doesn't matter in this case.
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()
精彩评论