在PyQt中拖动/移动一个QPushButton。

时间:2022-06-10 23:13:31

I am really struggling to figure out a way to do this. Say I implement a button very simply in a widget window:

我真的很难想出办法来做这件事。假设我在小部件窗口中非常简单地实现了一个按钮:

self.button = QPushButton("Drag Me", self)

I can move its initialization point around the parent widget's area using self.button.move(x,y), and I can get mouse events from mousePressEvent(self, e) via e.x() and e.y(), so that the button moves to wherever I click, but I just cannot seem to put all this together into a drag and drop framework.

我可以移动它的初始化点父部件的区域使用self.button.move(x,y),我可以得到mousePressEvent鼠标事件(自我,e)通过e.x()和e.y(),所以无论我点击该按钮移动,但是我似乎就是不能把这一切放在一起变成一个拖拽框架。

Clarification: After reading on the 'true' meaning of Drag/Drop, that's not what I need. I just want to be able to move a widget around with my mouse, much similar to the way you move magnets on a fridge.

澄清:在阅读了“真正的”拖放的含义后,这不是我所需要的。我只是想用我的鼠标移动一个小部件,就像你在冰箱上移动磁铁一样。

2 个解决方案

#1


11  

Here is an example of a moveable button that still supports the normal click signal properly:

下面是一个移动按钮的例子,它仍然支持正常的点击信号:

from PyQt4 import QtCore, QtGui


class DragButton(QtGui.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()

        super(DragButton, self).mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if event.buttons() == QtCore.Qt.LeftButton:
            # adjust offset from clicked point to origin of widget
            currPos = self.mapToGlobal(self.pos())
            globalPos = event.globalPos()
            diff = globalPos - self.__mouseMovePos
            newPos = self.mapFromGlobal(currPos + diff)
            self.move(newPos)

            self.__mouseMovePos = globalPos

        super(DragButton, self).mouseMoveEvent(event)

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

        super(DragButton, self).mouseReleaseEvent(event)

def clicked():
    print "click as normal!"

if __name__ == "__main__":
    app = QtGui.QApplication([])
    w = QtGui.QWidget()
    w.resize(800,600)

    button = DragButton("Drag", w)
    button.clicked.connect(clicked)

    w.show()
    app.exec_()

In the mousePressEvent I record both the initial start position, and a position that will get updated throughout the drag.

在mousePressEvent中,我记录了初始起始位置和在整个拖动过程中会得到更新的位置。

In the mouseMoveEvent, I get the proper offset of the widget from where it was clicked to where the actual origin is, so that the move is accurate.

在mouseMoveEvent中,我得到了一个小部件的适当偏移量,从它被点击的地方到实际的原点,这样移动是准确的。

In the mouseReleaseEvent, I check to see if the overall move was greater than at least a tiny amount. If it was, then it was a drag and we ignore the normal event to not produce a "clicked" signal. Otherwise, we allow the normal event handler to produce the click.

在mouseReleaseEvent中,我查看总体移动是否大于至少一个小数目。如果是,那么它就是一个拖拽,我们忽略了正常的事件,不产生一个“点击”的信号。否则,我们允许正常的事件处理程序生成click。

#2


2  

It depends on what you are trying to do. If you are trying to do actual "Drag & Drop", you're going about it wrong. What you are doing is just moving the button around in its X,Y coordinate space within its parent. Its never actually invoking any Drag/Drop events, those are entirely different.

这取决于你想做什么。如果你想做实际的“拖放”,那你就错了。你所做的就是在它的X,Y坐标空间中移动这个按钮。它从来没有实际调用任何拖放事件,它们是完全不同的。

You should read through the drag & drop documentation here:

您应该阅读下面的拖放文档:

http://doc.qt.nokia.com/4.7-snapshot/dnd.html
http://qt-project.org/doc/qt-5.0/qdrag.html

http://doc.qt.nokia.com/4.7-snapshot/dnd.html http://doc.qt.nokia.com/4.7-snapshot/dnd.html project.org/doc/qt - 5.0 - / - qdrag.html

Instead of moving the button within the mousePressEvent, you'll need to create a new QDrag object and execute it. You can make it look like your button by taking a snapshot of your button using the QPixmap::grabWidget method and assign it to the QDrag instance using the QDrag::setPixmap method.

无需在mousePressEvent中移动按钮,您需要创建一个新的QDrag对象并执行它。通过使用QPixmap::grabWidget方法,您可以让它看起来像您的按钮,并使用QDrag::setPixmap方法将它分配给QDrag实例。

Event if all you are trying to do is move the widget around in the parent space, I would recommend using this framework and just accepting the drop event for your the button. Then you don't trigger a bunch of unnecessary redraws.

如果您所要做的只是在父空间中移动小部件,那么我建议使用这个框架,并接受这个按钮的drop事件。这样你就不会触发一堆不必要的重绘。

#1


11  

Here is an example of a moveable button that still supports the normal click signal properly:

下面是一个移动按钮的例子,它仍然支持正常的点击信号:

from PyQt4 import QtCore, QtGui


class DragButton(QtGui.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()

        super(DragButton, self).mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if event.buttons() == QtCore.Qt.LeftButton:
            # adjust offset from clicked point to origin of widget
            currPos = self.mapToGlobal(self.pos())
            globalPos = event.globalPos()
            diff = globalPos - self.__mouseMovePos
            newPos = self.mapFromGlobal(currPos + diff)
            self.move(newPos)

            self.__mouseMovePos = globalPos

        super(DragButton, self).mouseMoveEvent(event)

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

        super(DragButton, self).mouseReleaseEvent(event)

def clicked():
    print "click as normal!"

if __name__ == "__main__":
    app = QtGui.QApplication([])
    w = QtGui.QWidget()
    w.resize(800,600)

    button = DragButton("Drag", w)
    button.clicked.connect(clicked)

    w.show()
    app.exec_()

In the mousePressEvent I record both the initial start position, and a position that will get updated throughout the drag.

在mousePressEvent中,我记录了初始起始位置和在整个拖动过程中会得到更新的位置。

In the mouseMoveEvent, I get the proper offset of the widget from where it was clicked to where the actual origin is, so that the move is accurate.

在mouseMoveEvent中,我得到了一个小部件的适当偏移量,从它被点击的地方到实际的原点,这样移动是准确的。

In the mouseReleaseEvent, I check to see if the overall move was greater than at least a tiny amount. If it was, then it was a drag and we ignore the normal event to not produce a "clicked" signal. Otherwise, we allow the normal event handler to produce the click.

在mouseReleaseEvent中,我查看总体移动是否大于至少一个小数目。如果是,那么它就是一个拖拽,我们忽略了正常的事件,不产生一个“点击”的信号。否则,我们允许正常的事件处理程序生成click。

#2


2  

It depends on what you are trying to do. If you are trying to do actual "Drag & Drop", you're going about it wrong. What you are doing is just moving the button around in its X,Y coordinate space within its parent. Its never actually invoking any Drag/Drop events, those are entirely different.

这取决于你想做什么。如果你想做实际的“拖放”,那你就错了。你所做的就是在它的X,Y坐标空间中移动这个按钮。它从来没有实际调用任何拖放事件,它们是完全不同的。

You should read through the drag & drop documentation here:

您应该阅读下面的拖放文档:

http://doc.qt.nokia.com/4.7-snapshot/dnd.html
http://qt-project.org/doc/qt-5.0/qdrag.html

http://doc.qt.nokia.com/4.7-snapshot/dnd.html http://doc.qt.nokia.com/4.7-snapshot/dnd.html project.org/doc/qt - 5.0 - / - qdrag.html

Instead of moving the button within the mousePressEvent, you'll need to create a new QDrag object and execute it. You can make it look like your button by taking a snapshot of your button using the QPixmap::grabWidget method and assign it to the QDrag instance using the QDrag::setPixmap method.

无需在mousePressEvent中移动按钮,您需要创建一个新的QDrag对象并执行它。通过使用QPixmap::grabWidget方法,您可以让它看起来像您的按钮,并使用QDrag::setPixmap方法将它分配给QDrag实例。

Event if all you are trying to do is move the widget around in the parent space, I would recommend using this framework and just accepting the drop event for your the button. Then you don't trigger a bunch of unnecessary redraws.

如果您所要做的只是在父空间中移动小部件,那么我建议使用这个框架,并接受这个按钮的drop事件。这样你就不会触发一堆不必要的重绘。