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!