关于qt:Python:如何防止QTimer.singleShot(..)函数中的延迟扩展?

Python: How do I prevent delayed expansion in the QTimer.singleShot(..) function?

本问题已经有最佳答案,请猛点这里访问。

我经常使用QTimer.singleShot(..)函数延迟执行某些动作/功能。当然,我也可以使用time.sleep(..)进行操作,但这会冻结用户界面。

有时您想将参数交给延迟功能。这就是Python的lambda功能派上用场的地方。看下面的例子:

1
2
3
4
5
6
7
8
    #######################################
    #   Print 'Hello world' vertically    #
    #######################################
    myString = 'Hello world'
    i = 0
    for c in myString:
        QtCore.QTimer.singleShot(10 + i*100, lambda: print(str(c)))
        i += 1

可以直观地预期左侧显示的输出。但实际上,您实际上获得了字符" d"的垂直列表-这是(惊讶)给定字符串中的最后一个字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
         _                                _
    H     |                          d     |
    e     |                          d     |
    l     |                          d     |
    l     |                          d     |
    o     |   Result you             d     |   Result you
           >  would expect           d      >  actually get!
    w     |                          d     |
    o     |                          d     |
    r     |                          d     |
    l     |                          d     |
    d     |                          d     |
         _|                               _|

似乎QTimer.singleShot(..)函数应用了"延迟扩展"原理。在某些情况下这很好,但并非总是如此。

您如何控制扩展?您可以强制立即扩展吗?

示例代码:

将以下代码复制粘贴到一个新的python文件中,然后从shell中运行它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    import sys
    from PyQt4 import QtGui, QtCore

    class MainForm(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(MainForm, self).__init__(parent)

            # create button
            self.button = QtGui.QPushButton("QTimer.singleShot(..)", self)
            self.button.clicked.connect(self.btnAction)
            self.button.resize(200, 30)


        def btnAction(self):
            myString = 'Hello world'
            i = 0
            for c in myString:
                QtCore.QTimer.singleShot(10 + i*100, lambda: print(str(c)))
                i += 1


    def main():
        app = QtGui.QApplication(sys.argv)
        form = MainForm()
        form.show()
        app.exec_()

    if __name__ == '__main__':
        main()

单击按钮,您将在外壳中看到" Hello world"示例:

enter image description here


我想我找到了答案。 functools.partial(..)库允许您执行与lambda:..功能完全相同的操作,只是参数被冻结:-)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import sys
from PyQt4 import QtGui, QtCore
import functools



class MainForm(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)

        # create button
        self.button = QtGui.QPushButton("QTimer.singleShot(..)", self)
        self.button.clicked.connect(self.btnAction)
        self.button.resize(200, 30)


    def btnAction(self):
        myString = 'Hello world'
        i = 0
        for c in myString:
            #QtCore.QTimer.singleShot(10 + i*100, lambda: print(str(c)))
            QtCore.QTimer.singleShot(10 + i * 100, functools.partial(print, str(c)))
            i += 1


def main():
    app = QtGui.QApplication(sys.argv)
    form = MainForm()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()

编辑:

谢谢@ekhumoro的评论。 您的代码行也可以正常工作:

1
QtCore.QTimer.singleShot(10 + i*100, lambda arg=c: print(str(arg)))