Results 1 to 16 of 16

Thread: Rapid updating of QSortFilterProxyModel

  1. #1
    Join Date
    Dec 2014
    Posts
    48
    Thanks
    23
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4 PyQt3 PyQt4
    Platforms
    Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    Having a bit of trouble updating a proxyFilter for display in QtableView with 'rapidly' changing user selected ranges.

    Currently the workflow is setup as:

    [QAbstractTableModel]<--->[QSortFilterProxyModel]<--->[QtableView]<--->[GIS interface]

    When the user selects a range of objects(in this case, grid cells) via the GIS interface, I push a string of 'ID' values associated with this selection to the proxyFilter; allowing their display in the QtableView.

    Although this works for some selections in the upper section of the grid, it oddly doesn't elsewhere - and I'm a bit perplexed as to why.

    And the proxy-code bits:
    Within the __init__ function of the QtableView Class-
    Qt Code:
    1. self.tableData = CSVModel(fileName)
    2. self.proxyModel = QtGui.QSortFilterProxyModel()
    3. self.proxyModel.setSourceModel(self.tableData)
    4. self.proxyModel.setFilterKeyColumn(0)
    5. self.tableView.setModel(self.proxyModel)
    6.  
    7. ...
    8.  
    9. self.show()
    10. self.selectedObjects()
    11. self.iface.mapCanvas().selectionChanged.connect(se lf.selectedObjects)
    To copy to clipboard, switch view to plain text mode 

    The called function 'selectedObjects()'-
    Qt Code:
    1. def selectedObjects(self):
    2. ## self.proxyModel.reset()
    3. ## self.proxyModel.dynamicSortFilter(True)
    4. selectListIDs = []
    5. editLayer = self.iface.activeLayer().name()
    6. editLayer_Atrib = ftools_utils.getMapLayerByName(unicode("%s") % (editLayer))
    7. totalFeatures = range(editLayer_Atrib.featureCount())
    8. features = editLayer_Atrib.selectedFeatures()
    9. for feature in features:
    10. selectListIDs.append(list(feature.attributes())[0])
    11. revisedSelection = "|".join([str(cellID) for cellID in selectListIDs])
    12. print revisedSelection
    13. self.proxyModel.setFilterRegExp('^('+str(revisedSe lection)+')$')
    To copy to clipboard, switch view to plain text mode 

    I have tried dynamicSortFilter(), and reset() to no effects.
    Is there a fundamental bit for refreseh or something as such I am missing?


    Added after 1 15 minutes:


    ...Alright, after spending some time experimenting it was reviealed that either using the mouse scroll wheel or resizing the QtableView window populated the table with the properly selected range(s).

    Is there anyway to assure updates occur 'automatically', thus avoiding a initially blank display?
    Last edited by jkrienert; 29th December 2014 at 14:20.

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,420
    Thanks
    37
    Thanked 1,545 Times in 1,495 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    The filter should have emitted respective signals and the view should have reacted to that.
    E.g. rowsRemoved() if the new filter reduces the set

    Can you check if the proxy is emitting any of these?

    In general, if your model is not used with a different proxy in a second view, it might be worth considering implementing the filter in the model itself.

    Usually the model can do sorting/filtering more efficiently since it "knows" the data.
    E.g. if you add a filter criterium it could only have to test the currently accepted rows and remove those that no longer are instead of having to rebuild the whole set (which is what the QSortFilterProxyModel would have to do).

    Cheers,
    _

  3. The following user says thank you to anda_skoa for this useful post:

    jkrienert (29th December 2014)

  4. #3
    Join Date
    Dec 2014
    Posts
    48
    Thanks
    23
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4 PyQt3 PyQt4
    Platforms
    Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    It seems I might be making use of QSortFilterProxyModel incorrectly. Is it proper to distinguish the proxy in its own class or alright to implement within the TableView class?
    (Current needs only resort to a single TableView for this model)

    Sorting out the rows within the TableModel makes more sence - but this wont require a reset of the Model upon every new ID range would it?
    The documentation for filtration within the QAbstractTableModel seems to be limited, or perhaps I'm searching for the wrong aspects.
    Would this be accomplished by way of flags or ...?



    Also... Attached below is a visual reference to the current workings at the Ui level:

    Key for the video-link below:
    -Main window on upper right (with cursor) displays the GIS interface selection
    -Python console below main window displays string of 'IDs' (x) sent to setFilterRegExp(x)
    -Upper left window displays the updates to the QtableView care of the filterProxy

    http://www.instructables.com/files/o...NFI49RMVOI.mp4


    Added after 16 minutes:


    Short addition (for clarities sake)

    Images with proxy.rowcount() printed in lower right corner [python console] as selected from yellow squares in main window upper right [GIS program] ...[and (lagged) display of TableView upper left]~

    Partial initial display (notice proxy.rowCount() = 0):
    unfull_inital.jpg

    Full resultant display (after jostling the horizontal scroll bar left to right frequently):
    full_scrollJostle.jpg

    Nominal full display (initial, with proper proxy.rowCount()):
    full_inital-nominal.jpg

    ...and some code:
    Qt Code:
    1. self.iface.mapCanvas().selectionChanged.connect(se lf.selectedObjects)
    2. def selectedObjects(self):
    3. selectListIDs = []
    4. editLayer = self.iface.activeLayer().name()
    5. editLayer_Atrib = ftools_utils.getMapLayerByName(unicode("%s") % (editLayer))
    6. totalFeatures = range(editLayer_Atrib.featureCount())
    7. features = editLayer_Atrib.selectedFeatures()
    8. for feature in features:
    9. selectListIDs.append(list(feature.attributes())[0])
    10. revisedSelection = "|".join([str(cellID) for cellID in selectListIDs])
    11. print revisedSelection
    12. self.proxyModel.setFilterRegExp('^('+str(revisedSe lection)+')$')
    13. print self.proxyModel.rowCount()
    14. del revisedSelection
    To copy to clipboard, switch view to plain text mode 
    Last edited by jkrienert; 30th December 2014 at 00:23.

  5. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,420
    Thanks
    37
    Thanked 1,545 Times in 1,495 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    Quote Originally Posted by jkrienert View Post
    It seems I might be making use of QSortFilterProxyModel incorrectly. Is it proper to distinguish the proxy in its own class or alright to implement within the TableView class?
    I am not sure what you mean. Do you have a subclass of QSortFilterProxyModel?
    Or are you asking if it is OK to have the proxy model a member of the view?

    Quote Originally Posted by jkrienert View Post
    I
    Sorting out the rows within the TableModel makes more sence - but this wont require a reset of the Model upon every new ID range would it?
    Not necessarily.
    A reset usually indicates when a model's content changes in its whole, filtering differently could also be a combination of row insertions and removals.

    Quote Originally Posted by jkrienert View Post
    I
    The documentation for filtration within the QAbstractTableModel seems to be limited, or perhaps I'm searching for the wrong aspects.
    Would this be accomplished by way of flags or ...?
    It is not explicitly documented because it is not so much "filtering" as it is presenting a different view on the data.

    Lets for example assume a simple filtering criteria: only show every second row.
    The model could do that by returning half the number of the internal data as row count and multiply every incoming row number by two.
    Or it could create a lookup table. Or even reduce the internal data set and reload from source if the filter is changed.

    Anyway, the QSortFilterProxyModel should work, the other approach is mostly for potential optimizations

    Cheers,
    _

  6. The following user says thank you to anda_skoa for this useful post:

    jkrienert (30th December 2014)

  7. #5
    Join Date
    Dec 2014
    Posts
    48
    Thanks
    23
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4 PyQt3 PyQt4
    Platforms
    Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    Very helpful thoughts. Thank you.

    Is there any reason (of error on my part) for the 'print self.proxyModel.rowCount()' returning a zero? Even whilst when fiddling with the tableView window scale - the selected values eventually get displayed?

    ...is OK to have the proxy model a member of the view?
    Currently my proxyModel is a member of the TableView.
    It sounds like any optimization of TableView update delays might require moving filtering to the dataModel itself?
    (would there be any sence in having a signal sent to the setData() function in my AbstractModel everytime the RegExp(list) is updated, causing the Model to push a dataChanged() callout to the TableView as a sort of adhoc refresh?)

    The lookup-table sounds like it might be route to check out, and have not come across such a topic in documentation.
    Would this involve incorporating a function within the dataModel that receives a limiting-list of 'IDs' to populate tableView, everytime the user selects a new range?
    Last edited by jkrienert; 30th December 2014 at 12:56. Reason: added some liner notes

  8. #6
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,420
    Thanks
    37
    Thanked 1,545 Times in 1,495 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    Quote Originally Posted by jkrienert View Post
    Is there any reason (of error on my part) for the 'print self.proxyModel.rowCount()' returning a zero? Even whilst when fiddling with the tableView window scale - the selected values eventually get displayed?
    No, the API of the proxy should work independently of any view.
    I.e. if you call proxy.rowCount() before and after changing the filter it should print accurate values depending on both filter criteria.


    Quote Originally Posted by jkrienert View Post
    Currently my proxyModel is a member of the TableView.
    That's definitely ok.

    Quote Originally Posted by jkrienert View Post
    It sounds like any optimization of TableView update delays might require moving filtering to the dataModel itself?
    The proxy should react to any changes, direct or delayed, in the model. It should apply filter criteria changes immediately though.
    If you want to delay the filtering, another option would be to subclass QSortFilterProxyModel, implement filterAcceptsRow() and do the delay between setting of your own filter data (not re-using any of the options of QSortFilterProxyModel) and the call to invalidateFilter().

    Quote Originally Posted by jkrienert View Post
    (would there be any sence in having a signal sent to the setData() function in my AbstractModel everytime the RegExp(list) is updated, causing the Model to push a dataChanged() callout to the TableView as a sort of adhoc refresh?)
    No, the proxy should be doing all the necessary signalling already.

    Quote Originally Posted by jkrienert View Post
    The lookup-table sounds like it might be route to check out, and have not come across such a topic in documentation.
    Would this involve incorporating a function within the dataModel that receives a limiting-list of 'IDs' to populate tableView, everytime the user selects a new range?
    Yes. Your model would need a method to set the filter items, e.g. as a set of strings or a list.

    Maybe you could try the setup with a simple QStringListModel as the source model?

    Cheers,
    _

  9. #7
    Join Date
    Dec 2014
    Posts
    48
    Thanks
    23
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4 PyQt3 PyQt4
    Platforms
    Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    I might have spoken awkwardly earlier about needs. No delay in filtering is needed, nor wanted actually.
    The current setup requires the user to jostle the scroll bar rapidly to update TableView relative to the selected-values; of which I hope to alleviate.

    Conclusively - it seems like the proxyFilter will work great for my cause; ergo - not really needing further optimization.
    That is of course, only if I am able to overcome the hurdle(s) expressed in the video link below.
    The actions depicted lead me to feel I am missing a crucial command, or some important aspect of ordering things:
    (green tracers around the cursor when moved over the TableView indicate jostling the scroll wheel)
    -Notice that the 'new selection rowCount()' does not equal 'last selection rowCount()'
    http://www.instructables.com/files/o...OWI48CHLIO.avi
    Any clues?

    code snip of print region:
    Qt Code:
    1. features = editLayer_Atrib.selectedFeatures()
    2. for feature in features:
    3. selectListIDs.append(list(feature.attributes())[0])
    4. revisedSelection = "|".join([str(cellID) for cellID in selectListIDs])
    5. print 'before filter (last selection rowCount() ='+str(self.proxyModel.rowCount())+' )'
    6. self.proxyModel.setFilterRegExp('^('+str(revisedSe lection)+')$')
    7. print 'new selection IDs ='+str(revisedSelection)
    8. print 'after filter (new selection rowCount() ='+str(self.proxyModel.rowCount())+' )'
    To copy to clipboard, switch view to plain text mode 
    Last edited by jkrienert; 30th December 2014 at 16:00.

  10. #8
    Join Date
    Dec 2014
    Posts
    48
    Thanks
    23
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4 PyQt3 PyQt4
    Platforms
    Windows

    Question Re: Rapid updating of QSortFilterProxyModel

    Wow.
    The un-technical procedure of 'switch this' ~ 'change that' ~ 'then repeat' revealed my blunder.
    Changing activeRows from 35 to a number far exceeding any list length which will ever be used (1*10^12) completely dissolved my problems.
    Qt Code:
    1. class CSVModel(QtCore.QAbstractTableModel):
    2. activeRows = 35
    3. def __init__(self, fileName, parent=None):
    4. super(CSVModel,self).__init__()
    5. self.header = []
    6. self.rows = []
    7. self.fileName = fileName
    8. with open(self.fileName, "rb") as fileInput:
    9. for idx, row in enumerate(csv.reader(fileInput)):
    10. headerIDx = 0
    11. if idx is headerIDx:
    12. self.header.append(row)
    13. elif idx>headerIDx:
    14. items = [field for field in row]
    15. self.rows.append(items)
    16. self.rowsLoaded = CSVModel.activeRows
    To copy to clipboard, switch view to plain text mode 
    The question is, why exactly was this limiting things - and is there a more dynamic means of setting this initialization parameter?
    (fwiw, that sizable number adds absolutely no lag to opening the TableView - since the population of the table is based on the proxy selection )
    Last edited by jkrienert; 30th December 2014 at 18:46.

  11. #9
    Join Date
    Dec 2014
    Posts
    48
    Thanks
    23
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4 PyQt3 PyQt4
    Platforms
    Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    To revist this a bit,
    Maintaining 'activeRows' to a value equal to or greater than len(self.rows) <=[number of rows], works alright for small user selections - but is uncomfortably slow with large selections.
    Furthermore, my implementation of AbstractTableModel was to efficiently populate the table based on an incremental loading of rows into the TableView (hence initially setting 'activeRows' to equal 35.
    There must be a 'best of both worlds' possible, although my review of the doc's and forums has turned up little in the last week.
    Any lingering clues out there?

  12. #10
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,420
    Thanks
    37
    Thanked 1,545 Times in 1,495 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    As far as I can tell from your code you already do all the loading in the constructor, so you could just set activeRows to len(rows) or discard activeRows as it is not needed, no?

    Cheers,
    _

  13. #11
    Join Date
    Dec 2014
    Posts
    48
    Thanks
    23
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4 PyQt3 PyQt4
    Platforms
    Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    Indeed, substituting len(self.rows) does enable functionality without selection error, yet with larger selections there is a major slow-down in load time overall (it is populating the table based on every row of data selected).

    The benefit of implementing activeRows (when the number is 35 rather than len(rows)) allowed an initially small parcel of row population for rapid tableview display - then subsequent calling of more rows from the total model as needed (inferred) by moving the scroll bar. Simply put, this is like a 'pagination' method which only loads needed rows - greatly speeding up opening of the view and user workflow.

    Ideally, I am hopeful to nest the functionality observed with 'activeRows' within an abstractModel. Is it proper to have a model within a model - and any clues as to if this would be a manner to fulfill said functionality ?
    (If my logic is skewed, please bear in mind its product of a novice skill-set)

    Maybe this logic sketch will help describe my intent:
    sketchUp.jpg

    Also, this might help; written as current w/out the sketch implemented above:
    Qt Code:
    1. # QTableView Ui handeler (aka tableView)
    2. class CSVeditor(QtGui.QDialog, QtGui.QWidget):
    3. # Initalize primary connections between [Data]<->[Proxy]<->[View]
    4. # and commit fundamental tableView formating
    5. def __init__(self, iface, fileName, parent=None):
    6. super(CSVeditor, self).__init__(parent)
    7. self.iface = iface
    8. self.fileName = fileName
    9. self.tableView = QtGui.QTableView(self)
    10. self.tableData = CSVModel(fileName)
    11. self.proxyModel = QtGui.QSortFilterProxyModel()
    12. self.proxyModel.setSourceModel(self.tableData)
    13. self.proxyModel.setFilterKeyColumn(0)
    14. self.tableView.setModel(self.proxyModel)
    15. cellSelectionModel = self.tableView.selectionModel()
    16. cellSelectionModel.selectionChanged.connect(self.selRange)
    17. self.connect(self.proxyModel,QtCore.SIGNAL('dataChanged(QModelIndex,QModelIndex)'),self.shareChanges)
    18. # create buttons and connections
    19. self.pushButtonSave = QtGui.QPushButton(self)
    20. self.pushButtonSave.setText("Save Changes")
    21. self.pushButtonSave.clicked.connect(self.on_pushButtonSave_clicked)
    22. self.pushButtonClose = QtGui.QPushButton(self)
    23. self.pushButtonClose.setText("Exit")
    24. self.pushButtonClose.clicked.connect(self.Cancel)
    25. # implement read only parameters (column specific)
    26. self.ReadOnly = ReadOnly(self.tableView)
    27. self.tableView.setItemDelegateForColumn(0,self.ReadOnly)
    28. # open Ui and populate based upon QgsLayer feature(s) selection
    29. self.show()
    30. self.selectedObjects()
    31. self.iface.mapCanvas().selectionChanged.connect(self.selectedObjects)
    32.  
    33. # retrieve list of all selected objects on mapCanvas to proxy-filter displayed rows in CSV editor
    34. def selectedObjects(self):
    35. selectListIDs = []
    36. editListIDs = []
    37. editLayer = self.iface.activeLayer().name()
    38. editLayer_Atrib = ftools_utils.getMapLayerByName(unicode("%s") % (editLayer))
    39. totalFeatures = range(editLayer_Atrib.featureCount())
    40. features = editLayer_Atrib.selectedFeatures()
    41. for feature in features:
    42. selectListIDs.append(list(feature.attributes())[0])
    43. revisedSelection = "|".join([str(cellID) for cellID in selectListIDs])
    44. self.proxyModel.setFilterRegExp('^('+str(revisedSelection)+')$')
    45. print self.proxyModel.rowCount()
    46. print self.tableData.rowCount(self)
    47.  
    48. # nessecary slot and signal functions
    49. @QtCore.pyqtSlot()
    50. def on_pushButtonSave_clicked(self):
    51. self.tableData.saveIt()
    52. def Cancel(self):
    53. self.close()
    54.  
    55. # main startup
    56. if __name__ == "__main__":
    57. app = QtGui.QApplication(sys.argv)
    58. app.setApplicationName('CSV Editor')
    59. main = CSVeditor(fileName)
    60. main.show()
    61. sys.exit(app.exec_())
    62.  
    63. #---------------------------------------------------------------------------------------------------
    64.  
    65. # readOnly column establishments in QtableView
    66. class ReadOnly(QtGui.QItemDelegate):
    67. def __init__(self, parent):
    68. QtGui.QItemDelegate.__init__(self, parent)
    69. def createEditor(self, parent, option, index):
    70. item = QtGui.QLineEdit(parent)
    71. item.setReadOnly(True)
    72. return item
    73. def setEditorData(self, editor, index):
    74. editor.blockSignals(True)
    75. editor.setText(index.model().data(index))
    76. editor.blockSignals(False)
    77. def setModelData(self, editor, model, index):
    78. model.setData(index, editor.text())
    79.  
    80. #---------------------------------------------------------------------------------------------------
    81.  
    82. # QTableModel data handeler (aka tableData)
    83. class CSVModel(QtCore.QAbstractTableModel):
    84.  
    85. activeRows = 1000000000000 #<=== this value is equivalent to len(rows) or ensuring that all rows are initially filtered via proxy
    86. # load in csv data, and establish inital limiter on loaded row count (for efficency)
    87. def __init__(self, fileName, parent=None):
    88. super(CSVModel,self).__init__()
    89. self.header = []
    90. self.rows = []
    91. self.fileName = fileName
    92. with open(self.fileName, "rb") as fileInput:
    93. for idx, row in enumerate(csv.reader(fileInput)):
    94. headerIDx = 0
    95. if idx is headerIDx:
    96. self.header.append(row)
    97. elif idx>headerIDx:
    98. items = [field for field in row]
    99. self.rows.append(items)
    100. self.rowsLoaded = CSVModel.activeRows
    101.  
    102. # relative axis index counts
    103. def rowCount(self,index):
    104. if not self.rows:
    105. return 0
    106. if len(self.rows) <= self.rowsLoaded:
    107. return len(self.rows)
    108. else:
    109. return self.rowsLoaded
    110. def columnCount(self,index):
    111. return len(self.header[0])
    112.  
    113. # extended data retrieval based upon Qtableview / proxyFilter demands (scrolling)
    114. def canFetchMore(self,index):
    115. if len(self.rows) > self.rowsLoaded:
    116. return True
    117. else:
    118. return False
    119. def fetchMore(self,index):
    120. reminder = len(self.rows) - self.rowsLoaded
    121. itemsToFetch = min(reminder,CSVModel.activeRows)
    122. self.beginInsertRows(index,self.rowsLoaded,(self.rowsLoaded+itemsToFetch-1))
    123. self.rowsLoaded += itemsToFetch
    124. self.endInsertRows()
    125. def addRow(self,row):
    126. self.beginResetModel()
    127. self.rows.append(row)
    128. self.endResetModel()
    129.  
    130. # source value data for cell(s) in QtableView
    131. def data(self,index,role):
    132. if index.isValid() and role == Qt.DisplayRole:
    133. return self.rows[index.row()][index.column()]
    134.  
    135. # establish header data
    136. def headerData(self,section,orientation,role):
    137. if role != Qt.DisplayRole:
    138. return
    139. if orientation == Qt.Vertical:
    140. return int(section)
    141. if orientation == Qt.Horizontal:
    142. return self.header[0][section]
    143.  
    144. # write changes from QtableView and proxyFilter to (self) tableData
    145. def setData(self, index, value, role):
    146. if index.isValid() and role == Qt.EditRole:
    147. self.rows[index.row()][index.column()]=str(value)
    148. self.dataChanged.emit(index, index)
    149. return True
    150. return False
    151.  
    152. # distinguish capabilities of total cells in tableData model
    153. def flags(self, index):
    154. return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
    155.  
    156. # one toast & peanutbutter-jelly sandwitch please
    157. def saveIt(self):
    158. with open(self.fileName, "wb") as fileOutput:
    159. writer = csv.writer(fileOutput)
    160. writer.writerow(self.header[0])
    161. for rowNumber in range(len(self.rows)):
    162. fields = self.rows[rowNumber]
    163. writer.writerow(fields)
    To copy to clipboard, switch view to plain text mode 
    Last edited by jkrienert; 4th January 2015 at 14:09.

  14. #12
    Join Date
    Dec 2014
    Posts
    48
    Thanks
    23
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4 PyQt3 PyQt4
    Platforms
    Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    Or, can a QSortFilterProxyModel be a trunk; housing, filtering, providing, and applying changes?

    oRthis.jpg

    In hindsight, this seems to not be a fishable alternative.
    Last edited by jkrienert; 4th January 2015 at 15:41.

  15. #13
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,420
    Thanks
    37
    Thanked 1,545 Times in 1,495 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    A proxy model is always in between two entities, e.g. between the actual data model and the view, or between the data model and another proxy, or between two other proxies.
    I.e. it always works on a source model.

    Your own model can do anything you want it to do, including sorting, filtering, data manipulation, etc.

    The proxy model approach is mostly a matter of convenience, e.g. not having to deal with sorting/filtering in your own code, or when there is no access to the source code of the actual model or changing it is not an option.

    Cheers,
    _

  16. The following user says thank you to anda_skoa for this useful post:

    jkrienert (5th January 2015)

  17. #14
    Join Date
    Dec 2014
    Posts
    48
    Thanks
    23
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4 PyQt3 PyQt4
    Platforms
    Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    What would be the recommended manner of filtering within an AbstractModel?

    Also, since the entirety of the source data is loaded and saved in the AbstractModel (used here), does internal filtering run into (write) complexities ? Hopefully not, since I am presuming the filtering method would mearly send a list of rows to table view, rather then edit the housed bulk data.

    If this route is taken, there would be less overhead I assume (than with the QsortFilterProxyModel @ current)?

  18. #15
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,420
    Thanks
    37
    Thanked 1,545 Times in 1,495 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    Well, since you want to eventually write the data again, if I remember correctly, you will have to edit the bulk data.

    One approach for filtering could be via a list of indexes into the actual data.

    When unfiltered, the index list would be as long as the number of data rows, each entry being the index of the entry itself
    Qt Code:
    1. [ 0, 1, 2, 3, 4, 5 .... ]
    To copy to clipboard, switch view to plain text mode 

    When you apply a filter, this index list gets reduced to the "visible" portion of the data.
    Lets say the first two rows would be discarded by the filter:
    Qt Code:
    1. [ 2, 3, 4, 5 .... ]
    To copy to clipboard, switch view to plain text mode 

    rowCount() would always be the length of this list. When data needs to be read or written, the row() of QModelIndex is the index into this second list, the value at that position is the actual row in the data list.

    Cheers,
    _

  19. #16
    Join Date
    Dec 2014
    Posts
    48
    Thanks
    23
    Thanked 1 Time in 1 Post
    Qt products
    Qt3 Qt4 PyQt3 PyQt4
    Platforms
    Windows

    Default Re: Rapid updating of QSortFilterProxyModel

    While the following (code below) runs without error, the tableView is never populated with rows.
    Yet if I call 'print self.rows' after the 'filterData' function is ran - the list depicts every selected value which should be
    published to the tableView.

    Is this an incorrect means of doing the filtering directly; e.g. - within the AbstractModel?

    Qt Code:
    1. class CSVModel(QtCore.QAbstractTableModel):
    2.  
    3. # load in csv data, and establish inital limiter on loaded row count (for efficency)
    4. def __init__(self, iface, fileName, parent=None):
    5. super(CSVModel,self).__init__()
    6. self.iface = iface
    7. self.rows = []
    8. self.fileName = fileName
    9. self.loadCSV(fileName)
    10. self.rowsLoaded = len(self.rows)
    11. self.iface.mapCanvas().selectionChanged.connect(self.filterData)
    12.  
    13. def loadCSV(self,fileName):
    14. self.header = []
    15. self.data = []
    16. with open(self.fileName, "rb") as fileInput:
    17. for idx, row in enumerate(csv.reader(fileInput)):
    18. headerIDx = 0
    19. if idx is headerIDx:
    20. self.header.append(row)
    21. elif idx>headerIDx:
    22. items = [field for field in row]
    23. self.data.append(items)
    24.  
    25. def filterData(self):
    26. editLayer_Atrib = ftools_utils.getMapLayerByName(unicode(self.iface.activeLayer().name()))
    27. totalFeatures = range(editLayer_Atrib.featureCount())
    28. features = editLayer_Atrib.selectedFeatures()
    29. self.beginResetModel()
    30. for feature in features:
    31. ID = feature.attributes()[0]
    32. self.rows.append(self.data[ID])
    33. self.endResetModel()
    34.  
    35. # relative axis index counts
    36. def rowCount(self,index):
    37. if not self.rows:
    38. return 0
    39. if len(self.rows) <= self.rowsLoaded:
    40. return len(self.rows)
    41. else:
    42. return self.rowsLoaded
    43. def columnCount(self,index):
    44. return len(self.header[0])
    45.  
    46. # source value data for cell(s) in QtableView
    47. def data(self,index,role):
    48. if index.isValid() and role == Qt.DisplayRole:
    49. return self.rows[index.row()][index.column()]
    50.  
    51. # establish header data
    52. def headerData(self,section,orientation,role):
    53. if role != Qt.DisplayRole:
    54. return
    55. if orientation == Qt.Vertical:
    56. return int(section)
    57. if orientation == Qt.Horizontal:
    58. return self.header[0][section]
    59.  
    60. # write changes from QtableView and proxyFilter to (self) tableData
    61. def setData(self, index, value, role):
    62. if index.isValid() and role == Qt.EditRole:
    63. self.rows[index.row()][index.column()]=str(value)
    64. self.dataChanged.emit(index, index)
    65. return True
    66. return False
    67.  
    68. # distinguish capabilities of total cells in tableData model
    69. def flags(self, index):
    70. return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
    71.  
    72. # Save Changes Made
    73. def saveIt(self):
    74. with open(self.fileName, "wb") as fileOutput:
    75. writer = csv.writer(fileOutput)
    76. writer.writerow(self.header[0])
    77. for rowNumber in range(len(self.rows)):
    78. fields = self.rows[rowNumber]
    79. writer.writerow(fields)
    To copy to clipboard, switch view to plain text mode 


    EDIT:
    At this point I'm inclined to wonder if this thread should be laid to rest - and the topic moved anew considering the use of SortFilterProxyModel is possibly going to be bypassed...
    Last edited by jkrienert; 4th January 2015 at 18:45.

Similar Threads

  1. Replies: 0
    Last Post: 26th June 2014, 14:03
  2. QSortFilterProxyModel not updating
    By karn862 in forum Qt Programming
    Replies: 1
    Last Post: 31st October 2010, 23:16
  3. Rapid Update of Pixmap
    By dbrmik in forum Qt Programming
    Replies: 5
    Last Post: 23rd April 2009, 10:49
  4. QSortFilterProxyModel
    By evgenM in forum Qt Programming
    Replies: 1
    Last Post: 18th March 2007, 11:53

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.