如何在pyside2中限制鼠标光标离开qwidget区域

5ktev3wc  于 2021-08-25  发布在  Java
关注(0)|答案(1)|浏览(396)

我有一个包含两个按钮的小部件,可以使用鼠标中键(拖放)交换。我试图在拖放qpushbutton时阻止鼠标光标离开qwidget区域。。。我使用的是dragmoveevent(),每次光标穿过小部件的边界时,它都会偏移光标。当您缓慢移动鼠标时,它会工作,但快速移动会使光标离开该区域。实现这一目标的最佳方式是什么?谢谢

import os
import random
import sys
import time
from PySide2 import QtOpenGL
from PySide2 import QtWidgets
from PySide2.QtCore import QEvent, QMimeData, QPoint, QRect
from PySide2.QtGui import QCursor, QDrag, QWindow

# import nuke

# import nukescripts

from collapse import Collapse
try:
    from PySide import QtGui, QtCore
except ImportError:
    from PySide2 import QtCore
    from PySide2 import QtWidgets as QtGui
    from PySide2 import QtGui as QtG

class CreateNodeBoard(QtGui.QWidget):
    def __init__(self, parent = None):
        QtGui.QWidget.__init__(self, parent)

        self.nukePathSeparator = "/"
        #self.toolPath = self.getFullPathWithExt()
        self.currentDir = os.path.dirname(os.path.realpath(__file__))

    ################################################################################
    # GUI
    ################################################################################
        self.setMinimumWidth(350)        
        self.mainLayout = QtGui.QVBoxLayout()
        self.mainLayout.setSpacing(0)
        self.mainLayout.setAlignment(QtCore.Qt.AlignTop)
        self.setLayout(self.mainLayout)
        self.target = None
        self.setAcceptDrops(True)

        self.nodeBoardWidget = QtGui.QWidget()
        self.nodeBoardWidget.setAcceptDrops(True)
        nodeBoardVLayout = QtWidgets.QVBoxLayout()
        self.nodeBoardWidget.setLayout(nodeBoardVLayout)

        self.userButtonLayout = QtGui.QGridLayout()
        nodeBoardVLayout.addLayout(self.userButtonLayout)
        button1 = QtWidgets.QPushButton("a")
        button2 = QtWidgets.QPushButton("b")
        self.userButtonLayout.addWidget(button1)
        self.userButtonLayout.addWidget(button2)
        self.userButtonLayout.setAlignment(QtCore.Qt.AlignLeft)

        self.mainLayout.addWidget(self.nodeBoardWidget)
    def get_index(self, pos):
        for i in range(self.userButtonLayout.count()):
            buttonGlob = self.userButtonLayout.itemAt(i).widget().mapToGlobal(QPoint(0,0)) 
            if QtCore.QRect(buttonGlob.x(), buttonGlob.y(), 80, 23).contains(pos) and i != self.target:
                return i

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.MiddleButton:           
            self.target = self.get_index(QCursor.pos())
        else:
            self.target = None

    def mouseMoveEvent(self, event):        
        if event.buttons() & QtCore.Qt.MiddleButton and self.target is not None:
            print("moving")
            drag = QDrag(self.userButtonLayout.itemAt(self.target).widget())
            pix = self.userButtonLayout.itemAt(self.target).widget().grab()
            mimedata = QMimeData()
            mimedata.setImageData(pix)
            drag.setMimeData(mimedata)
            drag.setPixmap(pix)
            drag.setHotSpot(QPoint(40,10))
            drag.exec_()

    def dragMoveEvent(self, event):
        cursorPos = QCursor.pos()
        widgetPos = self.nodeBoardWidget.mapToGlobal(QPoint(0,0))
        if cursorPos.x() < widgetPos.x() or cursorPos.y() < widgetPos.y():
           QCursor.setPos(QCursor.pos().x() + 1 , QCursor.pos().y() + 1 )
        event.accept()

    def dragEnterEvent(self, event):
        print("drag enter event")
        if event.mimeData().hasImage():
              event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        print("drop")
        buttonGlob = self.userButtonLayout.itemAt(self.target).widget().mapToGlobal(self.pos())
        if not QtCore.QRect(buttonGlob.x(), buttonGlob.y(), 80, 23).contains(QCursor.pos()):
            source = self.get_index(QCursor.pos())
            if source is None:
                return

            i, j = max(self.target, source), min(self.target, source)
            p1, p2 = self.userButtonLayout.getItemPosition(i), self.userButtonLayout.getItemPosition(j)

            self.userButtonLayout.addItem(self.userButtonLayout.takeAt(i), *p2)
            self.userButtonLayout.addItem(self.userButtonLayout.takeAt(j), *p1)
            self.target = None

app = QtWidgets.QApplication(sys.argv)

# Create a Qt widget, which will be our window.

window = CreateNodeBoard()
window.show()  # IMPORTANT!!!!! Windows are hidden by default.

# Start the event loop.

app.exec_()
bd1hkmkf

bd1hkmkf1#

前提

这个答案仅限于特定的问题(防止用户将鼠标移动到给定小部件的边界之外)。不幸的是,由于给定代码中存在许多概念性问题,这并不是一个完整的解决方案:
拖放事件应始终由实际处理它们的小部件管理(在本例中, nodeBoardWidget ),而不是他们的父母;
获取项目的布局索引应始终考虑项目几何结构(使用固定大小是令人沮丧的,因为控件大小取决于很多方面)和项目不能是小部件的事实(嵌套布局仍然是布局项目)。 layout.itemAt().widget() 可能会回来 None );
基于项目索引的“交换”项目并不总是保留项目索引,因为生成的索引可能不可靠(特别是对于网格布局);

部分解

要记住的一个重要方面是,尝试移动鼠标一小部分并固定其位置是错误的,因为鼠标事件不是连续的:如果鼠标从 x=0x=100 并不是所有值都在0到100之间,而是中间位置的一小部分。
出于同样的原因,尝试仅通过固定数量的像素“固定”位置是错误的,因为偏移量可以根据鼠标速度而变化。
上述结果 dragMoveEvent 如果鼠标在父边界外移动过快,则不会调用。虽然在您的具体案例中它“起作用”,但这只是因为您在父级中实现了该功能(如前所述,这不是建议的方法,这是一个明确的例子)。如果必须“包含”鼠标位置,则 dragLeaveEvent 而必须实施。

class CreateNodeBoard(QtWidgets.QWidget):
    def __init__(self, parent = None):
        # ...
        self.targetWidget = None

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.MiddleButton:
            widget = QtWidgets.QApplication.widgetAt(event.globalPos())
            if (widget != self.nodeBoardWidget and 
                self.nodeBoardWidget.isAncestorOf(widget)):
                    self.targetWidget = widget

    def mouseMoveEvent(self, event):
        if self.targetWidget:
            drag = QDrag(self.targetWidget)
            pix = self.targetWidget.grab()
            mimedata = QMimeData()
            mimedata.setImageData(pix)
            drag.setMimeData(mimedata)
            drag.setPixmap(pix)
            drag.setHotSpot(QPoint(40,10))
            drag.exec_()

    def dragEnterEvent(self, event):
        if self.nodeBoardWidget.isAncestorOf(event.source()):
            event.accept()

    def dragLeaveEvent(self, event):
        geo = self.nodeBoardWidget.rect().translated(
            self.nodeBoardWidget.mapToGlobal(QtCore.QPoint()))
        pos = QtGui.QCursor.pos()
        if pos not in geo:
            if pos.x() < geo.x():
                pos.setX(geo.x())
            elif pos.x() > geo.right():
                pos.setX(geo.right())
            if pos.y() < geo.y():
                pos.setY(geo.y())
            elif pos.y() > geo.bottom():
                pos.setY(geo.bottom())
            QtGui.QCursor.setPos(pos)

我强烈建议您研究上面的示例和注意事项,因为您的代码有许多概念性问题,如果不从头创建一个全新的示例,我的答案将无法解决这些问题。另外,很明显,您从web上的各种来源获取代码,因此我也建议您提高认识。模仿是一种很好的学习方式,但不是不了解正在做什么。研究所有使用的函数和类,研究所有相关文档,从布局管理器开始,拖放,不要忘记官方代码样式实践。

相关问题