class TextAnnotation(Annotation): """Text annotation item for the canvas scheme. """ editingFinished = Signal() """Emitted when the editing is finished (i.e. the item loses focus).""" textEdited = Signal() """Emitted when the edited text changes.""" def __init__(self, parent=None, **kwargs): Annotation.__init__(self, parent, **kwargs) self.setFlag(QGraphicsItem.ItemIsMovable) self.setFlag(QGraphicsItem.ItemIsSelectable) self.setFocusPolicy(Qt.ClickFocus) self.__textMargins = (2, 2, 2, 2) rect = self.geometry().translated(-self.pos()) self.__framePathItem = QGraphicsPathItem(self) self.__framePathItem.setPen(QPen(Qt.NoPen)) self.__textItem = GraphicsTextEdit(self) self.__textItem.setPlaceholderText(self.tr("Enter text here")) self.__textItem.setPos(2, 2) self.__textItem.setTextWidth(rect.width() - 4) self.__textItem.setTabChangesFocus(True) self.__textItem.setTextInteractionFlags(Qt.NoTextInteraction) self.__textItem.setFont(self.font()) self.__textInteractionFlags = Qt.NoTextInteraction layout = self.__textItem.document().documentLayout() layout.documentSizeChanged.connect(self.__onDocumentSizeChanged) self.__updateFrame() def adjustSize(self): """Resize to a reasonable size. """ self.__textItem.setTextWidth(-1) self.__textItem.adjustSize() size = self.__textItem.boundingRect().size() left, top, right, bottom = self.textMargins() geom = QRectF(self.pos(), size + QSizeF(left + right, top + bottom)) self.setGeometry(geom) def setFramePen(self, pen): """Set the frame pen. By default Qt.NoPen is used (i.e. the frame is not shown). """ self.__framePathItem.setPen(pen) def framePen(self): """Return the frame pen. """ return self.__framePathItem.pen() def setFrameBrush(self, brush): """Set the frame brush. """ self.__framePathItem.setBrush(brush) def frameBrush(self): """Return the frame brush. """ return self.__framePathItem.brush() def setPlainText(self, text): """Set the annotation plain text. """ self.__textItem.setPlainText(text) def toPlainText(self): return self.__textItem.toPlainText() def setHtml(self, text): """Set the annotation rich text. """ self.__textItem.setHtml(text) def toHtml(self): return self.__textItem.toHtml() def setDefaultTextColor(self, color): """Set the default text color. """ self.__textItem.setDefaultTextColor(color) def defaultTextColor(self): return self.__textItem.defaultTextColor() def setTextMargins(self, left, top, right, bottom): """Set the text margins. """ margins = (left, top, right, bottom) if self.__textMargins != margins: self.__textMargins = margins self.__textItem.setPos(left, top) self.__textItem.setTextWidth( max(self.geometry().width() - left - right, 0) ) def textMargins(self): """Return the text margins. """ return self.__textMargins def document(self): """Return the QTextDocument instance used internally. """ return self.__textItem.document() def setTextCursor(self, cursor): self.__textItem.setTextCursor(cursor) def textCursor(self): return self.__textItem.textCursor() def setTextInteractionFlags(self, flags): self.__textInteractionFlags = flags def textInteractionFlags(self): return self.__textInteractionFlags def setDefaultStyleSheet(self, stylesheet): self.document().setDefaultStyleSheet(stylesheet) def mouseDoubleClickEvent(self, event): Annotation.mouseDoubleClickEvent(self, event) if event.buttons() == Qt.LeftButton and \ self.__textInteractionFlags & Qt.TextEditable: self.startEdit() def startEdit(self): """Start the annotation text edit process. """ self.__textItem.setTextInteractionFlags(self.__textInteractionFlags) self.__textItem.setFocus(Qt.MouseFocusReason) # Install event filter to find out when the text item loses focus. self.__textItem.installSceneEventFilter(self) self.__textItem.document().contentsChanged.connect( self.textEdited ) def endEdit(self): """End the annotation edit. """ self.__textItem.setTextInteractionFlags(Qt.NoTextInteraction) self.__textItem.removeSceneEventFilter(self) self.__textItem.document().contentsChanged.disconnect( self.textEdited ) self.editingFinished.emit() def __onDocumentSizeChanged(self, size): # The size of the text document has changed. Expand the text # control rect's height if the text no longer fits inside. try: rect = self.geometry() _, top, _, bottom = self.textMargins() if rect.height() < (size.height() + bottom + top): rect.setHeight(size.height() + bottom + top) self.setGeometry(rect) except Exception: log.error("error in __onDocumentSizeChanged", exc_info=True) def __updateFrame(self): rect = self.geometry() rect.moveTo(0, 0) path = QPainterPath() path.addRect(rect) self.__framePathItem.setPath(path) def resizeEvent(self, event): width = event.newSize().width() left, _, right, _ = self.textMargins() self.__textItem.setTextWidth(max(width - left - right, 0)) self.__updateFrame() QGraphicsWidget.resizeEvent(self, event) def sceneEventFilter(self, obj, event): if obj is self.__textItem and event.type() == QEvent.FocusOut: self.__textItem.focusOutEvent(event) self.endEdit() return True return Annotation.sceneEventFilter(self, obj, event) def itemChange(self, change, value): if change == QGraphicsItem.ItemSelectedHasChanged: if self.isSelected(): self.setFramePen(QPen(Qt.DashDotLine)) else: self.setFramePen(QPen(Qt.NoPen)) return Annotation.itemChange(self, change, value) def changeEvent(self, event): if event.type() == QEvent.FontChange: self.__textItem.setFont(self.font()) Annotation.changeEvent(self, event)
class TextAnnotation(Annotation): """Text annotation item for the canvas scheme. """ editingFinished = Signal() """Emitted when the editing is finished (i.e. the item loses focus).""" textEdited = Signal() """Emitted when the edited text changes.""" def __init__(self, parent=None, **kwargs): Annotation.__init__(self, parent, **kwargs) self.setFlag(QGraphicsItem.ItemIsMovable) self.setFlag(QGraphicsItem.ItemIsSelectable) self.setFocusPolicy(Qt.ClickFocus) self.__textMargins = (2, 2, 2, 2) rect = self.geometry().translated(-self.pos()) self.__framePathItem = QGraphicsPathItem(self) self.__framePathItem.setPen(QPen(Qt.NoPen)) self.__textItem = GraphicsTextEdit(self) self.__textItem.setPlaceholderText(self.tr("Enter text here")) self.__textItem.setPos(2, 2) self.__textItem.setTextWidth(rect.width() - 4) self.__textItem.setTabChangesFocus(True) self.__textItem.setTextInteractionFlags(Qt.NoTextInteraction) self.__textItem.setFont(self.font()) self.__textInteractionFlags = Qt.NoTextInteraction layout = self.__textItem.document().documentLayout() layout.documentSizeChanged.connect(self.__onDocumentSizeChanged) self.__updateFrame() def adjustSize(self): """Resize to a reasonable size. """ self.__textItem.setTextWidth(-1) self.__textItem.adjustSize() size = self.__textItem.boundingRect().size() left, top, right, bottom = self.textMargins() geom = QRectF(self.pos(), size + QSizeF(left + right, top + bottom)) self.setGeometry(geom) def setFramePen(self, pen): """Set the frame pen. By default Qt.NoPen is used (i.e. the frame is not shown). """ self.__framePathItem.setPen(pen) def framePen(self): """Return the frame pen. """ return self.__framePathItem.pen() def setFrameBrush(self, brush): """Set the frame brush. """ self.__framePathItem.setBrush(brush) def frameBrush(self): """Return the frame brush. """ return self.__framePathItem.brush() def setPlainText(self, text): """Set the annotation plain text. """ self.__textItem.setPlainText(text) def toPlainText(self): return self.__textItem.toPlainText() def setHtml(self, text): """Set the annotation rich text. """ self.__textItem.setHtml(text) def toHtml(self): return self.__textItem.toHtml() def setDefaultTextColor(self, color): """Set the default text color. """ self.__textItem.setDefaultTextColor(color) def defaultTextColor(self): return self.__textItem.defaultTextColor() def setTextMargins(self, left, top, right, bottom): """Set the text margins. """ margins = (left, top, right, bottom) if self.__textMargins != margins: self.__textMargins = margins self.__textItem.setPos(left, top) self.__textItem.setTextWidth( max(self.geometry().width() - left - right, 0)) def textMargins(self): """Return the text margins. """ return self.__textMargins def document(self): """Return the QTextDocument instance used internally. """ return self.__textItem.document() def setTextCursor(self, cursor): self.__textItem.setTextCursor(cursor) def textCursor(self): return self.__textItem.textCursor() def setTextInteractionFlags(self, flags): self.__textInteractionFlags = flags def textInteractionFlags(self): return self.__textInteractionFlags def setDefaultStyleSheet(self, stylesheet): self.document().setDefaultStyleSheet(stylesheet) def mouseDoubleClickEvent(self, event): Annotation.mouseDoubleClickEvent(self, event) if event.buttons() == Qt.LeftButton and \ self.__textInteractionFlags & Qt.TextEditable: self.startEdit() def startEdit(self): """Start the annotation text edit process. """ self.__textItem.setTextInteractionFlags(self.__textInteractionFlags) self.__textItem.setFocus(Qt.MouseFocusReason) # Install event filter to find out when the text item loses focus. self.__textItem.installSceneEventFilter(self) self.__textItem.document().contentsChanged.connect(self.textEdited) def endEdit(self): """End the annotation edit. """ self.__textItem.setTextInteractionFlags(Qt.NoTextInteraction) self.__textItem.removeSceneEventFilter(self) self.__textItem.document().contentsChanged.disconnect(self.textEdited) self.editingFinished.emit() def __onDocumentSizeChanged(self, size): # The size of the text document has changed. Expand the text # control rect's height if the text no longer fits inside. try: rect = self.geometry() _, top, _, bottom = self.textMargins() if rect.height() < (size.height() + bottom + top): rect.setHeight(size.height() + bottom + top) self.setGeometry(rect) except Exception: log.error("error in __onDocumentSizeChanged", exc_info=True) def __updateFrame(self): rect = self.geometry() rect.moveTo(0, 0) path = QPainterPath() path.addRect(rect) self.__framePathItem.setPath(path) def resizeEvent(self, event): width = event.newSize().width() left, _, right, _ = self.textMargins() self.__textItem.setTextWidth(max(width - left - right, 0)) self.__updateFrame() QGraphicsWidget.resizeEvent(self, event) def sceneEventFilter(self, obj, event): if obj is self.__textItem and event.type() == QEvent.FocusOut: self.__textItem.focusOutEvent(event) self.endEdit() return True return Annotation.sceneEventFilter(self, obj, event) def itemChange(self, change, value): if change == QGraphicsItem.ItemSelectedHasChanged: if self.isSelected(): self.setFramePen(QPen(Qt.DashDotLine)) else: self.setFramePen(QPen(Qt.NoPen)) return Annotation.itemChange(self, change, value) def changeEvent(self, event): if event.type() == QEvent.FontChange: self.__textItem.setFont(self.font()) Annotation.changeEvent(self, event)