Skip to content

Instantly share code, notes, and snippets.

@niccokunzmann
Last active March 1, 2023 15:52
Show Gist options
  • Save niccokunzmann/8673951 to your computer and use it in GitHub Desktop.
Save niccokunzmann/8673951 to your computer and use it in GitHub Desktop.
You can use while and for loops without blocking the GUI. This is currently for Tkinter and PyQT. To install guiLoop copy the guiLoop.py file into the directory of your script.
## MIT License
##
## Copyright 2014 Nicco Kunzmann
##
## Permission is hereby granted, free of charge, to any person obtaining a copy of this
## software and associated documentation files (the "Software"), to deal in the Software
## without restriction, including without limitation the rights to use, copy, modify, merge,
## publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
## to whom the Software is furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included in all copies
## or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
## INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
## PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
## FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
## OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
## DEALINGS IN THE SOFTWARE.
"""
This script uses a while loop that lets an LED blink while the GUI is still responsive.
Running this script outputs
LED on shiny!
LED off shiny!
LED on shiny!
LED off shiny!
LED on shiny!
LED off shiny!
while the GUI is responsive.
This was created because of an stackoverflow question:
http://stackoverflow.com/questions/21411748/python-how-do-i-continuously-repeat-a-sequence-without-a-while-loop-and-still
examples:
http://stackoverflow.com/questions/21440731/tkinter-getting-key-pressed-event-from-a-function
https://gist.github.com/niccokunzmann/8673951#file-example-py
https://gist.github.com/niccokunzmann/8673951#file-start_and_stop-py
"""
try: from Tkinter import *
except ImportError: from tkinter import *
from guiLoop import guiLoop # https://gist.github.com/niccokunzmann/8673951
@guiLoop
def led_blink(argument):
while 1:
print("LED on " + argument)
yield 0.5 # time to wait
print("LED off " + argument)
yield 0.5
t = Tk()
# run led_blink in this GUI
led_blink(t, 'shiny!')
### you can run several loops at once:
##led_blink(t, 'red')
##led_blink(t, 'blue')
##led_blink(t, 'green')
# add a responsive button
def button_pressed():
print('button pressed')
Button(t, command = button_pressed, text = 'press me').pack()
# start the GUI loop
t.mainloop()
## MIT License
##
## Copyright 2014 Nicco Kunzmann
##
## Permission is hereby granted, free of charge, to any person obtaining a copy of this
## software and associated documentation files (the "Software"), to deal in the Software
## without restriction, including without limitation the rights to use, copy, modify, merge,
## publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
## to whom the Software is furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included in all copies
## or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
## INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
## PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
## FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
## OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
## DEALINGS IN THE SOFTWARE.
"""
This shows how to use guiLoop as an method of a gui element object
This script uses a while loop that lets an LED blink while the GUI is still responsive.
"""
try: from Tkinter import *
except ImportError: from tkinter import *
from guiLoop import guiLoop # https://gist.github.com/niccokunzmann/8673951
class MyWindow(Tk):
def __init__(self):
Tk.__init__(self)
self.led_blink('blue')
@guiLoop
def led_blink(self, argument):
while 1:
print("LED on " + argument)
yield 0.5 # time to wait
print("LED off " + argument)
yield 0.5
t = MyWindow()
t.mainloop()
## MIT License
##
## Copyright 2014 Nicco Kunzmann
##
## Permission is hereby granted, free of charge, to any person obtaining a copy of this
## software and associated documentation files (the "Software"), to deal in the Software
## without restriction, including without limitation the rights to use, copy, modify, merge,
## publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
## to whom the Software is furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included in all copies
## or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
## INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
## PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
## FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
## OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
## DEALINGS IN THE SOFTWARE.
"""
This script uses a while loop that lets an LED blink while the GUI is still responsive.
Running this script outputs
LED on shiny!
LED off shiny!
LED on shiny!
LED off shiny!
LED on shiny!
LED off shiny!
while the GUI is responsive.
This was created because of an stackoverflow question:
http://stackoverflow.com/questions/23057031/how-to-quit-the-program-in-while-loop-using-push-button-in-pyqt/23057966#23057966
"""
from PyQt4 import QtGui
import sys
from guiLoop import guiLoop # https://gist.github.com/niccokunzmann/8673951
@guiLoop
def led_blink(argument):
while 1:
print("LED on " + argument)
yield 0.5 # time to wait
print("LED off " + argument)
yield 0.5
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
w.resize(250, 150)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
led_blink(w, 'shiny!')
sys.exit(app.exec_())
## MIT License
##
## Copyright 2014 Nicco Kunzmann
##
## Permission is hereby granted, free of charge, to any person obtaining a copy of this
## software and associated documentation files (the "Software"), to deal in the Software
## without restriction, including without limitation the rights to use, copy, modify, merge,
## publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
## to whom the Software is furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included in all copies
## or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
## INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
## PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
## FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
## OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
## DEALINGS IN THE SOFTWARE.
"""
guiLoop lets you use while and for loops with GUIs.
Usually using loops in GUIs makes them block.
This module uses the yield statement in loops to let the GUI update while the loop is still running.
See the example.py or start_and_stop.py for examples.
The code is available here: https://gist.github.com/niccokunzmann/8673951#file-guiloop-py
Currently only tkinter is supported but with a little help this can be adapted to other GUI frameworks, too.
Change the function _start_in_gui for different GUI frameworks.
If you use this code for an other GUI than tkinter send me your code or
leave a comment so that some day this can become a module on pypi.python.org
useful for others, too.
This was created because of an stackoverflow question:
http://stackoverflow.com/questions/21411748/python-how-do-i-continuously-repeat-a-sequence-without-a-while-loop-and-still
"""
def use_tkinter_after(gui_element, wait_time, call_this):
# the following line needs replacement depending on the GUI
# it calls 'call_this' after a period of time 'wait_time' in ms
# for Tkinter
gui_element.after(wait_time, call_this)
def use_PyQT4_QTimer(gui_element, wait_time, call_this):
from PyQt4.QtCore import QTimer
QTimer.singleShot(wait_time, call_this)
def use_any_timer(gui_element, wait_time, call_this):
if hasattr(gui_element, 'after'):
use_tkinter_after(gui_element, wait_time, call_this)
elif hasattr(gui_element, 'pyqtConfigure'):
use_PyQT4_QTimer(gui_element, wait_time, call_this)
else:
raise TypeError("Can not automatically detect which GUI this is.")
def _loop_in_the_gui(gui_element, generator, _start_in_gui):
try:
# generator yields the time to wait
wait_time = next(generator)
except StopIteration:
pass
else:
if wait_time is None:
# yield
wait_time = 0
else:
# yield seconds
wait_time = int(wait_time * 1000) # Tkinter works with milli seconds
call_this_again = lambda: _loop_in_the_gui(gui_element, generator,
_start_in_gui)
_start_in_gui(gui_element, wait_time, call_this_again)
class guiLoop(object):
def __init__(self, function, start_in_gui = use_any_timer):
"""make a function to a guiLoop function
The resulting function needs a gui element as first argument."""
self.function = function
self.__doc__ = function.__doc__
self.__name__ = function.__name__
self.start_in_gui = start_in_gui
def __call__(self, gui_element, *args, **kw):
generator = self.function(*args, **kw)
_loop_in_the_gui(gui_element, generator, self.start_in_gui)
return generator
def __get__(self, gui_element, cls):
if gui_element is None:
return self
return lambda *args, **kw: self(gui_element, gui_element, *args, **kw)
def tkLoop(function):
"""a guiLoop for tkinter"""
return guiLoop(function, use_tkinter_after)
def qt4Loop(function):
"""a guiLoop for PyQT4"""
return guiLoop(function, use_PyQT4_QTimer)
class StopLoopException(Exception):
"""This is raised if the loop shall stop"""
pass
def stopLoop(generator):
"""stop the loop
Generator is the return value of guiLoop."""
try: generator.throw(StopLoopException())
except StopLoopException: pass
__all__ = ['guiLoop', 'stopLoop', 'StopLoopException', 'tkLoop', 'qt4Loop']
## MIT License
##
## Copyright 2014 Nicco Kunzmann
##
## Permission is hereby granted, free of charge, to any person obtaining a copy of this
## software and associated documentation files (the "Software"), to deal in the Software
## without restriction, including without limitation the rights to use, copy, modify, merge,
## publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
## to whom the Software is furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included in all copies
## or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
## INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
## PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
## FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
## OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
## DEALINGS IN THE SOFTWARE.
"""
This script uses a while loop that lets an LED blink while the GUI is still responsive.
It shows how started loops can be interrupted.
Example 1 throws an error in the loop and makes it terminate immediately.
Example 2 uses a shared variable to exit the loop when the LED is turned off again.
This was created because of an stackoverflow question:
http://stackoverflow.com/questions/21411748/python-how-do-i-continuously-repeat-a-sequence-without-a-while-loop-and-still
"""
try: from Tkinter import *
except ImportError: from tkinter import *
from guiLoop import guiLoop, stopLoop # https://gist.github.com/niccokunzmann/8673951
@guiLoop
def led_blink(state):
while state != ["stop"]:
print("LED on")
yield 0.5 # time to wait
print("LED off")
yield 0.5
t = Tk()
def button_pressed1():
"stop the function throwing an error in yield"
generator = led_blink(b1, [])
def turn_off():
stopLoop(generator)
b1["command"] = button_pressed1
b1["command"] = turn_off
# this button stops at any yield
b1 = Button(t, command = button_pressed1, text = 'press me (1)')
b1.pack()
def button_pressed2():
"stop the function using a shared variable"
state = []
generator = led_blink(b2, state)
def turn_off():
state.append("stop")
b2["command"] = button_pressed2
b2["command"] = turn_off
# this button stops when the loop is done
b2 = Button(t, command = button_pressed2, text = 'press me (2)')
b2.pack()
t.mainloop()
@AISWARYA2019
Copy link

How to install guiLoop ?

@niccokunzmann
Copy link
Author

@AISWARYA2019 you can download it and copy it next to the file you import it from. If you download the ZIP file, the example files should work because the guiLoop is next to them in the same directory.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment