超过定义的QRECT时,QgraphicStextItem的文本
我有一个 QGraphicsTextItem ,它是绘制一个盒子的 QGraphicsPathItem 的子项。我希望 QGraphicsTextItem 只显示适合该框的文本,如果它溢出,我希望该文本被忽略。
我已经能够使其正常工作,但使用硬编码值,这并不理想。 这是我的基本代码:
class Node(QtWidgets.QGraphicsPathItem):
def __init__(self, scene, parent=None):
super(Node, self).__init__(parent)
scene.addItem(self)
# Variables
self.main_background_colour = QtGui.QColor("#575b5e")
self.dialogue_background_colour = QtGui.QColor("#2B2B2B")
self.dialogue_text_colour = QtGui.QColor("white")
self.brush = QtGui.QBrush(self.main_background_colour)
self.pen = QtGui.QPen(self.dialogue_text_colour, 2)
self.dialogue_font = QtGui.QFont("Calibri", 12)
self.dialogue_font.setBold(True)
self.dialogue_font_metrics = QtGui.QFontMetrics(self.dialogue_font)
self.dialogue_text = "To find out how fast you type, just start typing in the blank textbox on the right of the test prompt. You will see your progress, including errors on the left side as you type. You can fix errors as you go, or correct them at the end with the help of the spell checker. If you need to restart the test, delete the text in the text box. Interactive feedback shows you your current wpm and accuracy. Bring me all the biscuits, for I am hungry. They will be a fine meal for me and all the mice in town!"
# Rects
self.main_rect = QtCore.QRectF(0, -40, 600, 240)
self.dialogue_rect = QtCore.QRectF(self.main_rect.x() + (self.main_rect.width() * 0.05), self.main_rect.top() + 10,
(self.main_rect.width() * 0.9), self.main_rect.height() - 20)
self.dialogue_text_point = QtCore.QPointF(self.dialogue_rect.x() + (self.dialogue_rect.width() * 0.05), self.dialogue_rect.y() + 10)
# Painter Paths
self.main_path = QtGui.QPainterPath()
self.main_path.addRoundedRect(self.main_rect, 4, 4)
self.setPath(self.main_path)
self.dialogue_path = QtGui.QPainterPath()
self.dialogue_path.addRect(self.dialogue_rect)
self.dialogue_text_item = QtWidgets.QGraphicsTextItem(self.dialogue_text, self)
self.dialogue_text_item.setCacheMode(QtWidgets.QGraphicsPathItem.DeviceCoordinateCache)
self.dialogue_text_item.setTextWidth(self.dialogue_rect.width() - 40)
self.dialogue_text_item.setFont(self.dialogue_font)
self.dialogue_text_item.setDefaultTextColor(self.dialogue_text_colour)
self.dialogue_text_item.setPos(self.dialogue_text_point)
# HARDCODED ELIDE
elided = self.dialogue_font_metrics.elidedText(self.dialogue_text, QtCore.Qt.ElideRight, 3300)
self.dialogue_text_item.setPlainText(self.dialogue_text) # elided
# Flags
self.setFlag(self.ItemIsMovable, True)
self.setFlag(self.ItemSendsGeometryChanges, True)
self.setFlag(self.ItemIsSelectable, True)
self.setFlag(self.ItemIsFocusable, True)
self.setCacheMode(QtWidgets.QGraphicsPathItem.DeviceCoordinateCache)
def boundingRect(self):
return self.main_rect
def paint(self, painter, option, widget=None):
# Background
self.brush.setColor(self.main_background_colour)
painter.setBrush(self.brush)
painter.drawPath(self.path())
# Dialogue
self.brush.setColor(self.dialogue_background_colour)
painter.setBrush(self.brush)
self.pen.setColor(self.dialogue_background_colour.darker())
painter.setPen(self.pen)
painter.drawPath(self.dialogue_path)
这是我尝试使用的,但我的数学不行。我认为我以错误的方式处理这个问题:
# Dialogue
text_length = self.dialogue_font_metrics.horizontalAdvance(self.dialogue_text)
text_metric_rect = self.dialogue_font_metrics.boundingRect(QtCore.QRect(0, 0, self.dialogue_text_item.textWidth(), self.dialogue_font_metrics.capHeight()), QtCore.Qt.TextWordWrap, self.dialogue_text)
elided_length = (text_length / text_metric_rect.height()) * (self.dialogue_rect.height() - 20)
elided = self.dialogue_font_metrics.elidedText(self.dialogue_text, QtCore.Qt.ElideRight, 3300)
self.dialogue_text_item.setPlainText(elided)
任何建议将不胜感激!
I have a QGraphicsTextItem
that is a child of a QGraphicsPathItem
which draws a box. I want the QGraphicsTextItem
to only display text that fits within the box, if it overflows I want that text to be elided.
I've been able to get this working, but with hardcoded values, which isn't ideal.
Here is my basic code:
class Node(QtWidgets.QGraphicsPathItem):
def __init__(self, scene, parent=None):
super(Node, self).__init__(parent)
scene.addItem(self)
# Variables
self.main_background_colour = QtGui.QColor("#575b5e")
self.dialogue_background_colour = QtGui.QColor("#2B2B2B")
self.dialogue_text_colour = QtGui.QColor("white")
self.brush = QtGui.QBrush(self.main_background_colour)
self.pen = QtGui.QPen(self.dialogue_text_colour, 2)
self.dialogue_font = QtGui.QFont("Calibri", 12)
self.dialogue_font.setBold(True)
self.dialogue_font_metrics = QtGui.QFontMetrics(self.dialogue_font)
self.dialogue_text = "To find out how fast you type, just start typing in the blank textbox on the right of the test prompt. You will see your progress, including errors on the left side as you type. You can fix errors as you go, or correct them at the end with the help of the spell checker. If you need to restart the test, delete the text in the text box. Interactive feedback shows you your current wpm and accuracy. Bring me all the biscuits, for I am hungry. They will be a fine meal for me and all the mice in town!"
# Rects
self.main_rect = QtCore.QRectF(0, -40, 600, 240)
self.dialogue_rect = QtCore.QRectF(self.main_rect.x() + (self.main_rect.width() * 0.05), self.main_rect.top() + 10,
(self.main_rect.width() * 0.9), self.main_rect.height() - 20)
self.dialogue_text_point = QtCore.QPointF(self.dialogue_rect.x() + (self.dialogue_rect.width() * 0.05), self.dialogue_rect.y() + 10)
# Painter Paths
self.main_path = QtGui.QPainterPath()
self.main_path.addRoundedRect(self.main_rect, 4, 4)
self.setPath(self.main_path)
self.dialogue_path = QtGui.QPainterPath()
self.dialogue_path.addRect(self.dialogue_rect)
self.dialogue_text_item = QtWidgets.QGraphicsTextItem(self.dialogue_text, self)
self.dialogue_text_item.setCacheMode(QtWidgets.QGraphicsPathItem.DeviceCoordinateCache)
self.dialogue_text_item.setTextWidth(self.dialogue_rect.width() - 40)
self.dialogue_text_item.setFont(self.dialogue_font)
self.dialogue_text_item.setDefaultTextColor(self.dialogue_text_colour)
self.dialogue_text_item.setPos(self.dialogue_text_point)
# HARDCODED ELIDE
elided = self.dialogue_font_metrics.elidedText(self.dialogue_text, QtCore.Qt.ElideRight, 3300)
self.dialogue_text_item.setPlainText(self.dialogue_text) # elided
# Flags
self.setFlag(self.ItemIsMovable, True)
self.setFlag(self.ItemSendsGeometryChanges, True)
self.setFlag(self.ItemIsSelectable, True)
self.setFlag(self.ItemIsFocusable, True)
self.setCacheMode(QtWidgets.QGraphicsPathItem.DeviceCoordinateCache)
def boundingRect(self):
return self.main_rect
def paint(self, painter, option, widget=None):
# Background
self.brush.setColor(self.main_background_colour)
painter.setBrush(self.brush)
painter.drawPath(self.path())
# Dialogue
self.brush.setColor(self.dialogue_background_colour)
painter.setBrush(self.brush)
self.pen.setColor(self.dialogue_background_colour.darker())
painter.setPen(self.pen)
painter.drawPath(self.dialogue_path)
This is what I've tried to use, but my maths is off. I think I'm approaching this in the wrong way:
# Dialogue
text_length = self.dialogue_font_metrics.horizontalAdvance(self.dialogue_text)
text_metric_rect = self.dialogue_font_metrics.boundingRect(QtCore.QRect(0, 0, self.dialogue_text_item.textWidth(), self.dialogue_font_metrics.capHeight()), QtCore.Qt.TextWordWrap, self.dialogue_text)
elided_length = (text_length / text_metric_rect.height()) * (self.dialogue_rect.height() - 20)
elided = self.dialogue_font_metrics.elidedText(self.dialogue_text, QtCore.Qt.ElideRight, 3300)
self.dialogue_text_item.setPlainText(elided)
Any suggestions would be appreciated!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
qfontmetrics Elide函数仅适用于单一的文本,不能用于 buarded 文本,这是涉及单词包装或新行时发生的事情。
即使试图根据任意大小为ELIDE函数设置宽度,它也无效:每当包装一条线时,用作该行的参考的宽度是“重置”。
想象一下,您希望文本宽50像素,所以您假设 将以两行分开,总共有100个像素。然后,您在该文本中有三个单词,每个40像素宽,
elidedeText()的结果
,带有100个像素的结果,将是所有三个单词,最后一个单词都含有。<<<<<<<<<<<<<<< br>然后,您将启用单词包装的文本设置为50像素的最大宽度:结果是您只会看到前两个单词,因为每行只能适合一个单词。
唯一可行的解决方案是使用 qtextlayout ,并通过所有文本行迭代它会创建,然后,如果 Next 行的高度超过最大高度,则仅针对该行调用
ElidedText()
。但是请注意,这假设格式(字体,字体尺寸和重量)始终在整个文本中相同。更高级的布局是可能的,但是它需要更高级使用qtextDocument功能,qtextlayout和qtextformat。
请注意,您的项目实现有点值得怀疑:首先,您实际上是不是使用QGraphicsPathitem的任何功能,因为您覆盖了两个
paint()
>和boundingRect()
。如果您想做类似的事情,只需使用基本的QGraphicSitem,否则始终尝试使用QT提供的现有类和函数,这对于图形视图框架尤其重要,这依赖于C ++优化:覆盖> paint
paint ()
迫使图形通过python,这是巨大瓶颈,尤其是在涉及许多项目的情况下。与其绘画所有内容,不如创建具有正确设置属性的子项目。
最后,项目不应将自己添加到场景中。
这是一个更好,更简单(更可读性的)实现,它考虑了以上所有内容:
The QFontMetrics elide function only works for a single line of text, and cannot be used for layed out text, which is what happens when word wrapping or new lines are involved.
Even trying to set the width for the elide function based on an arbitrary size, it wouldn't be valid: whenever a line is wrapped, the width used as reference for that line is "reset".
Imagine that you want the text to be 50 pixels wide, so you suppose that some text would be split in two lines, with a total of 100 pixels. Then you have three words in that text, each 40 pixels wide, for which the result of
elidedText()
with 100 pixels will be that you'll have all three words, with the last one elided.Then you set that text with word wrapping enabled and a maximum width of 50 pixels: the result will be that you'll only see the first two words, because each line can only fit one word.
The only viable solution is to use QTextLayout, and iterate through all the text lines it creates, then, if the height of the next line exceeds the maximum height, you call
elidedText()
for that line only.Be aware, though, that this assumes that the format (font, font size and weight) will always be the same along the whole text. More advanced layouts are possible, but it requires more advanced use of QTextDocument features, QTextLayout and QTextFormat.
Note that your item implementation is a bit questionable: first of all, you're practically not using any of the features of QGraphicsPathItem, since you're overriding both
paint()
andboundingRect()
.If you want to do something like that, just use a basic QGraphicsItem, otherwise always try to use the existing classes and functions Qt provides, which is particularly important for the Graphics View framework, which relies on the C++ optimizations: overriding
paint()
forces the drawing to pass through python, which is a huge bottleneck, especially when many items are involved.Instead of painting everything, create child items with properly set properties.
Finally, an item should not add itself to a scene.
Here's a better, simpler (and more readable) implementation that considers all the above: