Pause a python tkinter script indefinetly - python

I have a game in Tkinter in which I want to implement a pause option. I want to bind the key p to stop the script. I tried using time.sleep, but I want to pause the game until the user presses u. I have tried:
def pause(self, event):
self.paused = True
while self.paused:
time.sleep(0.000001)
def unpause(self, event):
self.paused = False
However, this crashes the program and doesn't un-pause.
What is going wrong and how can I fix it?

while creates a loop which makes the GUI loop unresponsive to anything--including KeyPress bindings. Calling just time.sleep(9999999) in the pause method would do the same thing. I'm not sure how the rest of your program is structured, but you should look into the after() method for an easy way to add start and stop features. Here's a simple example:
class App(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.text = Text(self)
self.text.pack()
self._unpause(None) # start the method
self.bind_all('<p>', self._pause)
self.bind_all('<u>', self._unpause)
def _unpause(self, event):
'''this method is the running portion. after its
called, it continues to call itself with the after
method until it is cancelled'''
self.text.insert(END, 'hello ')
self.loop = self.after(100, self._unpause, None)
def _pause(self, event):
'''if the loop is running, use the after_cancel
method to end it'''
if self.loop is not None:
self.after_cancel(self.loop)
root = Tk()
App(root).pack()
mainloop()

Related

Python Multithreading - not running simultaneously

im trying to learn python. But i have problems with the threading. First i failed in the "Proces" class because i putted the loop on the wrong place and my program newer returned from the other class.
But now i think all is correct and it still does not work. I need to have a GUI where i want to be able to write my conditions via text entries and i need another class "Proces" that will do stuff, checking status ower internet and so on constantly or in a specified interval...
The Problem is that my tkinter GUI is freezing after pressing something
here is my GUI.py file:
import tkinter as tk
from Proces import Proces
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
button = tk.Button(frame, text="QUIT", fg="red",command=quit).pack(side=tk.LEFT)
pr = Proces()
print("\nGUI: proces init...")
pr.start()
print("\nGUI: Start ended")
root.mainloop()
here is the Proces.py file:
import time, threading
class Proces(threading.Thread):
def loop(self):
while True:
time.sleep(2)
print("\nProces: looping")
def __init__(self):
threading.Thread.__init__(self)
print("\nProces: Starting proces")
time.sleep(2)
def run(self):
self.deamon = True
print("\nProces: Starting loop")
self.loop()
*This is the output: *
Proces: Starting proces
GUI: proces init...
Proces: Starting loop
GUI: Start ended
Proces: looping
Proces: looping
Proces: looping
Proces: looping
*But the GUI of the tkinter does not react.*
How should i do this kind of task?
Thank you for your help, advice and answer
I think you don't have problem about starting thread etc. However, you should be able to control your thread, meaning your thread should return based on some condition. It seems you have a button to quit. I assume you want to finish the process by clicking the button.
To do that, when you click to button, your main thread should pass a variable to Process in order to break your while loop, which is your thread basically.
Here is a code that you can work on.
import tkinter as tk
import time
import threading
class MainWindow:
def __init__(self, master):
self.master = master
self.quit_button = tk.Button(self.master, text="QUIT", command=lambda:self.quit(), width=20)
self.quit_button.pack(side=tk.LEFT)
self.process = None
print("\nGUI: process init...")
self.start_task()
def quit(self):
self.process.stop_process = True
print("\nGUI: Start ended")
def start_task(self):
self.process = Process()
self.process.start()
class Process(threading.Thread):
def loop(self):
while True:
if not self.stop_process:
time.sleep(2)
print("\nProcess: looping")
else:
print("\nProcess: looping ended")
return
def __init__(self):
threading.Thread.__init__(self)
self.stop_process = False
print("\nProcess: Starting proces")
time.sleep(2)
def run(self):
self.deamon = True
print("\nProcess: Starting loop")
self.loop()
if __name__ == '__main__':
root = tk.Tk()
app = MainWindow(master=root)
root.mainloop()
So you start yout tkinter, which is your main thread. Then iniating another class within your main thread, which is inherits the thread. So you have two seperate thread that working. When you clicked to "quit" button, you pass variable stop_process, which breaks the loop and return, meaning ending your thread. Your main thread is still alive for running your tkinter window.
I hope it will help

Trying to keep a function constantly running while tkinter button is held

I currently have a button in tkinter to run a function when the button is released. I need the button to constantly add toa number at a certain rate the entire time the button is being held.
global var
var=1
def start_add(event,var):
global running
running = True
var=var+1
print(var)
return var
def stop_add(event):
global running
print("Released")
running = False
button = Button(window, text ="Hold")
button.grid(row=5,column=0)
button.bind('<ButtonPress-1>',start_add)
button.bind('<ButtonRelease-1>',stop_add)
i dont necessarily need any function to run when the button is released, just while the button is being held if this helps. Any help is much appreciated.
There is nothing builtin that can do this, but it would be easy to make your own Button that can. You are on the right track too, only thing you are missing is that you need to use after to make the loop and after_cancel to stop the loop:
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
class PaulButton(tk.Button):
"""
a new kind of Button that calls the command repeatedly while the Button is held
:command: the function to run
:timeout: the number of milliseconds between :command: calls
if timeout is not supplied, this Button runs the function once on the DOWN click,
unlike a normal Button, which runs on release
"""
def __init__(self, master=None, **kwargs):
self.command = kwargs.pop('command', None)
self.timeout = kwargs.pop('timeout', None)
tk.Button.__init__(self, master, **kwargs)
self.bind('<ButtonPress-1>', self.start)
self.bind('<ButtonRelease-1>', self.stop)
self.timer = ''
def start(self, event=None):
if self.command is not None:
self.command()
if self.timeout is not None:
self.timer = self.after(self.timeout, self.start)
def stop(self, event=None):
self.after_cancel(self.timer)
#demo code:
var=0
def func():
global var
var=var+1
print(var)
root = tk.Tk()
btn = PaulButton(root, command=func, timeout=100, text="Click and hold to repeat!")
btn.pack(fill=tk.X)
btn = PaulButton(root, command=func, text="Click to run once!")
btn.pack(fill=tk.X)
btn = tk.Button(root, command=func, text="Normal Button.")
btn.pack(fill=tk.X)
root.mainloop()
As #rioV8 mentioned, the after() call is not extremely accurate. If you set the timeout to 100 milliseconds, you can usually expect anywhere from 100 - 103 milliseconds between calls. These errors will stack up the longer the button is held. If you are trying to time exactly how long the button has been held down you will need a different approach.
#Novel's answer should work I think, but here is something more along the lines of what you were trying, that doesn't require a whole new class:
from tkinter import *
INTERVAL=5 #miliseconds between runs
var=1
def run():
global running, var
if running:
var+=1
print(var)
window.after(INTERVAL, run)
def start_add(event):
global running
running = True
run()
def stop_add(event):
global running, var
print("Released")
running = False
window=Tk()
button = Button(window, text ="Hold")
button.grid(row=5,column=0)
button.bind('<ButtonPress-1>',start_add)
button.bind('<ButtonRelease-1>',stop_add)
mainloop()

Python Tkinter: Call function after long press spacebar for 1 second

I would like to create a function for my GUI using key-press events. My goal is to allow a function to be called if user presses the spacebar for more than 1 second, and abort the function if key releases within this 1 second.
How do I do this?
Feel free to edit my example:
from Tkinter import Tk, Frame
class Application(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.parent.geometry('%dx%d+%d+%d' % (800, 300, 0, 0))
self.parent.resizable(0, 0)
self.pack(expand = True)
self.parent.bind('<Control-s>', self.printer)
def printer(self, event = None):
print "Hello World"
def main():
root = Tk()
Application(root)
root.mainloop()
if __name__ == '__main__':
main()
Python 2.7, Linux
Reference: http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm
This is either going to be really easy, or really hard. Which it is depends on several factors. Conceptually, the solution is simple:
on the press of the space key, use after to schedule a job to run in the future
on release of the key, cancel the job.
Where it gets hard is that some systems, when you keep a key pressed, will continue to auto-repeat either the keypress (so you'll get a stream of presses in a row, without a release) or a pair of presses and releases (you'll get a steady stream of press/release events). This might be done at the keyboard hardware level, or it might be done by the OS.
I know that this is an old question, but I was able to implement a solution with a bit of trial-and-error, and thought I'd post it here in case it helps anybody else. (Note that I've only tested this with Python 3.6 and Windows, but I've got a similar solution working with long mouse button presses on Linux, so I'm assuming this transfers).
from tkinter import Tk, Frame
class Application(Frame):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.parent.geometry('%dx%d+%d+%d' % (800, 300, 0, 0))
self.parent.resizable(0, 0)
self.pack(expand = True)
self._short_press = None
self.parent.bind('<KeyPress-space>', self.on_press_space)
self.parent.bind('<KeyRelease-space>', self.on_release_space)
# set timer for long press
def on_press_space(self, event):
if self._short_press is None: # only set timer if a key press is not ongoing
self._short_press = True
self._do_space_longpress = self.after(1000, self.do_space_longpress)
# if it was a short press, cancel event. If it was a long press, event already happened
def on_release_space(self, event):
if self._short_press:
self.cancel_do_space_longpress()
# do long press action
def do_space_longpress(self):
self.cancel_do_space_longpress() # cancel any outstanding timers, if they exist
print('long press')
# cancels long press events
def cancel_do_space_longpress(self):
self._short_press = None
if self._do_space_longpress:
self.parent.after_cancel(self._do_space_longpress)
def main():
root = Tk()
Application(root)
root.mainloop()
if __name__ == '__main__':
main()

Getting data from a long function to update a GUI

I've got a GUI which gives the user the option to run a selection of tests.
These tests run in threads as they use MIDI data in.
I have a checkQueue() function that is run using after() but once the user selects a test, the checkQueue() function is no longer called until the function finishes.
How do I get the checkQueue to continue running during my buttonTest() function so I can use data from the test to update the GUI?
Here is a simplified version of my code:
import Tkinter as tk
import Queue
class Program(tk.Frame):
def __init__(self,parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.initUI()
self.q = Queue.Queue()
self.after(200, checkQueue, self.q, self)
def initUI(self):
start = tk.Button(self.parent, command=self.runTest, text="Run Test 1")
start.pack()
self.instruction = tk.Label(self.parent, text="Press Button 1")
self.instruction.pack()
def runTest(self):
buttonTest(self)
def checkQueue(q,app):
print "Calling checkQueue"
while not q.empty():
#HandleData (update a label/canvas etc.)
app.update()
app.after(200, checkQueue,q,app)
def buttonTest(gui):
#Does lots of functions but is just a while for this example
x=1
while x==1:
if x == 100:
gui.q.put("Some Data")
def main():
root = tk.Tk()
root.configure(background="black")
app = Program(root)
root.mainloop()
root.destroy()
if __name__ == "__main__":
main()
I'm assuming buttonTest is the function that's calling your tests that are running in other threads. Even though the actual work is done in child threads, buttonTest is still running in the main thread, and its while loop is hogging all the main thread's processing cycles. Try starting buttonTest itself in its own thread. This will give the main thread the breathing room necessary to process your checkQueue calls.
def runTest(self):
Thread(target=buttonTest, args=(self,)).start()

wxPython: Stuck in .MainLoop()

I am not an experienced programmer. This is probably a simple problem to solve.
I have a function that is supposed to run every two minutes. This function is inside a simple wxPython system tray program. The problem is that I do not know how to run the function because wxPython never leave .MainLoop(). Where should I put the function?
Here is the code: (I have left out the function and import because it is not relevant.)
TRAY_TOOLTIP = 'System Tray Demo'
TRAY_ICON = 'star.png'
def create_menu_item(menu, label, func):
item = wx.MenuItem(menu, -1, label)
menu.Bind(wx.EVT_MENU, func, id=item.GetId())
menu.AppendItem(item)
return item
class TaskBarIcon(wx.TaskBarIcon):
def __init__(self):
super(TaskBarIcon, self).__init__()
self.set_icon(TRAY_ICON)
self.Bind(wx.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)
def CreatePopupMenu(self):
menu = wx.Menu()
create_menu_item(menu, 'Say Hello', self.on_hello)
menu.AppendSeparator()
create_menu_item(menu, 'Exit', self.on_exit)
return menu
def set_icon(self, path):
icon = wx.IconFromBitmap(wx.Bitmap(path))
self.SetIcon(icon, TRAY_TOOLTIP)
def on_left_down(self, event):
print 'Tray icon was left-clicked.'
MailCheck()
def on_hello(self, event):
print 'Hello, world!'
def on_exit(self, event):
wx.CallAfter(self.Destroy)
def main():
app = wx.PySimpleApp()
TaskBarIcon()
app.MainLoop()
#This is my function I want to run
#But MainLoop() never ends. Where should I put MainCheck() ?
MailCheck()
if __name__=='__main__':
main()
wxPython, as with most GUI frameworks, uses an event-driven programming model. That means that bits of your program are run in response to actions that may have originated from the user, (such as a key press, a menu selection, etc.) the system or perhaps from some other program. The rest of the time it sits in MainLoop waiting for one of those things to happen.
For situations like yours, there is the wx.Timer class which can trigger an event once or perhaps periodically after N milliseconds. If you bind an event handler for the timer event, then that handler will be called when the timer expires.
I've never used wxPython but you could use the threading-module of Python's standard library.
A minimal example:
import threading
def work():
threading.Timer(0.25, work).start()
print "stackoverflow"
work()
Look at this thread (example is from there): Periodically execute function in thread in real time, every N seconds

Categories

Resources