Warning: as of 22/April of 2018 there have been some serious changes in the tree management, which are still not documented. We hope, although that, as an API they are full compatible
Table of Contents
- General preview
- General Auxiliary functions
- class TreeFormat(object):
- class GuideItem(QStandardItem):
- class GuideItemModel(QStandardItemModel):
- Implementation details
- Attributes
- datos
- name
- vista
- orthogonal
- init(self,parent=None):
- traverse(self,base=None):
- numRecords(self,type=None):
- asDict(self):
- asHdr(self,**parms):
- asDictFilter(self,filter):
- asHdrFilter(self,filter,**parms):
- lenPayload(self,leafOnly=False):
- searchHierarchy(self,valueList,role=None):
- setStats(self,switch):
- data(self,index,role):
- MonkeyPatch section
We have developed the classes here to be models for the Qt Model-View Programming.
They are tailored for the case we have to deal with two dimensional data, and one of the dimensions can be also viewed as a tree
o ------> row
|-> row
|
|--> row
|--> row
Each row is
row head + data col1 + data col2 + ....
To represent the tree we have developed the GuideItemModel (from QStandardItemModel)
And to handle each item individually we created the GuideItem (from QStandardItem
Column 0 of each row is the row header and has two data inside it:
- the internal key accessed via the Qt.Role (Qt.QUserRole +1) and
- an human readable value. Accessd via the Qt.Role Qt.QDisplayRole
When we navigate the tree we access for each row the row header.
The other columns of the row is what we call payload, and can be accessed thru the header individually or as a block
Implement a binary search inside a QStandardItemModel.
Extremely faster than the .find method.
Only perform the search in one hierarchical level
currently REQUIRES that the children be ordered by default. As is the case in danacube
Actual binary search code was retrieved from StackOverflow. Sadly i lost the reference
-
Input Parameters
- item (QStandardItem) parent of the level we are searching for
- value value we are searching for
- role the Qt Role for the value. (An item can contain different data associated to different roles)
-
Returns
the item which has that value or None
-
Programming notes.
Look for GuideModel.searchHierarchy for usage
TODO Make a variant which do not requires previous order
This class mantains the format definition (for a display or background roles) for the value contents of an item in GuideItemModel (see the .data method)
NOTES The current implementation is pretty lame, and compatibility forced from previous code, and the parameters needed by util.numeros.fmtNumber
Boolean. IF the tree will keep statistics for each row
A dictionary containing following entries
- yellowoutliers Boolean. If statistic ooutliers in a row will be backgrounded in yellow. Default True
- rednegatives Boolean. If negative values will be backgrounded in red- Default True
- thousandseparator a Char. character for thousands separation. Default "." (spanish style)
- decimalmarker a Char. decimal character. Default "," (spanish style)
- decimalplaces number of default decimal places for floating point values. Default 2
- Input parameters.
A kwparms dict of
- stats Boolean. IF the tree will keep statistics for each row
- __format __ A dictionary containing following entries
- yellowoutliers Boolean. If statistic outliers in a row will be backgrounded in yellow. Default True
- rednegatives Boolean. If negative values will be backgrounded in red- Default True
- thousandseparator a Char. character for thousands separation. Default "." (spanish style)
- decimalmarker a Char. decimal character. Default "," (spanish style)
- decimalplaces number of default decimal places for floating point values. Default 2
This class is an specialization of QStandardItem for use with GuideItemModel
In our current implementation GuideItem instantiates every element of the row. We view a row as containing:
row head + payload (the equivalent of row head + data col1 + data col2 + ....
Column 0 of each row is the row header and has two data inside it:
- the internal key accessed via the Qt.Role (Qt.QUserRole +1) and
- an human readable value. Accessed via the Qt.Role Qt.QDisplayRole
When we navigate the tree we get the row header. for each row
The other columns of the row is what we call payload, and can be accessed thru the header individually or as a block
both are given value during the application workflow
A value for the item which to return
a dict with following entries (excluding null values during computation)
- avg average
- std standard deviation
- max max value
- median median value
- min minimum value
- out_low upper threshold for low outliers
- out_hig lower threshold for upper outliers
Methods reimplemented from QStandardItem.
See documentation there
We define three different types of items (really of the rows)
- TOTAL which correspond to the grand total
- BRANCH rows which have children
- LEAF rows without children
We make Qt.UserRole +1 as default role if Qt.DisplayRole returns None
We define three types of fields
- 'key' the UserRole content
- 'value' the DisplaRole content
- a number: the playload column
hierachical level of the row
- returns
- an integer with the current level, starting from 0
We seach for a specific value inside the children of the current row
-
Input parameters
- value the value to be searched for. Type has to be QStandardItem.setData compatible
- role Qt.Role, if not specified Qt.UserRole +1
-
Returns
- An item which contains this value or None
-
Implementation notes
Wide faster and hierachical level specific than model().find Currently the values of the rows have to be sorted in advance
Returns the item at column col for the current row
-
Input parameters
- col the index of the column requested. 0 is not allowed
-
Returns
- The item at that column or None if it doesn't exist
-
Implementation notes
Surprisingly such a method does not exist at QStandardItem
Method to set , for the current row, a payload entry at position col with value value . It's a destructive function: if the column already exist is replaced with the new version Can only be executed on GuideItems with column 0 (the head of the row)
-
Input parameters
- col the index of the column to be set. 0 is not allowed
- value the value to be inserted. Type be QStandardItem.setData compatible
- role Qt.Role, if not specified Qt.UserRole +1
-
returns
- a reference to the new column item or None (if 0 happens to be specified at col)
-
Implementation notes.
QtStandardItem.insertColumn and QStandardItem.insertColumns are most probably faulty, and, even if worked, the need to create a list of QStandardItem (or derived) before using QStandardItem.insertColumns is unconfortable to program. Column 0 is not allowed because (as it is the head of the row,i.e. self) it would destroy self
Method to set , for the current row, a payload entry at position col with value value . If the item already exist it is updated, else is created Can only be executed on GuideItems with column 0 (the head of the row)
-
Input parameters
- col the index of the column to be set. 0 is not allowed
- value the value to be inserted. Type be QStandardItem.setData compatible
- role Qt.Role, if not specified Qt.UserRole +1
-
returns
- a reference to the new column item or None (if 0 happens to be specified at col)
EXPERIMENTAL
generator to navigate the payload elements inside a row
- Implementation note:
If executed over an arbitrary item it starts on the following column
If the corresponding column is still not defined it returns a QStandardItem, not a real column
If we use expanded functionality of the class we must code like
for item in self.rowTraverse():
if type(item) == QStandardItem:
pass
else:
...
Returns a list with the values of the payload (columns 1 .. of the current row
Updates in one step all the values of the payload
If len(lista) < len(vector) only those values get updated; otherwise the vector will be expanded as apropiate
- Input parameters
- lista a list with the new values for the vector
Length of the payload for this row
-
Returns
- Length of the payload
-
implementation notes
QStandardItem.columnCount() does not seem to work, it should be lenPayload = columnCount -1. In this case the use of the generator seems not to be that performant
Returns the data at payload position idx for the current row It corresponds to column idx + 1
-
Input parameters
- idx the index inside the payload.
-
Returns
- The item at that column or None if it doesn't exist
Method to set , for the current row, a payload column at index idx with value value . If the item already exist it is updated, else is created It will correspond to column = idx +1
-
Input parameters
- idx the index of the payload to be set.
- value the value to be inserted.
-
returns
- a reference to the new column item or None
returns the internal key of the current row
returns the display value for the current row
Returns data from an specified index and role inside the payload.
-
Input parameter
- idx the index in the payload
- role data associated to that role. Default is Qt.UserRole +1
-
Implementation notes For compatibility with other tree implementations
- parms
- content = ('key','value')
- format = ('simple','string','array')
- delimiter (only if format == string)
- sparse = boolean. Default False
obtains the key of the row in string format (i.e each hierachical step separated by DELIMITER ).
Key is the DB internal key and Qt.UserRole + 1 data
obtains the description of the row in array format (i.e each hierachical step in an occurrence of a list ).
Description is the presentation values of the keys and Qt.DisplayRole data
returns the statistics gathered for this row
Boolean to check if the row is a totalizer row
Boolean to check if the row is a branch row (has children of its own)
Boolean to check if the row is a leaf row (has no children of its own)
Returns the payload item to the original value
If executed at the header restores the full row
Sets the current value as backup value (self.originalValue) for a payload column If executed at the header restores the full row
For orthogonality sake, we allow to process the full row if executed at the header
Executes the statistic gathering routine at the row
Only executable at the row header
The statistics routine is util.numeros.stats and creates a dict with following entries (excluidn null values)
- avg average
- std standard deviation
- max max value
- median median value
- min minimum value
- out_low upper threshold for low outliers
- out_hig lower threshold for upper outliers
We get the value of the elements in the payload which are not Null (None)
Only to be executed at the head
- return
- A list with the values of the non null payload
- A list with the indexes of those elements
We get the value of the elements in the payload at this level and its parents. Only return those which are not Null at this level
Only to be executed at the head
- return
- A list for each hiearchical level of lists of values of the non null payload
- A list with the indexes of those elements
GuideItemModel is an extension of Qt's QStandardItemModel, specially crafted for holding trees with vector like items (defined as GuideItem, see below). They also have some attributes and methods specific for the handling of danacube
In danacube the guides of a view (row_hdr_idx, col_hdr_idx) are implemented with this class
Of the methods, following are reimplemetation of base methods, and are task tailored * __init__ * data
Those are provided as extensions to the general model I miss * traverse * numRecords * lenPayload * __searchHierarchy
The rest is task tailored for danacube, but the as* methods can be used as a frame for less specific work
A TreeFormat object. Holds the formating info for the tree. THe name is merely historical
Holds the name of the guide on which the tree is based. Is not mandatory
En que vista se utiliza (activado en core.vista.toNewArray*
Cual es el otro modelo que complementa la vista (activado en core.vista.toNewArray*)
Generator to navigate the tree. It only reads the head items of each rows
- Input parameter
- base initial item to process. default is .invisibleRootItem, but this element is not yielded
- returns
- next item in sequence
TODO
It would be nice to put it functionally on par to util.treebasic.TreeModel
Returns the number of items in the tree. The standard model rowCount method only gives the number of direct children of the first level
-
Input Parameters
- type integer the type to be searched for (an integer above 1000. See Qt.StandardItem.type())
-
returns The number of items in the tree
returns a dictionary view of the tree. The purpose is to allow a quasi direct access to the item based on the key values
*Returns
A dictionary whose keys are the fullkeys of the items. The fullkey is a string which concatenates the keys of the tree hierarchy of the item. To each item is attached, as value a dict with following entries
-
idx the ordinal of the item in the tree traversal
-
objid a reference to the item itself
-
Implementation notes Trees are meant to be navigated via traversal, but are complex to use as direct access (via the key). This function generates a dictionary which can be accessed directly via the key. Python didn't allowed a QStandardItem as a dict key, so we had to use the key as index. It is the full hierachical key to ensure unicity
Returns a list with an element for every item in the tree. Each element contains data which can be used as headers
- Input parms.
A kwparm whith can contain any of the following entries
-
parms used by GuideItem.getFullHeadInfo
- content = ('key','value'). Default 'value'
- format = ('simple','string','array'). Default 'simple'
- delimiter (only if format == string). Concatenation delimiter
- sparse = boolean. Default False
-
specific parms
- offset. a number of blank elements to precede the list
- normArray. a number only if format = array, all elements will have at least this length (num of entries)
-
returns A list with one element for each item in the tree. What is offered in each element varies according to the input parameters:
- Content
- if 'key' we return the internal key of the item (the domain's code of the guide).
- if 'value' its display text (the domain's desc of the guide
- format
- if 'simple' just the value for this entry (production rule in guide)
- if 'string' the hierarchy of keys concatenated in one string
- if 'array' an array with the hierarchy of keys
- Content
-
Implementation notes In danacbue the need for header structures related to the guides has been an usual task. This function is a generic one to generate such headers
returns a dictionary view of the tree, but only those items for which the filter applies.
- Input parameter
- filter a function which returns a boolean and accept a QStandardItem as parameter
*Returns
A dictionary whose keys are the fullkeys of the items. The fullkey is a string which concatenates the keys of the tree hierarchy of the item. To each item is attached, as value a dict with following entries
-
idx the ordinal of the item in the filtered tree traversal
-
objid a reference to the item itself
-
oidx the original index without filter
-
Implementation notes Enhanced version of the asDict method. Allows any filtering
Returns a list with an element for every item in the tree for which the filter is True. Each element contains data which can be used as headers
-
Input parms.
-
filter a function which returns a boolean and accept a QStandardItem as parameter
-
A kwparm whith can contain any of the following entries
- parms for getFullHeadInfo
- content = ('key','value')
- format = ('simple','string','array')
- delimiter (only if format == string)
- sparse = boolean. Default False
- specific parms
- offset. a number
- normArray. a number only if format = array
- parms for getFullHeadInfo
-
returns A list with one element for each item in the filtered tree. What is offered in each element varies according to the input parameters:
- Content
- if 'key' we return the internal key of the item (the domain's code of the guide).
- if 'value' its display text (the domain's desc of the guide
- format
- if 'simple' just the value for this entry (production rule in guide)
- if 'string' the hierarchy of keys concatenated in one string
- if 'array' an array with the hierarchy of keys
- Content
-
Implementation notes Enhanced version of the asDict method. Allows any filtering
Returns the maximum lenght of the payload for each item. (in fact the length of the orthogonal model) QStandardItem.columnCount() give some incorrect results
-
Input parameters
- leafOnly. Boolean. counting is done only for leaf elements
-
returns
- the number of columns expected
-
Programming notes Implementation will vary, most probably
Does a search thru all the hierarchy given the key/value data
-
Input parameters
- valueList an array with the hierachy data to be searched
- role the Qt.Role the data is associated with
-
Returns An Item of the tree which mastches the valueList
-
Programming notes Which data is searched depends on the role. Qt.UserRole +1 searches for internal keys; Qt.DisplayRole for description
Sets (and evaluate) the per row statistics. While they are per row(item) are calculated once per tree
-
Input parameters:
- switch a Boolean, activates or deactivates the statistics
-
returns none. If activated loads the items with the corresponding statistics
Reimplementation of QStandardItemModel.data for the needs of danacube. It will be invoked when a view associated with the model is redrawn
-
Input parameters
- index a QIndexModel which identifies the item
- role which Qt.Role are we handling
-
Programming notes We define special actions for following cases
- Qt.TextAlignmentRole. For column 0 -the value- left aligned, else -the vector- right aligned (they are numbers)
- Qt.BackgroundRole. Color if the conditions in TreeFormat are met
- Qt.DisplayRole. Formats the numeric vector
Definition of synonyms of methods of the previous classes, used either for simplicity or compatibility
- GuideItemModel.len = GuideItemModel.numRecords
- GuideItem.childCount = GuideItem.rowCount
- GuideItem.gpi = GuideItem.getPayloadItem
- GuideItem.spi = GuideItem.setPayloadItem
- GuideItem.getLevel = GuideItem.depth
- GuideItem.getHeader = GuideItem.getFullHeadInfo