回答此问题可获得 20 贡献值,回答如果被采纳可获得 50 分。
<p>我有一个包含两个按钮的小部件,可以使用鼠标中键(拖放)交换。我试图在拖放Qpushbutton时阻止鼠标光标离开QWidget区域。。。我使用的是dragMoveEvent(),每次光标穿过小部件的边界时,它都会偏移光标。当您缓慢移动鼠标时,它会工作,但快速移动会使光标离开该区域。实现这一目标的最佳方式是什么?谢谢</p>
<p>PS:转到拖放区域以供参考</p>
<pre><code>
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_()
</code></pre>
<hr/>
<p>编辑</p>
<hr/>
<p>因此,在对LINUX/WINDOWS上的代码进行进一步调查和测试后,我得出结论,这两种行为都是由程序超出最大递归限制引起的。拖动事件期间的任何时候鼠标光标离开指定的小部件,都会导致事件相互调用,从而导致我的应用程序崩溃。把它作为一个独立的应用不会引起任何问题,我不知道为什么?另外,我不知道这个程序是如何进入递归的</p>
<p>我以前的解决方案是,我试图为鼠标创建一个“安全区”,但没有解决问题,因为某些鼠标移动会导致相同的错误</p>
<p>下面是一个更好的工作代码版本。正如我已经提到的,它作为一个独立的GUI工作,但会导致程序在另一个软件环境中崩溃</p>
<pre><code>from __future__ import print_function
import sys
try:
from PySide import QtWidgets, QtCore
except ImportError:
from PySide2 import QtCore
from PySide2 import QtWidgets
from PySide2 import QtGui
from PySide2 import QtOpenGL
class CreateNodeBoard(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
################################################################################
# GUI
################################################################################
self.setMinimumWidth(350)
self.mainLayout = QtWidgets.QVBoxLayout()
self.mainLayout.setSpacing(0)
self.mainLayout.setAlignment(QtCore.Qt.AlignTop)
self.setLayout(self.mainLayout)
self.target = None
self.targetWidget = None
self.setAcceptDrops(True)
################################################################################
# GUI - NODE BOARD
################################################################################
# Create a Layout to hold all widgets
self.nodeBoardWidget = QtWidgets.QWidget()
self.nodeBoardWidget.setAcceptDrops(True)
nodeBoardVLayout = QtWidgets.QVBoxLayout()
self.nodeBoardWidget.setLayout(nodeBoardVLayout)
# create a grid layout inside nodeBoaardVLayout and load buttons from JSON
self.userButtonLayout = QtWidgets.QGridLayout()
nodeBoardVLayout.addLayout(self.userButtonLayout)
button1 = QtWidgets.QPushButton('button1')
self.userButtonLayout.addWidget(button1)
button2 = QtWidgets.QPushButton('button2')
self.userButtonLayout.addWidget(button2)
button3 = QtWidgets.QPushButton('test button')
button3.clicked.connect(self._test)
self.userButtonLayout.addWidget(button3)
self.userButtonLayout.setAlignment(QtCore.Qt.AlignLeft)
self.mainLayout.addWidget(self.nodeBoardWidget)
nodeBoardVLayout.addStretch(1)
############################################################################
# test
############################################################################
def _test(self):
print(self.topLevelWidget())
def dragLeaveEvent(self, event):
print("dragLeaveEvent :", event)
# XXX: does not work on macOS
# self.drag.cancel()
# parent = self.parent().mapToGlobal(self.drag.hotSpot())
# QtGui.QCursor.setPos(parent.x() + 50, parent.y() + 50)
# XXX: could still causes a crash
# q = QMessageBox()
# q.setText('no can do')
# q.exec_()
def leaveEvent(self, event):
pass
def enterEvent(self, event):
pass
################################################################################
# DRAG AND DROP
################################################################################
def get_index(self, pos):
for i in range(self.userButtonLayout.count()):
buttonGlob = self.userButtonLayout.itemAt(
i).widget().mapToGlobal(QtCore.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(QtGui.QCursor.pos())
else:
self.target = None
def mouseMoveEvent(self, event):
if event.buttons() and QtCore.Qt.MiddleButton and self.target is not None:
print("mouseClickEvent :", event)
self.drag = QtGui.QDrag(
self.userButtonLayout.itemAt(self.target).widget())
pix = self.userButtonLayout.itemAt(self.target).widget().grab()
mimedata = QtCore.QMimeData()
mimedata.setImageData(pix)
self.drag.setMimeData(mimedata)
self.drag.setPixmap(pix)
self.drag.setHotSpot(QtCore.QPoint(40, 10))
self.drag.exec_()
def dragMoveEvent(self, event):
# print("dragMoveEvent :", event)
cursorPos = QtGui.QCursor.pos()
widgetPos = self.nodeBoardWidget.mapToGlobal(QtCore.QPoint(0, 0))
if cursorPos.x() <= widgetPos.x() or cursorPos.y() <= widgetPos.y():
QtGui.QCursor.setPos(QtGui.QCursor.pos().x() +
10, QtGui.QCursor.pos().y() + 10)
def dragEnterEvent(self, event):
print("dragEnterEvent :", event)
# XXX: if ignored, will not crash but will not propagate events
event.accept()
def dropEvent(self, event):
# print("dropEvent :", event)
buttonGlob = self.userButtonLayout.itemAt(
self.target).widget().mapToGlobal(self.pos())
if not QtCore.QRect(buttonGlob.x(), buttonGlob.y(), 80, 23).contains(QtGui.QCursor.pos()):
source = self.get_index(QtGui.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
class TestWidget(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.test_widget = QtWidgets.QWidget()
self.set_test()
_layout = QtWidgets.QHBoxLayout()
_layout.addWidget(CreateNodeBoard())
_layout.addWidget(self.test_widget)
self.setLayout(_layout)
def set_test(self):
"""Adjacent test widget"""
self.test_widget.setAutoFillBackground(True)
self.test_widget.setPalette(QtGui.QColor(255, 0, 0))
_test_layout = QtWidgets.QVBoxLayout()
_test_layout.addWidget(QtWidgets.QLabel('TEST WIDGET'))
self.test_widget.setLayout(_test_layout)
try:
import nukescripts
except ImportError as error:
APP = QtWidgets.QApplication(sys.argv)
WINDOW = TestWidget()
WINDOW.show()
APP.exec_()
else:
nukescripts.panels.registerWidgetAsPanel(
'TestWidget', 'DragDrop',
'DragDrop.MainWindow')
</code></pre>