Source code for qconcurrency.widgets._dictmodelqcombobox_

#!/usr/bin/env python
"""
Name :          qconcurrency.widgets._dictmodelqcombobox_.py
Created :       Apr 16, 2017
Author :        Will Pittman
Contact :       willjpittman@gmail.com
________________________________________________________________________________
Description :   A QComboBox designed to work with a `DictModel`.

                Builtin QComboBoxes do not support nested items,
                this combobox is built to address this issue, in addition
                to being kept up to date with a Model as it is changed.
________________________________________________________________________________
"""
#builtin
from   __future__        import unicode_literals
from   __future__        import absolute_import
from   __future__        import division
from   __future__        import print_function
from   collections       import OrderedDict, MutableMapping
import functools
import uuid
#external
from   Qt                import QtWidgets, QtGui, QtCore
from   Qt.QtWidgets      import QSizePolicy
#internal
from qconcurrency.models import DictModel

__all__ = 'DictModelQComboBox'


#!TODO: test updates to model update qcombobox
#!TODO: allow different column for name/id for each different level

[docs]class DictModelQComboBox( QtWidgets.QComboBox ): """ ComboBox whose contents are determined by the contents of a :py:obj:`qconcurrency.models.DictModel`. The widget's contents are updated whenever the modelchanges. Example: .. image:: ../media/qconcurrency.widgets.DictModelQComboBox.png """
[docs] def __init__(self, dictmodel, indexinfo={'id':'_id','name':'name'} ): """ Args: dictmodel (DictModel): The model you want this QComboBox to display. indexinfo (dict, optional): Stores information on where the following information from the model: * `name`: the name, as it appears in the QComboBox * `id`: the id, generally a databaseId that corresponds with the name This can either be a single dict, if the id/name will be the same column for every level of the dictmodel, or it can be a dictionary with the level, and a dict of id/name for that specific level. .. code-block:: python # example 1: # applies to all levels of table indexinfo = { 'id':'_id', 'name':'name' } # example 2: # level-specific columns indexinfo = { 'departmenttype':{'id':'_id', 'name':'departmenttype_name'}, 'department' :{'id':'_id', 'name':'department_name'}, } """ QtWidgets.QComboBox.__init__(self) if not isinstance( dictmodel, DictModel ): raise TypeError( '`dictmodel` argument expected to be of type DictModel. Received %s' % type(dictmodel) ) self._dictmodel = dictmodel self._indexinfo = indexinfo self._modelindexes = {} # {combobox_index : QModelIndex} QModelIndex of each item in QComboBox self._level_specific_columns = False # True if self._indexinfo is in the format of { level : {'_id':..., 'name':,..}, level : {...} } # False if self._indexinfo is in the format of {'_id':..., 'name':...} if len(dictmodel.keys()): if isinstance( dictmodel[ dictmodel.keys()[0] ], MutableMapping ): self._level_specific_columns = True # Connections (update combobox every time model changes ) self._dictmodel.itemChanged.connect( self._handle_modelchange ) self.currentIndexChanged.connect(self.get_selected) # Load self._populate_combo()
def _handle_modelchange(self,*args,**kwds): self._populate_combo() def _populate_combo(self, _baseitem=None, _indent_lv=0): """ Rebuilds the available options """ if _baseitem is None: _baseitem = self._dictmodel selitem = self.get_selected() self.clear() for key in _baseitem.keys(): modelitem = _baseitem[ key ] self._modelindexes[ self.count() ] = modelitem.index() if self._level_specific_columns: name = self._indexinfo[ modelitem.level() ]['name'] else: name = self._indexinfo['name'] self.addItem( ' '*(3*_indent_lv) + modelitem.columnval( name ) ) # if modelitem has children if modelitem.rowCount(): self._populate_combo( _baseitem=modelitem, _indent_lv=_indent_lv+1 ) # TODO: # if this was the first item # restore the user's selection if possible if _baseitem is self._dictmodel: pass
[docs] def get_selected(self): """ Returns the modelitem corresponding to the user's selection. From this object, you can find any information from the model. Returns: The corresponding :py:obj:`DictModelRow` to the selected item. See Also: * :py:meth:`DictModelRow.columnvals` * :py:meth:`DictModelRow.columnval` * :py:meth:`DictModelRow.id` """ # if there are items in the list if self.currentIndex() != -1: modelitem = self._dictmodel.itemFromIndex( self._modelindexes[ self.currentIndex() ] ) return modelitem
[docs] def set_selected(self, modelindex): """ Sets the selected item in the QComboBox """ for combo_index in self._modelindexes: if self._modelindexes[ combo_index ] == modelindex: self.setCurrentIndex( combo_index ) return raise KeyError( 'Cannot find item in model with a modelindex of %s' % repr(_id) )
[docs] def get_item_indexinfo(self, modelitem): """ Returns all keys from `indexinfo` for a particular item. (taking into account item's nested-table-level, if `indexinfo` requires it) Returns: .. code-block:: python # if modelitem exists in model { 'id': 123, 'name': 'test name', } # if modelitem does not exist in model {} """ indexinfo = {} if self._level_specific_columns: for key in self._indexinfo[ modelitem.level() ]: indexinfo[ key ] = modelitem.columnval( self._indexinfo[ modelitem.level() ][ key ] ) else: for key in self._indexinfo: indexinfo[ key ] = modelitem.columnval( self._indexinfo[ key ] ) return indexinfo
[docs] def get_modelindex_from_index(self, index): """ Returns the :py:obj:`QtCore.QModelIndex` corresponding to the item with a :py:obj:`QtWidgets.QComboBox` index. """ return self._modelindexes[ index ]
if __name__ == '__main__': #external from Qt import QtWidgets #internal from qconcurrency import QApplication from qconcurrency.models import DictModel with QApplication(): model = DictModel( columns=['name'] ) model.add_row( 1, {'name':'one'} ) model.add_row( 2, {'name':'two'} ) model.add_row( 3, {'name':'three'} ) model[1].add_child( 10, {'name':'one-a'} ) model[1].add_child( 11, {'name':'one-b'} ) # This QComboBox can display a hierarchy (with indentation) combo = DictModelQComboBox( model, indexinfo={'id':'_id', 'name':'name'} ) combo.show() combo.set_selected( model[1][11].index() ) print( combo.get_selected().columnvals() )