Changing Font and background color of individual item in editable Treeview
-
Hi all,
I'm quite new to Qt, and I've had a lot of fun playing with the Editable Treeview example: https://doc.qt.io/qtforpython-6/examples/example_widgets_itemviews_editabletreemodel.html
But I'm struggling to find out how to change the formatting of individual cells. For example the font-size or the background color. I found how to change the font for the entire treeview, or just the headers, but not for individual items.
As a starting minimal case, I can edit the last routine of mainwindow.py as follows to change the text of that cell to say "hello":
def update_actions(self) -> None: selection_model = self.view.selectionModel() has_selection: bool = not selection_model.selection().isEmpty() self.remove_row_action.setEnabled(has_selection) self.remove_column_action.setEnabled(has_selection) current_index = selection_model.currentIndex() has_current: bool = current_index.isValid() self.insert_row_action.setEnabled(has_current) self.insert_column_action.setEnabled(has_current) # say hello model: QAbstractItemModel = self.view.model() model.setData(current_index, "hello", Qt.EditRole) if has_current: self.view.closePersistentEditor(current_index) msg = f"Position: ({current_index.row()},{current_index.column()})" if not current_index.parent().isValid(): msg += " in top level" self.statusBar().showMessage(msg)
Would anybody be able to help me change the font and background color of that specific item instead of just setting item to "hello", please?
Thanks a lot in advance!
-
There are some item roles that influence the styling: https://doc.qt.io/qtforpython-6/PySide6/QtCore/Qt.html#PySide6.QtCore.PySide6.QtCore.Qt.ItemDataRole ( FontRole , TextAlignmentRole , BackgroundRole , ForegroundRole ) . You can return values for them from QAbstractItemModel.data().
-
There are some item roles that influence the styling: https://doc.qt.io/qtforpython-6/PySide6/QtCore/Qt.html#PySide6.QtCore.PySide6.QtCore.Qt.ItemDataRole ( FontRole , TextAlignmentRole , BackgroundRole , ForegroundRole ) . You can return values for them from QAbstractItemModel.data().
@friedemannkleint said in Changing Font and background color of individual item in editable Treeview:
QAbstractItemModel
Thanks a lot for your help (again ;) Friedmann!
OK, so I can get a print of various Roles, which are "None" for the most part, simply because they are not set in the first place I assume. However, I cannot change these, because the Tree-model class has a wrapper that replaces the setData function. This is in treemodel.py
def setData(self, index: QModelIndex, value, role: int) -> bool: if role != Qt.EditRole: return False item: TreeItem = self.get_item(index) result: bool = item.set_data(index.column(), value) if result: self.dataChanged.emit(index, index, [Qt.DisplayRole, Qt.EditRole]) return result
where clearly it only deals with the Edit role. I assume I would need to change that function directly? Would you have any advice on how to do that? Thanks !
-
Hi,
The most straightforward would be to modify your first if and expand its check, the rest is the same.
-
Hi,
The most straightforward would be to modify your first if and expand its check, the rest is the same.
Thanks @SGaist for helping out!
That doesn't seem to work. If use
model.setData(current_index, 'QWidget { font: bold italic large "Times New Roman"; font-size: 10pt }', role=Qt.FontRole)
inside the update_actions function of mainwindow.py, together with
if (role != Qt.EditRole) and (role != Qt.FontRole):
inside the setData function of treemodel.py, what happens is that it just replaces the text of the item by
'QWidget { font: bold italic large "Times New Roman"; font-size: 10pt }'
as if that was a normal edit action. I think this is because the editable tree version has a whole wrapper of the data itself around it...
-
It looks like you are trying to set a style sheet in the font role.
The FontRole takes a QFont object.
You can see here more information about the roles and what they expect.
Ah. good point. But it still does the same. If I use
newFont = QFont("Helvetica") model.setData(current_index, newFont, role=Qt.FontRole)
It just replaces the content of the item with the text "Helvetica"...
-
Ah. good point. But it still does the same. If I use
newFont = QFont("Helvetica") model.setData(current_index, newFont, role=Qt.FontRole)
It just replaces the content of the item with the text "Helvetica"...
@Stan-Pamela do you also have a custom data function ?
-
Ah. good point. But it still does the same. If I use
newFont = QFont("Helvetica") model.setData(current_index, newFont, role=Qt.FontRole)
It just replaces the content of the item with the text "Helvetica"...
@Stan-Pamela
You need to return the right data for the roles in data() not setData() -
@Stan-Pamela do you also have a custom data function ?
@SGaist yes, it's
def data(self, index: QModelIndex, role: int = None): if not index.isValid(): return None #if role == Qt.FontRole: # sorry please ignore! that was confusing... # return item.data(index.column()) if role != Qt.DisplayRole and role != Qt.EditRole: return None item: TreeItem = self.get_item(index) return item.data(index.column())
which then goes to treeitem.py -> data
def data(self, column: int): if column < 0 or column >= len(self.item_data): return None return self.item_data[column]
in other words, it's wrapping a TreeItem to get item_data[column]. What would I need to target to return the font? or the style or background?
-
@SGaist yes, it's
def data(self, index: QModelIndex, role: int = None): if not index.isValid(): return None #if role == Qt.FontRole: # sorry please ignore! that was confusing... # return item.data(index.column()) if role != Qt.DisplayRole and role != Qt.EditRole: return None item: TreeItem = self.get_item(index) return item.data(index.column())
which then goes to treeitem.py -> data
def data(self, column: int): if column < 0 or column >= len(self.item_data): return None return self.item_data[column]
in other words, it's wrapping a TreeItem to get item_data[column]. What would I need to target to return the font? or the style or background?
@Stan-Pamela you are not passing the role down to your item data call.
-
@Stan-Pamela you are not passing the role down to your item data call.
@SGaist
:)
I can only reply every 600sec apparently because I'm a new user. I think you need to give me a "reputation" to be able to interact faster? :)Anyway, really sorry about last post, that was confusing, I was trying to edit the data function and copy pasted something that wasn't working...
Still, these are the two functions that wrap around item_data
def setData(self, index: QModelIndex, value, role: int) -> bool: if (role != Qt.EditRole) and (role != Qt.FontRole): return False item: TreeItem = self.get_item(index) result: bool = item.set_data(index.column(), value) if result: self.dataChanged.emit(index, index, [Qt.DisplayRole, Qt.EditRole, Qt.FontRole]) return result
and
def set_data(self, column: int, value): if column < 0 or column >= len(self.item_data): return False self.item_data[column] = value return True
-
@SGaist
:)
I can only reply every 600sec apparently because I'm a new user. I think you need to give me a "reputation" to be able to interact faster? :)Anyway, really sorry about last post, that was confusing, I was trying to edit the data function and copy pasted something that wasn't working...
Still, these are the two functions that wrap around item_data
def setData(self, index: QModelIndex, value, role: int) -> bool: if (role != Qt.EditRole) and (role != Qt.FontRole): return False item: TreeItem = self.get_item(index) result: bool = item.set_data(index.column(), value) if result: self.dataChanged.emit(index, index, [Qt.DisplayRole, Qt.EditRole, Qt.FontRole]) return result
and
def set_data(self, column: int, value): if column < 0 or column >= len(self.item_data): return False self.item_data[column] = value return True
@Stan-Pamela that's another issue: you don't handle the role in your wrapper just the value.
-
@Stan-Pamela that's another issue: you don't handle the role in your wrapper just the value.
@SGaist OK, how do I handle the role?
Really sorry, I know this is probably so obvious to you all, but I just can't figure it out. It seems this treeview example is a corner case with all the wrappers, isn't it? Or it's just me?Still can't write more than every 600sec :( maybe just as well, ahah!
-
Ok, I see the issue. The example works well for just handling "data".
You need to modify your custom TreeItem to handle the role in addition to the value. As a basic version, you can use a dictionary where the key is the role and the item the value. Add a role parameter to the set_data and data methods so that you can properly set and retrieve these values.
-
Ok, I see the issue. The example works well for just handling "data".
You need to modify your custom TreeItem to handle the role in addition to the value. As a basic version, you can use a dictionary where the key is the role and the item the value. Add a role parameter to the set_data and data methods so that you can properly set and retrieve these values.
Thanks again. I'm still not sure how to do that...
Even for the "value" itself, could you explain how/where the final target TreeItem->item_data is set? This doesn't seem to be a standard Qt class/variable... I'm confused...At the very end of treeitem.py, in this editable tree example, there is this recursive function:
def set_data(self, column: int, value): if column < 0 or column >= len(self.item_data): return False self.item_data[column] = value return True def __repr__(self) -> str: result = f"<treeitem.TreeItem at 0x{id(self):x}" for d in self.item_data: result += f' "{d}"' if d else " <None>" result += f", {len(self.child_items)} children>" return result
Is it this repr function that's used internally, in the backend of the Qt library, to display the text manually? Do I need to use a similar backend function for the role?
-
Thanks again. I'm still not sure how to do that...
Even for the "value" itself, could you explain how/where the final target TreeItem->item_data is set? This doesn't seem to be a standard Qt class/variable... I'm confused...At the very end of treeitem.py, in this editable tree example, there is this recursive function:
def set_data(self, column: int, value): if column < 0 or column >= len(self.item_data): return False self.item_data[column] = value return True def __repr__(self) -> str: result = f"<treeitem.TreeItem at 0x{id(self):x}" for d in self.item_data: result += f' "{d}"' if d else " <None>" result += f", {len(self.child_items)} children>" return result
Is it this repr function that's used internally, in the backend of the Qt library, to display the text manually? Do I need to use a similar backend function for the role?
Basically:
def set_data(self, column: int, value, role): if column < 0 or column >= len(self.item_data): return False if column not in self.item_data.keys(): self.item_data[column] = {} self.item_data[column][role] = value return True
Don't forget to handle the EditRole and Display role properly.
-
Basically:
def set_data(self, column: int, value, role): if column < 0 or column >= len(self.item_data): return False if column not in self.item_data.keys(): self.item_data[column] = {} self.item_data[column][role] = value return True
Don't forget to handle the EditRole and Display role properly.
That doesn't work either. Or I'm not doing it properly(?)
Even ignoring the font role, and just trying to do the simplest thing possible: to use a dict like this for the text edit alone, and replace the original functionality, it doesn't work:def set_data(self, column: int, value): if column < 0 or column >= len(self.item_data): return False #self.item_data[column] = value self.item_data[column] = {Qt.EditRole: value}
-
That doesn't work either. Or I'm not doing it properly(?)
Even ignoring the font role, and just trying to do the simplest thing possible: to use a dict like this for the text edit alone, and replace the original functionality, it doesn't work:def set_data(self, column: int, value): if column < 0 or column >= len(self.item_data): return False #self.item_data[column] = value self.item_data[column] = {Qt.EditRole: value}
@Stan-Pamela did you adapt the data function in a similar fashion ?
-
@Stan-Pamela did you adapt the data function in a similar fashion ?
@SGaist, got it!
Thanks so much and sorry it took me so long to understand the trail of wrappers and how that feeds back into the main model. Awesome. I did it in a dirty way for now, but shall I clean up and post a zip of the code for consistency and closure of this forum thread?