iT邦幫忙

0

PYQT 如何在兩個Button連接一條動態移動的線

這是我的code。
在View上有兩個可移動按鈕
我可以通過拖動鼠標在View上畫線。
但我想要做的是在兩個按鈕之間畫線。

希望功能 : 如果我右鍵btn1(Test1)然後右鍵btn2(Test2),將在兩個按鈕之間建一個按鈕,然後會跟著按鈕移動而移動。
我在這個問題卡了一個多月。
懇請大家幫忙!

from PyQt5 import QtGui, QtCore
from PyQt5.QtWidgets import *

class Window(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.view = View(self)
        self.button = QPushButton('Clear View', self)
        self.button.clicked.connect(self.handleClearView)
        layout = QVBoxLayout(self)
        layout.addWidget(self.view)
        layout.addWidget(self.button)


    def handleClearView(self):
        self.view.scene().clear()

class DragButton(QPushButton):

    def mousePressEvent(self, event):
        self.__mousePressPos = None
        self.__mouseMovePos = None
        if event.button() == QtCore.Qt.LeftButton:
            self.__mousePressPos = event.globalPos()
            self.__mouseMovePos = event.globalPos()


    def mouseMoveEvent(self, event):
        if event.buttons() == QtCore.Qt.LeftButton:

            currPos = self.mapToGlobal(self.pos())
            globalPos = event.globalPos()
            diff = globalPos - self.__mouseMovePos
            newPos = self.mapFromGlobal(currPos + diff)
            self.move(newPos)
            self.__mouseMovePos = globalPos


    def mouseReleaseEvent(self, event):
        if self.__mousePressPos is not None:
            moved = event.globalPos() - self.__mousePressPos 
            if moved.manhattanLength() > 3:
                event.ignore()
                return



class View(QGraphicsView):
    def __init__(self, parent):
        QGraphicsView.__init__(self, parent)
        self.setScene(QGraphicsScene(self))
        self.setSceneRect(QtCore.QRectF(self.viewport().rect()))
        btn1=DragButton('Test1', self)
        btn2=DragButton('Test2', self)

    def mousePressEvent(self, event):
        self._start = event.pos()

    def mouseReleaseEvent(self, event):
        start = QtCore.QPointF(self.mapToScene(self._start))
        end = QtCore.QPointF(self.mapToScene(event.pos()))
        self.scene().addItem(
            QGraphicsLineItem(QtCore.QLineF(start, end)))
        for point in (start, end):
            text = self.scene().addSimpleText(
                '(%d, %d)' % (point.x(), point.y()))
            text.setBrush(QtCore.Qt.red)
            text.setPos(point)

if __name__ == '__main__':

    import sys
    app = QApplication(sys.argv)
    window = Window()
    window.resize(640, 480)
    window.show()
    sys.exit(app.exec_())
froce iT邦大師 2 級 ‧ 2019-04-29 22:57:28 檢舉
奇怪,是我中文退步了嗎?怎麼完全看不懂你要啥?

1 個回答

0
froce
iT邦大師 2 級 ‧ 2019-04-30 07:21:33

嘗試在改動最小的狀態下,改到我看的懂你要的部分...
因為我實在看不懂你要啥。

from PyQt5 import QtGui, QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag


class Window(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.view = View(self)
        self.button = QPushButton('Clear View', self)
        self.button.clicked.connect(self.handleClearView)
        layout = QVBoxLayout(self)
        layout.addWidget(self.view)
        layout.addWidget(self.button)


    def handleClearView(self):
        self.view.scene().clear()


class DragButton(QPushButton):
    def __init__(self, title, parent=None):
        super().__init__(title, parent)

    def mouseMoveEvent(self, e):

        if e.buttons() != Qt.LeftButton:
            return

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


class View(QGraphicsView):
    def __init__(self, parent):
        QGraphicsView.__init__(self, parent)
        self.setScene(QGraphicsScene(self))
        self.setAcceptDrops(True)
        self.setSceneRect(QtCore.QRectF(self.viewport().rect()))
        self.btn1=DragButton('Test1', self)
        self.btn2=DragButton('Test2', self)

    def clearScene(self):
        self.scene().clear()

    def dragEnterEvent(self, e):
        if e.source() in [self.btn1, self.btn2]:
            self.clearScene()
        e.accept()

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

    def dropEvent(self, e):
        btn = e.source()
        otherBtn = self.btn2 if btn == self.btn1 else self.btn1
        position = e.pos()
        btn.move(position)

        start = QtCore.QPointF(self.mapToScene(btn.pos()))
        end = QtCore.QPointF(self.mapToScene(otherBtn.pos()))

        self.scene().addItem(
            QGraphicsLineItem(QtCore.QLineF(start, end)))

        e.setDropAction(Qt.MoveAction)
        e.accept()


if __name__ == '__main__':

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

然後你原本的code:

class DragButton(QPushButton):

    def mousePressEvent(self, event):
        self.__mousePressPos = None
        self.__mouseMovePos = None
        if event.button() == QtCore.Qt.LeftButton:
            self.__mousePressPos = event.globalPos()
            self.__mouseMovePos = event.globalPos()


    def mouseMoveEvent(self, event):
        if event.buttons() == QtCore.Qt.LeftButton:

            currPos = self.mapToGlobal(self.pos())
            globalPos = event.globalPos()
            diff = globalPos - self.__mouseMovePos
            newPos = self.mapFromGlobal(currPos + diff)
            self.move(newPos)
            self.__mouseMovePos = globalPos


    def mouseReleaseEvent(self, event):
        if self.__mousePressPos is not None:
            moved = event.globalPos() - self.__mousePressPos 
            if moved.manhattanLength() > 3:
                event.ignore()
                return

這個會允許你的btn移動超出view。

class View(QGraphicsView):
    def __init__(self, parent):
        QGraphicsView.__init__(self, parent)
        self.setScene(QGraphicsScene(self))
        self.setSceneRect(QtCore.QRectF(self.viewport().rect()))
        btn1=DragButton('Test1', self)
        btn2=DragButton('Test2', self)

    def mousePressEvent(self, event):
        self._start = event.pos()

    def mouseReleaseEvent(self, event):
        start = QtCore.QPointF(self.mapToScene(self._start))
        end = QtCore.QPointF(self.mapToScene(event.pos()))
        self.scene().addItem(
            QGraphicsLineItem(QtCore.QLineF(start, end)))
        for point in (start, end):
            text = self.scene().addSimpleText(
                '(%d, %d)' % (point.x(), point.y()))
            text.setBrush(QtCore.Qt.red)
            text.setPos(point)

重寫View的mousePressEvent和mouseReleaseEvent幹嘛?
你點到DragButton上,事件不會傳遞到View上,和HTML的冒泡機制不同,所以你拖曳DragButton並不會觸發View上的mousePressEvent,放開也不會觸發mouseReleaseEvent。

簡單的說就是不會依你所想的,按鈕移動由DragButton控制,畫線由View上的滑鼠座標控制。
實際上也不應該這樣,因為你滑鼠點擊時不一定會點到按鈕的同一位置,所以端點位置會不同。
應該由按鈕上做為標準的某點決定你線的端點。

froce iT邦大師 2 級 ‧ 2019-04-30 07:23:27 檢舉

最後:

from PyQt5.QtWidgets import *

這是非常不好的習慣,用多少拿多少,等到之後如果要打包你就知道了。

謝謝您提點了這麼多

不好意思我寫的不夠清楚,我最終希望的就是 : 當我各按兩個按鈕一下之後,會有一條線把他們兩個連在一起,然後線可以跟著按鈕一同移動
(因為這是一個我重作的簡易版),我本來做的是可以動態增加按鈕,不會只有兩個

我要發表回答

立即登入回答