Change color of part of the text in a QListWidgetItem
-
Hello,
I have the following scenario:
I receive text data from an application, which uses ASCII control characters to change the color of the text (intended for terminals). I want to display this text in different widgets, while also keeping the coloring.
I have a working setup for this using a QTextEdit widget, where I can simply set the font color whenever I encounter an ASCII control character before adding my actual text, which works as expected:tokens = gdb_output.split(b"\x1b[") ### ...loop through tokens... start = token[:3] # https://stackoverflow.com/a/33206814 if start == b"30m": self.setTextColor(Qt.GlobalColor.black) # Remove the control character from the text and add it to the QTextEdit self.insertPlainText(token.strip(start).decode())
Now I want to replicate the same using a QListWidget, since the text data I receive here are split into lines which I want to be able to interact with.
Is there a way to do this with the QListWidgetItems? The advantage of the QTextEdit was that I can set the text color, and then all the text I insert afterwards will have these changes.
The only option I see currently is using HTML, however it seems to me extremely difficult to translate the control characters into HTML.
Alternatively, is there a better widget for my use case? I would want to be able to interact with each line of text individually (e.g. be able to right click on it) while also being able to change the text color like in a QTextEdit.The answer to this thread seems to be unavailable unfortunately.
Thank you for your time.
-
Hi,
The original suggestion for a QStyledItemDelegate is the correct one.
See this Stack Overflow answer that provides an example of html rendering delegate. It uses QTextDocument which is also used by QTextEdit.
@SGaist
Seems to work well enough, although I share the sentiment in the SO post that this seems a bit hacky for such an important functionality.
There are many "solutions" provided in the post, I ended up using this answer, which worked forPySide6
after adapting some imports.Thank you both for your time :)
Adapted solution (in case linked post gets lost)
from PySide6.QtCore import QRectF, QSize from PySide6.QtGui import QTextDocument, QAbstractTextDocumentLayout from PySide6.QtWidgets import QStyledItemDelegate, QStyleOptionViewItem, QStyle # https://stackoverflow.com/a/66091713 class HTMLDelegate(QStyledItemDelegate): def __init__(self): super().__init__() # probably better not to create new QTextDocuments every ms self.doc = QTextDocument() def paint(self, painter, option, index): options = QStyleOptionViewItem(option) self.initStyleOption(options, index) painter.save() self.doc.setTextWidth(options.rect.width()) self.doc.setHtml(options.text) self.doc.setDefaultFont(options.font) options.text = '' options.widget.style().drawControl(QStyle.ControlElement.CE_ItemViewItem, options, painter) painter.translate(options.rect.left(), options.rect.top()) clip = QRectF(0, 0, options.rect.width(), options.rect.height()) painter.setClipRect(clip) ctx = QAbstractTextDocumentLayout.PaintContext() ctx.clip = clip self.doc.documentLayout().draw(painter, ctx) painter.restore() def sizeHint(self, option, index): options = QStyleOptionViewItem(option) self.initStyleOption(option, index) self.doc.setHtml(option.text) self.doc.setTextWidth(option.rect.width()) return QSize(self.doc.idealWidth(), self.doc.size().height())
-
Hello,
I have the following scenario:
I receive text data from an application, which uses ASCII control characters to change the color of the text (intended for terminals). I want to display this text in different widgets, while also keeping the coloring.
I have a working setup for this using a QTextEdit widget, where I can simply set the font color whenever I encounter an ASCII control character before adding my actual text, which works as expected:tokens = gdb_output.split(b"\x1b[") ### ...loop through tokens... start = token[:3] # https://stackoverflow.com/a/33206814 if start == b"30m": self.setTextColor(Qt.GlobalColor.black) # Remove the control character from the text and add it to the QTextEdit self.insertPlainText(token.strip(start).decode())
Now I want to replicate the same using a QListWidget, since the text data I receive here are split into lines which I want to be able to interact with.
Is there a way to do this with the QListWidgetItems? The advantage of the QTextEdit was that I can set the text color, and then all the text I insert afterwards will have these changes.
The only option I see currently is using HTML, however it seems to me extremely difficult to translate the control characters into HTML.
Alternatively, is there a better widget for my use case? I would want to be able to interact with each line of text individually (e.g. be able to right click on it) while also being able to change the text color like in a QTextEdit.The answer to this thread seems to be unavailable unfortunately.
Thank you for your time.
@Kekz
You will need aQStyledItemDelegate
with whatever functionality. Yes it does not have the interface ofQTextEdit
. (You could put aQTextEdit
in as a list item, but I think you want to deal with each line separately.) I would follow the approach of the SO post you linked to with HTML. I don't see that it is "extremely difficult to translate the control characters into HTML". -
@Kekz
You will need aQStyledItemDelegate
with whatever functionality. Yes it does not have the interface ofQTextEdit
. (You could put aQTextEdit
in as a list item, but I think you want to deal with each line separately.) I would follow the approach of the SO post you linked to with HTML. I don't see that it is "extremely difficult to translate the control characters into HTML".@JonB said in Change color of part of the text in a QListWidgetItem:
You could put a
QTextEdit
in as a list item, but I think you want to deal with each line separately.).But making a
QTextEdit
for each line should work (one line would correspond to one list item)?
And inside that item I could use theQTextEdit
functionalities like in my example.
Thanks for the idea, I will try that.@JonB said in Change color of part of the text in a QListWidgetItem:
I don't see that it is "extremely difficult to translate the control characters into HTML"
E.g. the following text:
"These are a couple of different words as an example"
Making words 1-5 red, 6-10 blue and words 3-7 underlined, is simple with ASCII control characters but more complicated when translating to HTML, as the tags would be nested and I can only close the last opened tag, i.e. when trying to close the red tag at word 6, it would instead close the last opened "underline" tag from word 3. I would always have to close all tags, and only reopen the ones I didn't want to close. -
@JonB said in Change color of part of the text in a QListWidgetItem:
You could put a
QTextEdit
in as a list item, but I think you want to deal with each line separately.).But making a
QTextEdit
for each line should work (one line would correspond to one list item)?
And inside that item I could use theQTextEdit
functionalities like in my example.
Thanks for the idea, I will try that.@JonB said in Change color of part of the text in a QListWidgetItem:
I don't see that it is "extremely difficult to translate the control characters into HTML"
E.g. the following text:
"These are a couple of different words as an example"
Making words 1-5 red, 6-10 blue and words 3-7 underlined, is simple with ASCII control characters but more complicated when translating to HTML, as the tags would be nested and I can only close the last opened tag, i.e. when trying to close the red tag at word 6, it would instead close the last opened "underline" tag from word 3. I would always have to close all tags, and only reopen the ones I didn't want to close.@Kekz said in Change color of part of the text in a QListWidgetItem:
Making words 1-5 red, 6-10 blue and words 3-7 underlined, is simple with ASCII control characters but more complicated with HTML, as the tags would be nested and I can only close the last opened tag, i.e. when trying to close the red tag at word 6, it would instead close the last opened "underline" tag from word 3. I would always have to close all tags, and only reopen the ones I didn't want to close.
Sorry, I just don't see what your issue is here. You do whatever opening/closing is necessary. Yes you would need to close and re-open the underlining at the color change boundary. Anyway it is what it is.
Out of interest, with your original
QTextEdit
code try callingtoHtml()
to see what it has generated. It has managed to produce that HTML from the instructions you gave it. -
@Kekz
You will need aQStyledItemDelegate
with whatever functionality. Yes it does not have the interface ofQTextEdit
. (You could put aQTextEdit
in as a list item, but I think you want to deal with each line separately.) I would follow the approach of the SO post you linked to with HTML. I don't see that it is "extremely difficult to translate the control characters into HTML".@JonB said in Change color of part of the text in a QListWidgetItem:
You could put a QTextEdit in as a list item
How would that actually be possible?
The way I see it, I can only addQListWidgetItem
s to theQListWidget
, where the items hold simple text.
How do I add a widget as a list item?@JonB said in Change color of part of the text in a QListWidgetItem:
Out of interest, with your original QTextEdit code try calling toHtml() to see what it has generated. It has managed to produce that HTML from the instructions you gave it.
That is true, unfortunately I don't have such a working example right now. But it gives the idea to use
QTextEdit
as my parser ^^ -
@JonB said in Change color of part of the text in a QListWidgetItem:
You could put a QTextEdit in as a list item
How would that actually be possible?
The way I see it, I can only addQListWidgetItem
s to theQListWidget
, where the items hold simple text.
How do I add a widget as a list item?@JonB said in Change color of part of the text in a QListWidgetItem:
Out of interest, with your original QTextEdit code try calling toHtml() to see what it has generated. It has managed to produce that HTML from the instructions you gave it.
That is true, unfortunately I don't have such a working example right now. But it gives the idea to use
QTextEdit
as my parser ^^@Kekz
I really just meant "QTextEdit
in a list of widgets", rather than necessarily aQListWidget
. For example, aQListView
at a pinch you can usesetIndexWidget()
to place an actual widget (should work fromQListWidget
too, but just use aQListView
). You're not supposed to have too many widgets in a list, but you could try it here maybe. -
@Kekz
I really just meant "QTextEdit
in a list of widgets", rather than necessarily aQListWidget
. For example, aQListView
at a pinch you can usesetIndexWidget()
to place an actual widget (should work fromQListWidget
too, but just use aQListView
). You're not supposed to have too many widgets in a list, but you could try it here maybe.Hi,
The original suggestion for a QStyledItemDelegate is the correct one.
See this Stack Overflow answer that provides an example of html rendering delegate. It uses QTextDocument which is also used by QTextEdit.
-
Hi,
The original suggestion for a QStyledItemDelegate is the correct one.
See this Stack Overflow answer that provides an example of html rendering delegate. It uses QTextDocument which is also used by QTextEdit.
@SGaist
Seems to work well enough, although I share the sentiment in the SO post that this seems a bit hacky for such an important functionality.
There are many "solutions" provided in the post, I ended up using this answer, which worked forPySide6
after adapting some imports.Thank you both for your time :)
Adapted solution (in case linked post gets lost)
from PySide6.QtCore import QRectF, QSize from PySide6.QtGui import QTextDocument, QAbstractTextDocumentLayout from PySide6.QtWidgets import QStyledItemDelegate, QStyleOptionViewItem, QStyle # https://stackoverflow.com/a/66091713 class HTMLDelegate(QStyledItemDelegate): def __init__(self): super().__init__() # probably better not to create new QTextDocuments every ms self.doc = QTextDocument() def paint(self, painter, option, index): options = QStyleOptionViewItem(option) self.initStyleOption(options, index) painter.save() self.doc.setTextWidth(options.rect.width()) self.doc.setHtml(options.text) self.doc.setDefaultFont(options.font) options.text = '' options.widget.style().drawControl(QStyle.ControlElement.CE_ItemViewItem, options, painter) painter.translate(options.rect.left(), options.rect.top()) clip = QRectF(0, 0, options.rect.width(), options.rect.height()) painter.setClipRect(clip) ctx = QAbstractTextDocumentLayout.PaintContext() ctx.clip = clip self.doc.documentLayout().draw(painter, ctx) painter.restore() def sizeHint(self, option, index): options = QStyleOptionViewItem(option) self.initStyleOption(option, index) self.doc.setHtml(option.text) self.doc.setTextWidth(option.rect.width()) return QSize(self.doc.idealWidth(), self.doc.size().height())
-
-
-