python - PyQt : How to create a lineItem between two button and can move with button?

标签 python pyqt pyqt5

“如何在两个按钮之间创建一个lineItem并且可以随按钮移动”

我的程序可以使用名为“添加按钮”的按钮创建新按钮

我想创建一个 lineItem 当我单击创建的两个按钮的名为“连接”的菜单操作时。

现在,我可以在它们之间建立一条线了。 但我仍然希望它们在按钮移动时移动。

我看到一些例子是删除它并构建新行.. 但我只想移动线的位置?

该线路可以执行该操作吗? 以下是我的代码

import sys, os
from PyQt5 import QtCore, QtGui, QtWidgets



can_draw=0
start=0
end=0
first_connect=0
second_connect =0


class DragButton(QtWidgets.QPushButton):
    def __init__(self, title, parent=None):
        super().__init__(title, parent)
        self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showMenu)

    def showMenu(self):
        menu = QtWidgets.QMenu()
        menu.addAction("connect", self.connectLine)
        menu.exec_(self.cursor().pos())



    def mouseMoveEvent(self, e):
        if e.buttons() != QtCore.Qt.LeftButton:
            return

        mimeData = QtCore.QMimeData()
        drag = QtGui.QDrag(self)
        drag.setMimeData(mimeData)
        drag.setHotSpot(e.pos() - self.rect().topLeft())
        dropAction = drag.exec_(QtCore.Qt.MoveAction)


    def connectLine(self):

        global can_draw
        global start
        global end
        global first_connect
        global second_connect 

        view = self.parent()

        can_draw +=1

        if can_draw == 1:


            start = QtCore.QPointF(view.mapToScene(self.pos()))


        if can_draw == 2:

            end = QtCore.QPointF(view.mapToScene(self.pos()))


            can_draw -= 2


            view.createLineItem(start,end)

class GraphicsLineItem(QtWidgets.QGraphicsLineItem):



    def contextMenuEvent(self, event):
        menu = QtWidgets.QMenu()
        menu.addAction("Delete", self.remove)
        menu.exec_(self.cursor().pos())
        print(self.a)

    def remove(self):
        self.scene().removeItem(self)

    def shape(self):
        p = super(GraphicsLineItem, self).shape()
        stroker = QtGui.QPainterPathStroker()
        stroker.setWidth(20)
        return stroker.createStroke(p)


class View(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super(View, self).__init__(parent)
        self.setScene(QtWidgets.QGraphicsScene(self))
        self.setAcceptDrops(True)
        self.setSceneRect(QtCore.QRectF(self.viewport().rect()))

        self.btn1 = QtWidgets.QPushButton("Start")
        self.btn1.setGeometry(230, 80, 100, 30)
        self.btn1.setCheckable(True)
        self.btn1.clicked.connect(self.add_Text)


        self.line = None

    def _createLineF(self,start,end):

        return QtCore.QLineF(start, end)

    def createLineItem(self,start,end):

        self.line = GraphicsLineItem(self._createLineF(start,end))
        self.scene().addItem(self.line)


    def clearScene(self):
        self.scene().clear()
        self.line = None

    def add_Text(self):

        self.button = DragButton('Text', self)
        self.button.setGeometry(230, 80, 100, 30)

        self.button.show() 



    def dragEnterEvent(self, e):
        e.accept()

    def dragMoveEvent(self, e):
        e.accept()

    def dropEvent(self, e):
        btn = e.source()
        position = e.pos()
        btn.move(position)
        if self.line:
            self.line.setLine(self._createLineF())
        e.setDropAction(QtCore.Qt.MoveAction)
        e.accept()


class Window(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.view = View()
        self.button = QtWidgets.QPushButton(
            "Clear View", clicked=self.view.scene().clear
        )
        self.btn1 = QtWidgets.QPushButton("add button")
        self.btn1.setCheckable(True)
        self.btn1.clicked.connect(self.view.add_Text)
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.view)
        layout.addWidget(self.btn1)


if __name__ == "__main__":

    import sys

    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.resize(640, 480)
    window.show()
    sys.exit(app.exec_())

最佳答案

您应该更多地使用信号/槽系统:按钮应该通知 View 它想要连接并在它移动时发出通知。当按钮移动时,该线应该自行处理更新。

这意味着线连接到按钮,按钮连接到 View 。

一个简单的例子:

当您想要连接按钮时,按钮将发送信号connectionRequested,当按钮移动时,将发送信号moved

class DragButton(QtWidgets.QPushButton):
    connectionRequested = pyqtSignal(QtWidgets.QPushButton)
    moved = pyqtSignal()
    def __init__(self, title, parent=None):
        super().__init__(title, parent)
        self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showMenu)

    def showMenu(self):
        menu = QtWidgets.QMenu()
        menu.addAction("connect", lambda: self.connectionRequested.emit(self))
        menu.exec_(self.cursor().pos())

    def mouseMoveEvent(self, e):
        if e.buttons() != QtCore.Qt.LeftButton:
            return
        mimeData = QtCore.QMimeData()
        drag = QtGui.QDrag(self)
        drag.setMimeData(mimeData)
        drag.setHotSpot(e.pos() - self.rect().topLeft())
        dropAction = drag.exec_(QtCore.Qt.MoveAction)

        self.moved.emit()

该线有两个参数:sourcedestination,用于在它们的位置之间绘制线(您可以定义除pos之外的其他方法)返回相对位置,例如按钮的中心)。

class GraphicsLineItem(QtWidgets.QGraphicsLineItem):
    def __init__(self, source, destination, parent=None):
        super().__init__(parent)
        self.source = source
        self.destination = destination

        self.move()

        self.source.moved.connect(self.move)
        self.destination.moved.connect(self.move)

    def contextMenuEvent(self, event):
        menu = QtWidgets.QMenu()
        menu.addAction("Delete", self.remove)
        menu.exec_(self.cursor().pos())
        print(self.a)

    def remove(self):
        self.scene().removeItem(self)

    def shape(self):
        p = super(GraphicsLineItem, self).shape()
        stroker = QtGui.QPainterPathStroker()
        stroker.setWidth(20)
        return stroker.createStroke(p)

    def move(self):
        self.setLine(QLineF(self.source.pos(), self.destination.pos()))

当您单击第二个按钮的“连接”操作时, View 将创建一个新行。

class View(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super(View, self).__init__(parent)
        self.setScene(QtWidgets.QGraphicsScene(self))
        self.setAcceptDrops(True)
        self.setSceneRect(QtCore.QRectF(self.viewport().rect()))

        self.btn1 = QtWidgets.QPushButton("Start")
        self.btn1.setGeometry(230, 80, 100, 30)
        self.btn1.setCheckable(True)
        self.btn1.clicked.connect(self.add_Text)
        self.source = None


    def clearScene(self):
        self.scene().clear()
        self.source = None

    def add_Text(self):
        button = DragButton('Text', self)
        button.setGeometry(230, 80, 100, 30)
        button.show() 
        button.connectionRequested.connect(self.connectButton)

    def connectButton(self, button):
        # Do not connect a button with itself
        if not self.source or button == self.source:
            self.source = button
            return

        line = GraphicsLineItem(self.source, button)
        self.scene().addItem(line)
        self.source = None

    def dragEnterEvent(self, e):
        e.accept()

    def dragMoveEvent(self, e):
        e.accept()

    def dropEvent(self, e):
        btn = e.source()
        position = e.pos()
        btn.move(position)
        e.setDropAction(QtCore.Qt.MoveAction)
        e.accept()

关于python - PyQt : How to create a lineItem between two button and can move with button?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56206236/

相关文章:

python - 创建一个 3 x 4 矩阵并添加列

Python PyQt5 QTreeView 设置行背景颜色

python - Pandas : join dataframes and merge values of identical columns

python - 如何使用 twisted 手动发送数据

python - Django 错误 --SyntaxError : invalid syntax

python - PYQT如何编辑在MainWindow中tab化的QDockWidget的标题?

python - 当我添加按钮时,KeyPressEvent() 不适用于标签

python - 如何将 PyQt 信号连接到外部函数

当我点击按钮两次时,Python 网格布局小部件变得很奇怪

python - 从一个 QListView 拖放到另一个 QListView