绘制任意线条以连接两个项目并移动它们 PyQt5

draw any lines to connect two items and move them PyQt5

提问人:Djodjo Badibanga 提问时间:11/13/2023 更新时间:11/13/2023 访问量:19

问:

根据此页面中的代码,我冒昧地根据自己的需要对其进行了调整,但现在我被卡住了。我添加了从按钮创建正方形的功能,还更改了点的位置,使它们不仅在一侧,而且在上方和下方。

现在我的问题是我无法在其中一个点上封线。当我创建线并在两个项目上连接两个点时,一旦我移动其中一个项目甚至两个项目,连接就会发生变化。如果该线连接到下面的点,则它将更改为连接到上面的点。无论移动如何,我都想密封链接。

谢谢你的帮助。

`import sys
from PyQt5 import QtWidgets, QtCore, QtGui

class CustomItem(QtWidgets.QGraphicsItem):

    def __init__(self, pointONLeft=False, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.ellipseOnLeft = pointONLeft
        self.point = None
   
    self.endPoint = None
        self.isStart = None
        self.line = None
        self.setAcceptHoverEvents(True)
        self.setFlag(self.ItemIsMovable)
        self.setFlag(self.ItemSendsGeometryChanges)
        self.lines = [] 

    def addLine(self, line, isStart):
        self.lines.append((line, isStart)) 

    def itemChange(self, change, value):
        if change == self.ItemPositionChange and self.scene():
            self.moveLineToCenter(value)
        return super(CustomItem, self).itemChange(change, value)

    def moveLineToCenter(self, newPos):
        for line, isStart in self.lines:
            xOffset = 50  
            yOffsetStart = -5  
            yOffsetEnd = 105  
            yOffset = yOffsetStart if isStart else yOffsetEnd
            newCenterPos = QtCore.QPointF(newPos.x() + xOffset, newPos.y() + yOffset)
            p1 = newCenterPos if isStart else line.line().p1()
            p2 = line.line().p2() if isStart else newCenterPos
            line.setLine(QtCore.QLineF(p1, p2))

    def containsPoint(self, pos):
        topEllipse = self.mapToScene(QtCore.QRectF(45, -5, 10, 10).adjusted(-0.5, -0.5, 0.5, 0.5))
        bottomEllipse = self.mapToScene(QtCore.QRectF(45, 95, 10, 10).adjusted(-0.5, -0.5, 0.5, 0.5))

        if topEllipse.containsPoint(pos, QtCore.Qt.OddEvenFill):
            return "top"
        elif bottomEllipse.containsPoint(pos, QtCore.Qt.OddEvenFill):
            return "bottom"
        else:
            return None

    def boundingRect(self):
        return QtCore.QRectF(-5, 0, 110, 110)

    def paint(self, painter, option, widget):
        pen = QtGui.QPen(QtCore.Qt.red)
        pen.setWidth(2)
        painter.setPen(pen)

        painter.setBrush(QtGui.QBrush(QtGui.QColor(31, 176, 224)))
        painter.drawRoundedRect(QtCore.QRectF(0, 0, 100, 100), 4, 4)  
        painter.setBrush(QtGui.QBrush(QtGui.QColor(214, 13, 36)))
        painter.drawEllipse(QtCore.QRectF(45, -5, 10, 10))
        painter.drawEllipse(QtCore.QRectF(45, 95, 10, 10))

    def containsPoint(self, pos):
        topEllipse = self.mapToScene(QtCore.QRectF(45, -5, 10, 10).adjusted(-0.5, -0.5, 0.5, 0.5))
        bottomEllipse = self.mapToScene(QtCore.QRectF(45, 95, 10, 10).adjusted(-0.5, -0.5, 0.5, 0.5))
        return topEllipse.containsPoint(pos, QtCore.Qt.OddEvenFill) or bottomEllipse.containsPoint(pos, QtCore.Qt.OddEvenFill)


class Scene(QtWidgets.QGraphicsScene):
    def __init__(self, *args, **kwargs):
        super(Scene, self).__init__(*args, **kwargs)
        self.startPoint = None
        self.endPoint = None
        self.line = None
        self.graphics_line = None
        self.item1 = None
        self.item2 = None
        self.isStart = None  

    def mousePressEvent(self, event):
        self.line = None
        self.graphics_line = None
        self.item1 = None
        self.item2 = None
        self.startPoint = None
        self.endPoint = None
        if self.itemAt(event.scenePos(), QtGui.QTransform()) and isinstance(self.itemAt(event.scenePos(), QtGui.QTransform()), CustomItem):
            self.item1 = self.itemAt(event.scenePos(), QtGui.QTransform())
            self.checkPoint1(event.scenePos())
            if self.startPoint:
                self.line = QtCore.QLineF(self.startPoint, self.endPoint)
                self.graphics_line = self.addLine(self.line)
                self.update_path()
        super(Scene, self).mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if event.buttons() & QtCore.Qt.LeftButton and self.startPoint:
            self.endPoint = event.scenePos()
            self.update_path()
        super(Scene, self).mouseMoveEvent(event)

    def filterCollidingItems(self, items):  # filters out all the colliding items and returns only instances of CustomItem
        return [x for x in items if isinstance(x, CustomItem) and x != self.item1]

    def mouseReleaseEvent(self, event):
        if self.graphics_line:
            self.checkPoint2(event.scenePos())
            self.update_path()
            if self.item2:           
                self.item1.addLine(self.graphics_line, True)  
                self.item2.addLine(self.graphics_line, False)  
            else:
                self.removeItem(self.graphics_line)
                self.graphics_line = None
        super(Scene, self).mouseReleaseEvent(event)

    def checkPoint1(self, pos):
        if self.item1.containsPoint(pos):
            self.item1.setFlag(self.item1.ItemIsMovable, False)
            self.startPoint = self.endPoint = pos
        else:
            self.item1.setFlag(self.item1.ItemIsMovable, True)

    def checkPoint2(self, pos):
        item_lst = self.filterCollidingItems(self.graphics_line.collidingItems())
        contains = False
        if not item_lst:  # checks if there are any items in the list
            return
        for self.item2 in item_lst:
            if self.item2.containsPoint(pos):
                contains = True
                self.endPoint = pos
                break
        if not contains:
            self.item2 = None

    def update_path(self):
        if self.startPoint and self.endPoint:
            self.line.setP2(self.endPoint)
            self.graphics_line.setLine(self.line)
            if self.item1:
                self.item1.moveLineToCenter(self.item1.pos())

def main():
    app = QtWidgets.QApplication(sys.argv)
    scene = Scene()
    mainWindow = QtWidgets.QMainWindow()
    mainWindow.setWindowTitle("Créateur de carrés")
    mainWindow.resize(800, 600)  
    view = QtWidgets.QGraphicsView(scene)
    view.setViewportUpdateMode(view.FullViewportUpdate)
    view.setMouseTracking(True)
    mainWindow.setCentralWidget(view)

    
    menuBar = mainWindow.menuBar()
    fileMenu = menuBar.addMenu("&File")
    createAction = QtWidgets.QAction("&Créer Carré", mainWindow)
    createAction.triggered.connect(lambda: createSquare(scene))
    fileMenu.addAction(createAction)
    mainWindow.show()    
    sys.exit(app.exec_())

def createSquare(scene):
    item = CustomItem()
    scene.addItem(item)
    item.setPos(QtCore.QPointF(50, 50))  

if __name__ == '__main__':
    main()`


pyqt5 qgraphicsitem

评论

0赞 musicamante 11/13/2023
如果实现两个连接点,则不能只考虑两个“主要”项,还需要检查从每个连接点的哪个连接点创建线路。您链接的帖子包含一个已经解决了该方面的解决方案,您为什么不尝试从该代码开始尝试呢?

答: 暂无答案