[英]how to make an overriden QGraphicsTextItem editable & movable?
我正在使用 PyQt 並且我正在嘗試重新實現QGraphicsTextItem ,但似乎我錯過了一些東西。
我想讓NodeTag項目的文本可編輯。 我曾嘗試設置諸如Qt.TextEditorInteraction和QGraphicsItem.ItemIsMovable 之類的標志,但這些似乎被忽略了......
這是一個最小的可重現示例:
import sys
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView, QMainWindow, QApplication, QGraphicsItem, QGraphicsTextItem
from PyQt5.QtCore import *
from PyQt5.QtGui import QPen
class NodeTag(QGraphicsTextItem):
def __init__(self,text):
QGraphicsTextItem.__init__(self,text)
self.text = text
self.setPos(0,0)
self.setTextInteractionFlags(Qt.TextEditorInteraction)
# self.setFlag(QGraphicsItem.ItemIsFocusable, True) # All these flags are ignored...
# self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
def boundingRect(self):
return QRectF(0,0,80,25)
def paint(self,painter,option,widget):
painter.setPen(QPen(Qt.blue, 2, Qt.SolidLine))
painter.drawRect(self.boundingRect())
painter.drawText(self.boundingRect(),self.text)
def mousePressEvent(self, event):
print("CLICK!")
# self.setTextInteractionFlags(Qt.TextEditorInteraction) # make text editable on click
# self.setFocus()
class GView(QGraphicsView):
def __init__(self, parent, *args, **kwargs):
super().__init__(*args, **kwargs)
self.parent = parent
self.setGeometry(100, 100, 700, 450)
self.show()
class Scene(QGraphicsScene):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
tagItem = NodeTag("myText") # create a NodeTag item
self.addItem(tagItem)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__() # create default constructor for QWidget
self.setGeometry(900, 70, 1000, 800)
self.createGraphicView()
self.show()
def createGraphicView(self):
self.scene = Scene(self)
gView = GView(self)
scene = Scene(gView)
gView.setScene(scene)
# Set the main window's central widget
self.setCentralWidget(gView)
# Run program
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
正如你所看到的,我已經嘗試覆蓋 mousePressEvent 並在那里設置標志,但到目前為止沒有運氣。 任何幫助表示贊賞!
嘗試一下:
...
class NodeTag(QGraphicsTextItem):
def __init__(self, text, parent=None):
super(NodeTag, self).__init__(parent)
self.text = text
self.setPlainText(text)
self.setFlag(QGraphicsItem.ItemIsMovable)
self.setFlag(QGraphicsItem.ItemIsSelectable)
def focusOutEvent(self, event):
self.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
super(NodeTag, self).focusOutEvent(event)
def mouseDoubleClickEvent(self, event):
if self.textInteractionFlags() == QtCore.Qt.NoTextInteraction:
self.setTextInteractionFlags(QtCore.Qt.TextEditorInteraction)
super(NodeTag, self).mouseDoubleClickEvent(event)
def paint(self,painter,option,widget):
painter.setPen(QPen(Qt.blue, 2, Qt.SolidLine))
painter.drawRect(self.boundingRect())
# painter.drawText(self.boundingRect(),self.text)
super().paint(painter, option, widget)
...
所有 QGraphicsItem 子類都有一個paint
方法,並且所有繪制某些內容的項目都覆蓋了該方法,以便它們可以實際繪制自己。
該機制是一樣的標准QWidgets,對其中有一個paintEvent
(不同的是, paint
的QGraphicsItem的接收已經實例化了QPainter),因此,如果您想進一步做畫比類已經提供,基本實現必須等叫做。
考慮到繪畫總是從下到上發生,所以所有需要在基礎繪畫后面繪制的東西都必須在調用super().paint()
之前完成,並且所有將要繪制在默認繪畫前面的東西都有之后放置。
根據情況,覆蓋可能需要調用默認的基本實現,這對於boundingRect
也很重要。 QGraphicsTextItem 在內容改變時會自動調整大小,所以你不應該總是返回一個固定的 QRect。 如果需要最小尺寸,解決方案是將最小矩形與默認boundingRect()
函數提供的矩形合並。
然后,在 QGraphicsTextItem 上的編輯發生在項目獲得焦點時,但由於您還希望能夠移動項目,事情變得更加棘手,因為這兩個操作都基於鼠標點擊。 如果您希望能夠通過單擊來編輯文本,解決方案是僅在釋放鼠標按鈕並且沒有移動一定數量的像素(startDragDistance()
屬性)時才使項目可編輯,否則項目隨鼠標移動。 這顯然使ItemIsMovable
標志無用,因為我們將在內部處理移動。
最后,由於提供了最小尺寸,我們還需要覆蓋shape()
方法以確保正確映射碰撞和點擊,並返回包含整個邊界矩形的 QPainterPath(對於應該是默認值的普通 QGraphicsItem行為,但 QGraphicsRectItem 不會發生這種情況)。
這是上述內容的完整實現:
class NodeTag(QGraphicsTextItem):
def __init__(self, text):
QGraphicsTextItem.__init__(self, text)
self.startPos = None
self.isMoving = False
# the following is useless, not only because we are leaving the text
# painting to the base implementation, but also because the text is
# already accessible using toPlainText() or toHtml()
#self.text = text
# this is unnecessary too as all new items always have a (0, 0) position
#self.setPos(0, 0)
def boundingRect(self):
return super().boundingRect() | QRectF(0, 0, 80, 25)
def paint(self, painter, option, widget):
# draw the border *before* (as in "behind") the text
painter.setPen(QPen(Qt.blue, 2, Qt.SolidLine))
painter.drawRect(self.boundingRect())
super().paint(painter, option, widget)
def shape(self):
shape = QPainterPath()
shape.addRect(self.boundingRect())
return shape
def focusOutEvent(self, event):
# this is required in order to allow movement using the mouse
self.setTextInteractionFlags(Qt.NoTextInteraction)
def mousePressEvent(self, event):
if (event.button() == Qt.LeftButton and
self.textInteractionFlags() != Qt.TextEditorInteraction):
self.startPos = event.pos()
else:
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.startPos:
delta = event.pos() - self.startPos
if (self.isMoving or
delta.manhattanLength() >= QApplication.startDragDistance()):
self.setPos(self.pos() + delta)
self.isMoving = True
return
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if (not self.isMoving and
self.textInteractionFlags() != Qt.TextEditorInteraction):
self.setTextInteractionFlags(Qt.TextEditorInteraction)
self.setFocus()
# the following lines are used to correctly place the text
# cursor at the mouse cursor position
cursorPos = self.document().documentLayout().hitTest(
event.pos(), Qt.FuzzyHit)
textCursor = self.textCursor()
textCursor.setPosition(cursorPos)
self.setTextCursor(textCursor)
super().mouseReleaseEvent(event)
self.startPos = None
self.isMoving = False
作為旁注,請記住 QGraphicsTextItem 支持富文本格式,因此即使您想對文本繪制過程進行更多控制,也不應使用QPainter.drawText()
,因為您只會繪制純文本。 實際上, QGraphicsTextItem 使用底層文本文檔的drawContents()
函數繪制其內容。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.