Qt modelview: views
TODO:
rewrite this documentation, with special handling of traps in various view types (tree, table, list, signals, etc)
TODO:
using tmacoreLogin's model/view as a model, document a minimal, but full example of signal handling for a view - one that covers all of the common traps.
Keeping Headers with Empty Items
TODO:
somewhere in TMA codebase I came up with a good auto-adjusting solution. Also see QTBUG-36665
- see
appsettingsmodel
Views lose their header names/sizing if a model, or rootIndex is set without at least an empty QStandardItem for each column.
NOTE:
You can quickly create, then immediately delete a model item, and it seems to preserve your header settings
Add Delegates to Views
TODO:
- problems styling delegates/workarounds
- parenting issue
Add Widgets to Views
TODO:
- mention is anti-pattern
- workflow
Handling DoubleClick
TODO:
doubleclick registers lmb, or rmb -- also be careful that doubleclick was registered on same item, clearing selection
Adding ContextMenu to Views
class MyTableView(QtWidgets.QTableView):
def __init__(self):
super(MyTableView, self).__init__()
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self._handle_customContextMenuRequested)
def _handle_customContextMenuRequested(self, local_pos):
menu = QtWidgets.QMenu() # your custom menu
global_pos = self.mapToGlobal(local_pos)
menu.popup(global_pos)
QActions
TODO:
how to use
DataWidgetMapper (widget represents row)
DataWidgetMappers are good at setting up a widget to represent one particular row in your data. Ex: more info about a particular user.
WARNING:
this may not work, I was unable to reproduce quickly in production
with QApplication():
stylesheetdir = StyleSheet().stylesheetdir()
# Create Model
model = QtGui.QStandardItemModel()
item_a = QtGui.QStandardItem('a')
item_b = QtGui.QStandardItem('b')
item_c = QtGui.QStandardItem('c')
model.setItem( 0, 0, item_a )
model.setItem( 1, 0, item_b )
model.setItem( 2, 0, item_c )
# Create widget
text = QtWidgets.QLineEdit()
# Create Mapper & map model to widget
mapper = QtGui.QDataWidgetMapper()
mapper.setModel( model )
mapper.addMapping( text, 0 ) # sets column to observe
# toFirst, toNext, toLast
# used to set the current ROW displayed by the widget
text.show()
mapper.toFirst() # 'a'
mapper.toNext() # 'b'
mapper.setCurrentModelIndex( item_c.index() ) # 'c'
mapper.toNext() # no items left, remains at 'c'
QAbstractItemView
Implementing your own custom views.
ftp://ftp.informatik.hu-berlin.de/pub/Linux/Qt/QT/videos/DevDays2011/TrainingDay/DevDays2011_-_Advanced_Qt_-_A_Deep_Dive.pdf | excellent overview information about advanced Qt topics |
Widgets
QTreeView
TODO:
adding a contextmenu to treeview
# expanding items class YourModel(QtGui.QStandardItemModel): def load_modelindex_children(self, modelindex): child_data = your_getter() if not len(child_data): # create empty row so shows as expandable self.append_child_data(child_data) # column widths/stretch header = tree.header() header.setStretchLastSection(False) # VERY IMPORTANT, overrides setSectionResizeMode when set header.setSectionResizeMode(0, header.Stretch) tree.setColumnWidth(1, 200) tree.setColumnHidden(0, True)
Troubleshooting
Model Data Mysteriously gets Deleted
I have a hunch this is often caused by connecting things to the
model.dataChanged
signal.This signal is needed by the view. If you are also using it to inspect the view, you may have a race condition.
I think that wrapping
model.update()
to emit a different signal after it has been updated may make programs more stable.NOTE:
My best results have come from overriding
QAbstractItemView.dataChanged()
, then emitting my owndata_changed
signal (guaranteed not to be handled by qt). This guarantees that the view has received an update from the model already.