PDA

View Full Version : Custom Filter(s) within QAbstractTableModel (PyQt)



jkrienert
5th January 2015, 14:25
Hello.
This is a carry-over from another thread Rapid Updates of QSortFilterProxyModel (http://www.qtcentre.org/threads/61220-Rapid-updating-of-QSortFilterProxyModel), where it was recommended filtering be bypassed in QSortFilterProxyModel - and move such responsibilities into the trunk AbstractTableModel.

Snags were hit rather quickly in this transition (novice with script here).
The code below results in correct headers for my QtableView window, but no rows ever populate.
To check why this was so, a print statement for 'rowCount()' was added after selected rows were 'updated', and appropriately reflects the size of the selection, yet the tableView is not updated...
10868

Most importantly, when using the save function, the *.CSV source file contains only the rows selected and all others are lost (overwritten).
The errors are probably obious - but wiser eyes might observe such quicker then I.
Thanks for any help.

# QTableModel data handeler (aka tableData)
class CSVModel(QtCore.QAbstractTableModel):

# load in csv data, and establish inital limiter on loaded row count (for efficency)
def __init__(self, iface, fileName, parent=None):
super(CSVModel,self).__init__()
self.iface = iface
self.rows = []
self.fileName = fileName
self.loadCSV(fileName)
self.rowsLoaded = len(self.rows)
self.iface.mapCanvas().selectionChanged.connect(se lf.addRow)

def loadCSV(self,fileName):
self.header = []
self.dataIn = []
with open(self.fileName, "rb") as fileInput:
for idx, row in enumerate(csv.reader(fileInput)):
headerIDx = 0
if idx is headerIDx:
self.header.append(row)
elif idx>headerIDx:
items = [field for field in row]
self.dataIn.append(items)

def addRow(self):
del self.rows[:]
editLayer_Atrib = ftools_utils.getMapLayerByName(unicode(self.iface. activeLayer().name()))
totalFeatures = range(editLayer_Atrib.featureCount())
features = editLayer_Atrib.selectedFeatures()
self.beginResetModel()
for feature in features:
ID = feature.attributes()[0]
self.rows.append(self.dataIn)
self.endResetModel()

# relative axis index counts
def rowCount(self,index):
print len(self.rows)
if not self.rows:
return 0
if len(self.rows) <= self.rowsLoaded:
return len(self.rows)
else:
return self.rowsLoaded
def columnCount(self,index):
return len(self.header[0])

# source value data for cell(s) in QtableView
def data(self,index,role):
if index.isValid() and role == Qt.DisplayRole:
return self.rows[index.row()][index.column()]

# establish header data
def headerData(self,section,orientation,role):
if role != Qt.DisplayRole:
return
if orientation == Qt.Vertical:
return int(section)
if orientation == Qt.Horizontal:
return self.header[0][section]

# write changes from QtableView and proxyFilter to (self) tableData
def setData(self, index, value, role):
if index.isValid() and role == Qt.EditRole:
self.rows[index.row()][index.column()]=str(value)
self.dataChanged.emit(index, index)
return True
return False

# distinguish capabilities of total cells in tableData model
def flags(self, index):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable

# one piece of toast and jam please
def saveIt(self):
with open(self.fileName, "wb") as fileOutput:
writer = csv.writer(fileOutput)
writer.writerow(self.header[0])
for rowNumber in range(len(self.rows)):
fields = self.rows[rowNumber]
writer.writerow(fields)

Added after 17 minutes:

Alright, discovered removing the un-necessaries from rowCount() now populates the table nearly immediately. Even for enormous selection counts!


def loadCSV(self,fileName):
self.header = []
self.dataIn = []
with open(self.fileName, "rb") as fileInput:
for idx, row in enumerate(csv.reader(fileInput)):
headerIDx = 0
if idx is headerIDx:
self.header.append(row)
elif idx>headerIDx:
items = [field for field in row]
self.dataIn.append(items)

def addRow(self):
del self.rows[:]
editLayer_Atrib = ftools_utils.getMapLayerByName(unicode(self.iface. activeLayer().name()))
totalFeatures = range(editLayer_Atrib.featureCount())
features = editLayer_Atrib.selectedFeatures()
self.beginResetModel()
for feature in features:
ID = feature.attributes()[0]
self.rows.append(self.dataIn[ID])
self.endResetModel()

# relative axis index counts
def rowCount(self,index):
return len(self.rows)
def columnCount(self,index):
return len(self.header[0])


Now my concern resides in the save function. Loaded rows (with possible changes made) need to be combined back with the original data source (dataIn) prior to writing.


# one piece of toast with jam please
def saveIt(self):
with open(self.fileName, "wb") as fileOutput:
writer = csv.writer(fileOutput)
writer.writerow(self.header[0])
for rowNumber in range(len(self.rows)):
fields = self.rows[rowNumber]
writer.writerow(fields)

Any clues on doing this in a clean manner?

Added after 57 minutes:

This IS a frowned upon solution, but it works.
j[0] and I[0] are refereeing to ID values, represented by each object and row (selected) respectively.
The problem is, if multiple edits are made to different selections - this will not work.
It seems the edits, as they are made, need to be pushed to the dataIn source correct?
...Rather then conclusively right before a save is called?


def mergeEdits(self):
for idx,j in enumerate(self.rows):
if j[0] == self.dataIn[idx][0]:
self.dataIn[idx] = j
else:
pass

# one piece of toast with jam please
def saveIt(self):
self.mergeEdits()
with open(self.fileName, "wb") as fileOutput:
writer = csv.writer(fileOutput)
writer.writerow(self.header[0])
for rowNumber in range(len(self.dataIn)):
fields = self.dataIn[rowNumber]
writer.writerow(fields)



[I]UPDATE:
As it turns out, this method does allow multiple edits. As each user change is pushed to the list (self.rows), and it not fully written until the user opts to use the save function.
I suppose this current procedure will be the best option - unless there is a wiser way?

Kudos to this forum, for its spoken and unspoken help!

d_stranz
5th January 2015, 18:49
You know, I hope that when you are doing all this talking to yourself, it isn't loud enough for your coworkers to hear. You might start getting some funny looks. ;)

Thanks for sharing your solutions.