I often use the QTimer.singleShot(..)
function to execute a certain action/function with some delay. Sure, I could do it with a time.sleep(..)
as well, but that would freeze my user interface.
Sometimes you want to hand over a parameter to the delayed function. That's where the lambda
feature of Python comes in handy. Take a look at the following example:
#######################################
# 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
One would intuitively expect the output shown on the left. But you actually get a vertical list of character 'd' - which is (surprise) the last character in the given string.
_ _
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 |
_| _|
It seems like the QTimer.singleshot(..)
function applies the 'delayed expansion' principle. That's great in some situations, but not always.
How do you get control over the expansion? Can you force immediate expansion?
Example code :
Copy-paste the following code into a fresh python file, and run it from the shell.
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()
You will see the 'Hello world' example in your shell when you click the button: