Solved Issues with setting checkboxes in QSqlTableModel
-
I have subclassed QSqlTableModel so I can add checkboxes to a QTableView. I've done this by overriding the flags(), data() and setData() methods. When the tableview loads the data from SQL it seems to work fine and does set the checkboxes accordingly. But when I click on the checkboxes nothing happens to either the data in the database, or the checkbox in the view.
Here's what I'm doing currently in my setData method:
def setData(self, index, value, role=Qt.EditRole): if role == Qt.CheckStateRole\ and self.headerData(index.column(), Qt.Horizontal) in \ ("Game", "Console", "Accessory", "Box", "Manual"): data = "Yes" if value == Qt.Checked else "No" return QSqlTableModel.setData(self, index, data, Qt.EditRole) else: return QSqlTableModel.setData(self, index, value, role)
Basically I'm checking if the role is CheckStateRole and if the column is in the column I have the checkboxes in. Then checking the value and passing the correct data to the database based on it.
It does enter the if statement, and checking the value of data seems like it does set it correctly. So the part that doesn't work is the call to QSqlTableModel.setData() in the return statement. It doesn't seem to do anything.
Editing columns that don't have checkboxes works fine.
Am I doing something obviously wrong here?
-
I'm not a python expert but
QSqlTableModel.setData(self, index, data, Qt.EditRole)
looks wrong. To call the base class implementation you should call something likesuper(QSqlTableModel,self).setData(self, index, data, Qt.EditRole)
-
@VRonin I'm pretty new to python myself, as well as QT, so I'm admittedly a bit unsure as to the difference between what I was doing and using super like that. Thanks for pointing it out. I should look into it more.
I changed the whole subclass to use super() but it still has the same behavior unfortunately. :(
Edit:
Actually now I can't edit any other column either. I guess that makes it consistent at least. But it doesn't make sense. Reading up on super() the way I was doing it is simply how you do it without using the super function, and the way it was done before super was introduced in Python. The behavior should be identical if I'm reading it correctly. But obviously something changed. Maybe someone can chime in on that.Edit²: Ok never mind the previous edit. Changing super(QSqlTableModel, self) into just super() everywhere (for example super().setData(index, data, Qt.EditRole) made the behavior exactly as when I used QSqlTableModel to call the base implementation. That is, I can edit the columns that don't have checkboxes but the checkboxes are still not co-operating.
-
So I took a look at the QSqlTableModel C++ source code, and I noticed that there's an if statement in there that checks if the index has the flag ItemIsEditable set. If it isn't, then it returns false.
Now in the example I was adapting from (Which was this one that is subclassing a QSortFilterProxyModel rather than QSqlTableModel, and was using boolean values and doing other things as well) did NOT set that flag in the overridden flags() method! I assume that's done somewhere else, but I only adapted what I needed. I didn't even know it was a flag you could set since I've never really done this before. Should've looked more carefully at the example I guess...
Anyway, after setting that flag in the flags method it now works as expected. Here's what the method is looking like now if anyone is interested:
def flags(self, index): if self.headerData(index.column(), Qt.Horizontal) in ("Game", "Console", "Accessory", "Box", "Manual"): return Qt.ItemIsUserCheckable | Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable else: return super().flags(index)
You can also set the return statement to simply:
return super().flags(index) | Qt.ItemIsUserCheckable
And it'll work just the same. That's actually probably a better/cleaner way of doing it now that I think about it.