如何使覆盖的QGraphicsTextItem可编辑和移动?

2024-05-23 21:16:16 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用PyQt,并试图重新实现一个QGraphicsTextItem,但似乎我遗漏了一些东西

我想使节点tag项的文本可编辑。我已尝试设置标志,例如Qt.TextEditorInteractionQGraphicsItem.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并在那里设置标志,但到目前为止运气不佳。 感谢您的帮助


Tags: textfromimportselfinitdefsysqt
2条回答

试试看:

...

        
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)
 ...

enter image description here

所有QGraphicsItem子类都有一个paint方法,所有绘制某些内容的项都覆盖了该方法,以便它们实际上可以绘制自己

该机制与标准QWidgets相同,对于标准QWidgets,有一个paintEvent(区别在于QGraphicsItem的paint接收一个已经实例化的QPaint),因此,如果您想在类已经提供的基础上进一步绘制其他,则必须调用基本实现

认为绘画总是自下而上,所以需要画出的东西后面的EEM >底漆必须在之前调用eEM>调用^ {CD4>},并且缺省绘画的前面中的所有东西都要放在。p>

根据具体情况,重写可能需要调用默认的基本实现,这对于boundingRect来说也很重要。QGraphicsTextItem在其内容更改时自动调整自身大小,因此不应总是返回固定的QRect。如果需要最小大小,解决方案是将最小矩形与默认boundingRect()函数提供的合并

然后,在QGraphicsTextItem上进行编辑时,项目会被聚焦到,但由于您也希望能够移动项目,所以这两个操作都是基于鼠标单击的,因此操作会变得更加复杂。如果您希望能够通过单击编辑文本,解决方案是当鼠标按钮被释放并且没有移动一定数量的像素时(即^{}属性),使项目仅可编辑,否则项目将随鼠标移动。这显然使ItemIsMovable标志无用,因为我们将在内部处理该运动

最后,由于提供了最小大小,我们还需要重写^{}方法,以确保正确映射碰撞和单击,并返回包含整个边界rect的QPainterPath(对于正常的QGraphicsSitem,这应该是默认行为,但QGraphicsRecItem不会发生)

下面是上述内容的完整实现:

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支持富文本格式,因此即使您希望对文本绘制过程进行更多控制,也不应使用^{,因为您只需绘制纯文本。事实上,QGraphicsTextItem使用底层text document^{}函数绘制其内容

相关问题 更多 >