【问题标题】:How to test drag and drop behavior in PyQt?如何在 PyQt 中测试拖放行为?
【发布时间】:2014-06-10 15:07:04
【问题描述】:

我想对我们的小部件的拖放进行单元测试。目前,我实例化了一个 QDragEnterEvent,但是由于 Qt 文档中的一个大警告不鼓励这样做,因为它依赖于 Qt 库内部状态。事实上,我收到的段错误似乎是由于违反了此警告。

在此前提下,如何测试拖放行为?

【问题讨论】:

    标签: qt testing pyqt


    【解决方案1】:

    如果使用 Unix,我们可以使用 QTest,但是为了获得跨平台解决方案,我们可以实现一个绕过 Qt 的解决方案。

    使用 QTest

    虽然 drag and drop 的 Qt 文档说它不会阻塞主事件循环,但仔细查看 QDrag.exec 会发现这不适用于 Windows。

    QTest.mousePress 的调用会导致测试阻塞,直到用户实际移动鼠标。

    我在 Linux 中通过使用计时器来安排鼠标移动和释放来解决这个问题:

    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

    请注意,我已经在 Fedora 20 上使用 pyqt4 和 Python 2.7 验证了这项工作

    跨平台

    您可以使用PyUserInput 包中的鼠标操作方法。将鼠标交互放在单独的线程中,以避免 Qt 主事件循环的锁定。我们可以这样做,因为我们在鼠标控制中根本没有使用 Qt。确保您在拖入/拖出的小部件上调用了show

    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 对此进行了测试

    【讨论】:

      【解决方案2】:

      没有尝试过,但如果您的拖放过程是 Qt 内部的(也就是说,您正在从 Qt 小部件拖放到 Qt 小部件),QTest 可能会有所帮助。

      基本上通过做一些事情:

      QTest.mousePress(drag_widget, Qt.LeftButton) # simulate mouse press on whatever you want to drag
      QTest.mouseMove(drop_widget) # move the mouse to the target - maybe this can be skipped
      QTest.mouseRelease(drop_widget, Qt.LeftButton) # simulate mouse release where you want to drop
      

      所有功能都可以提供进一步的位置信息(例如,单击小部件中的列表项),并提供可选的延迟以模拟人类用户。

      不是可复制粘贴的答案,但也许可以作为入门...

      【讨论】:

      • 你发的很有意思,我去看看,谢谢。这些方法我通常遇到的唯一问题是它们需要精确的定位和偏移,我发现这是相当不稳定的(例如,如果测试机桌面上存在另一个窗口)。无论如何,这可能是官方的方式。
      • 我想投票,但我更想知道它是否有效。如果@Stefano 可以测试并发布结果,我很乐意投票。
      • 这不起作用 - 我在 Windows 8.1 和 Qt 5.4.2 上进行了尝试。 mouseMove() 创建一个没有按钮的鼠标事件——这可能是问题所在。
      • 解决完全相同的问题。遗憾的是,这个提议的解决方案不起作用,我在带有 Qt 5.2.1 的 Xubuntu 14.04.5 LTS 上进行了尝试。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-03
      • 1970-01-01
      • 2021-08-12
      • 2019-08-17
      • 1970-01-01
      • 2019-01-24
      • 1970-01-01
      相关资源
      最近更新 更多