I am currently working a small database visualization using PyQt. As the visualization should include edit functionality on some columns, I wanted to use PyQts QSqlTableModel.
Basically a new dataset is given and compared to the existing database.
The entries not yet in the database should be displayed as well as the entries, which have a corresponding entry in the database already.
The new entries with changes are flaged as 'new' and the current entries are flaged as 'old'.
The Table is setup as follows:
Show_Again | Annotation | Status(flag) | ... |
---|---|---|---|
1 or null | text or null | old, new, null | ... |
1 or null | text or null | old, new, null | ... |
Now I was asked to highlight the entries with Status='new' in red and if possible highlight where the old and new entries differ.
The regular background coloring I tried to implent by adapting a solution a found on stackoverflow.
While the background is now colored red, the programm crashes as soon as I try to enter any value in the first three columns.
Traceback (most recent call last):
File "h:\Workspace_Arbeit\Pruefungsdb_Visualizer\minimum_viable_code.py", line 400, in data
if 'new' in QSqlTableModel.data(self, self.index(index.row(), 2), QtCore.Qt.DisplayRole):
TypeError: argument of type 'NoneType' is not iterable
This the current code:
class SqlTableModel(QSqlTableModel):
ExecuteRole = QtCore.Qt.UserRole 1
def __init__(self, parent=None, db = QSqlDatabase()):
super(QSqlTableModel,self).__init__(parent, db)
def data(self, index, role):
if role == QtCore.Qt.BackgroundRole:
if 'new' in QSqlTableModel.data(self, self.index(index.row(), 2), QtCore.Qt.DisplayRole):
return QtGui.QBrush(QtCore.Qt.red)
if role==QtCore.Qt.DisplayRole:
return QSqlTableModel.data(self, index, role)
if role==QtCore.Qt.EditRole:
return super(SqlTableModel, self).data(index, role)
return super(SqlTableModel, self).data(index, role)
def setData(self, index, value, role):
return QSqlTableModel.setData(self, index, value, role)
I have also tried other solutions, that suggested to use an ItemStyleDelegate, which I unfortunately did not get to work. Although I suspect it would have created different problems as I currently use an ItemDelegate to prevent editing on any column beyond the third.
CodePudding user response:
The error is clear, you're trying to do find new
in the result of data()
.
But, as you also reported in your scheme, that column could also have null
values, which in python terms corresponds to None
, that is clearly not an iterable type.
First you need to get the value, then you check if the value is a string and it contains "new". Note that the whole data()
implementation you did is almost unnecessary, since you're just returning the default behavior. Override only what you actually need to change, and leave the rest as it is.
class SqlTableModel(QSqlTableModel):
def data(self, index, role):
if role == QtCore.Qt.BackgroundRole:
value = super().data(index.siblingAtColumn(2), Qt.DisplayRole)
if isinstance(str, value) and 'new' in value:
return QtGui.QBrush(QtCore.Qt.red)
return super().data(index, role)
Note that you could do the same even with a delegate:
class MyDelegate(QStyledItemDelegate):
def initStyleOption(self, opt, index):
super().initStyleOption(opt, index)
state = index.siblingAtColumn(2).data()
if isinstance(state, str) and 'new' in state:
opt.backgroundBrush = QtGui.QBrush(QtCore.Qt.red)
This has absolutely no relation with the fact that you want to prevent editing, which you could do independently even at model level:
class SqlTableModel(QSqlTableModel):
# ...
def flags(self, index):
flags = super().flags(index)
if index.column() >= 3:
flags &= ~QtCore.Qt.ItemIsEditable
return flags