问题描述
我想对我们的小部件进行单元测试拖放.目前,我实例化了一个 QDragEnterEvent,但由于 Qt 文档中的一个大警告,不鼓励这样做,因为它依赖于 Qt 库内部状态.事实上,我收到的段错误似乎是由于违反了此警告.
I want to unittest drag and drop for our widgets. At the moment, I instantiate a QDragEnterEvent, but this is discouraged with a big warning on the Qt documentation, because it relies on the Qt library internal state. In fact, I get segfaults that appear to be due to a violation of this Warning.
在此前提下,如何测试拖放行为?
Given this premise, how can one test drag and drop behavior?
推荐答案
如果使用 Unix,我们可以使用 QTest,但是为了获得跨平台的解决方案,我们可以实现绕过 Qt 的解决方案.
尽管 拖放 的 Qt 文档说它不会阻塞主事件循环,仔细看看 QDrag.exec
将揭示这不适用于 Windows.
Although the Qt documentation for drag and drop says that it will not block the main event loop, a closer look at QDrag.exec
will reveal that this is not true for Windows.
调用 QTest.mousePress
导致测试阻塞,直到用户物理移动鼠标.
The call to QTest.mousePress
causes the test to block until the mouse is physically moved by the user.
我在 Linux 中通过使用计时器来安排鼠标移动和释放来解决这个问题:
I got around this in Linux by using a timer to schedule the mouse move and release:
def testDragAndDrop(self):
QtCore.QTimer.singleShot(100, self.dropIt)
QtTest.QTest.mousePress(dragFromWidget, QtCore.Qt.LeftButton)
# check for desired behaviour after drop
assert something
def dropIt(self):
QtTest.QTest.mouseMove(dropToWidget)
QtTest.QTest.mouseRelease(dropToWidget, QtCore.Qt.LeftButton, delay=15)
对于此解决方案,必须在 mouseRelease
调用中包含延迟,并在您的小部件上调用 show
.
For this solution, it is necessary to include a delay in the mouseRelease
call, and to have called show
on your widget.
请注意,我已经在 Fedora 20 上使用 pyqt4 和 Python 2.7 验证了这一点
Note that I have verified this works using pyqt4 and Python 2.7 on Fedora 20
您可以使用 PyUserInput 包中的鼠标操作方法.将鼠标交互放在单独的线程中,以避免锁定 Qt 主事件循环.我们可以这样做,因为我们在鼠标控制中根本没有使用 Qt.确保您在拖入/拖出的小部件上调用了 show
.
You can use the mouse manipulation methods from the PyUserInput package. Put the mouse interaction in separate thread to avoid the locking up of the Qt main event loop. We can do this since we are not using Qt at all in our mouse control. Make sure that you have called show
on the widgets you are dragging to/from.
from __future__ import division
import sys, time, threading
import numpy as np
from PyQt4 import QtGui, QtCore, QtTest
from pymouse import PyMouse
...
def mouseDrag(source, dest, rate=1000):
"""Simulate a mouse visible mouse drag from source to dest, rate is pixels/second"""
mouse = PyMouse()
mouse.press(*source)
# smooth move from source to dest
npoints = int(np.sqrt((dest[0]-source[0])**2 + (dest[1]-source[1])**2 ) / (rate/1000))
for i in range(npoints):
x = int(source[0] + ((dest[0]-source[0])/npoints)*i)
y = int(source[1] + ((dest[1]-source[1])/npoints)*i)
mouse.move(x,y)
time.sleep(0.001)
mouse.release(*dest)
def center(widget):
midpoint = QtCore.QPoint(widget.width()/2, widget.height()/2)
return widget.mapToGlobal(midpoint)
def testDragAndDrop(self):
# grab the center of the widgets
fromPos = center(dragFromWidget)
toPos = center(dropToWidget)
dragThread = threading.Thread(target=mouseDrag, args=((fromPos.x(),fromPos.y()), (toPos.x(), toPos.y())))
dragThread.start()
# cannot join, use non-blocking wait
while dragThread.is_alive():
QtTest.QTest.qWait(1000)
# check that the drop had the desired effect
assert dropToWidget.hasItemCount() > 0
请注意,我已经在 Fedora 和 Windows 7 上使用 PyQt4 和 Python 2.7 对此进行了测试
Note I have tested this using PyQt4 and Python 2.7 on Fedora and Windows 7
这篇关于如何在 PyQt 中测试拖放行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!