How to call back methods in python? - python

I am making a project in which whenever I stop the stopwatch, it updates the time into a Listbox. It can be updated in the self.m listbox. However it cannot be updated into topscoreslistbox.
In the printFunctionStop function, i call back the Stop method in a class. However i do not know how to call the TopscoresStop in the main function.
The Stop method and TopscoresStop method do the same thing. However the different thing is one is in the class and one is in the main function.
So how do i call the method TopscoresStop in the main function in printFunctionStop anyone knows?
class StopWatch(Frame):
def Stop(self):
""" Stop the stopwatch, ignore if stopped. """
tempo = self._elapsedtime - self.lapmod2
if self._running:
self.after_cancel(self._timer)
self._elapsedtime = time.time() - self._start
self._setTime(self._elapsedtime)
self._running = 0
if len(self.laps) == 3:
return
self.laps.append(self._setLapTime(tempo))
self.m.insert(END, (str(self.one.get()) , "%.2f" % self._elapsedtime))
self.m.yview_moveto(1)
def main():
def Topscores():
toplevel() = Toplevel()
toplevel.title("Top Scores")
topscoreslistbox = Listbox(toplevel, selectmode=EXTENDED, height=3, width=20, font=("Helvetica", 26))
topscoreslistbox.pack(side=RIGHT, fill=BOTH, expand=1, pady=5, padx=2)
first = Label(toplevel, text=("1st"), font=("Algerian", 30))
first.pack(side=TOP)
second = Label(toplevel, text=("2nd"), font=("Algerian", 30))
second.pack(side=TOP)
third = Label(toplevel, text=("3rd"), font=("Algerian", 30))
third.pack(side=TOP)
def TopscoresStop():
tempo = sw._elapsedtime - sw.lapmod2
if sw._running:
sw.after_cancel(sw._timer)
sw._elapsedtime = time.time() - sw._start
sw._setTime(sw._elapsedtime)
sw._running = 0
if len(sw.laps) == 3:
return
sw.laps.append(sw._setLapTime(tempo))
topscoreslistbox.insert(END, (str(sw.one.get()) , "%.2f" % sw._elapsedtime))
topscoreslistbox.yview_moveto(1)
def printFunctionStop(channel):
sw.event_generate("<<Stop>>", when = "tail")
GPIO.add_event_detect(16, GPIO.FALLING, callback = printFunctionStop, bouncetime=300)
sw.bind("<<Stop>>", lambda event:sw.Stop())

Have you tried passing the function as an argument to printfunctionstop?
sorry, can't make comments yet.
E.g.
def a():
print('hi')
def b(func):
func()
def main():
b(a)
main()
The main should print 'hi'

Related

I'm using threading, so why get my gui all time frezzed O.o

Oke i got the problem days ago and someone helped me with treading but my code was really ugly (im totaly new to coding) now i try to make it better and on an smarter way but now my gui get a frezze all time.
i tryed to do it on the way like my last code but it dosent work this time.
What have i to do this time i cant understand it but want understand it.
some Helpful Tips and tricks ?
Or better ways to do it smart, faster, and more powerfull, or mybae the gui more Beautyfule ?
import time
import sys
from tkinter import *
import threading
root = Tk()
root.geometry("600x400")
global start
start = 1
def startUp():
user_input()
thr = threading.Thread(target=user_input)
thr.start()
def user_input():
global nameInput
global start
nameInput = textBox.get("1.0","end-1c")
start = 0
if start < 1:
while True:
apex = ApexLegends("APIKey")
player = apex.player(nameInput)
print("Gesamt Kills: " + player.kills + "\n" + 'Gesamt Damage: ' + player.damage)
time.sleep(3)
else:
print("stop")
anleitung=Label(text="Gib einen Spielernamen ein und drücke Start")
anleitung.pack()
textBox=Text(root, height=1, width=30)
textBox.pack()
startButton=Button(root, height=1, width=10, text="Start", command=lambda:startUp())
startButton.pack()
Tkinter isn't designed to be accessed by more than one thread. Here is an excellent answer by one of the guys who has a very deep understainding of how tcl & tk works (the libraries that tkinter depends on), explaining why this is so.
Callback to python function from Tkinter Tcl crashes in windows
This is the first of the two paragraphs in that answer:
Each Tcl interpreter object (i.e., the context that knows how to run a Tcl procedure) can only be safely used from the OS thread that creates it. This is because Tcl doesn't use a global interpreter lock like Python, and instead makes extensive use of thread-specific data to reduce the number of locks required internally. (Well-written Tcl code can take advantage of this to scale up very large on suitable hardware.)
def startUp():
user_input()
thr = threading.Thread(target=user_input)
thr.start()
This doesn't look right. You're calling user_input() in both the main thread and the child thread. If you only want it to run in the child thread, don't call it that first time.
def startUp():
thr = threading.Thread(target=user_input)
thr.start()
Hi #Trason I've played with your code and I suggest an oo approach.
In the code below I've try to adapt a functional script to your code.
First of all I've use a variable as
self.nameInput = tk.IntVar()
to store the user input on
tk.Entry(w, bg='white', textvariable=self.nameInput).pack()
I've use an Entry widget instead of Text but it should be the same.
Furthermore I use a class to manage thread start and stop operation.
Look, I changed your 'start' variable with 'check' because start is a reserved word
in python thread module.
I tried to recreate the functionality of your code.
Try to import and use your ApexLegends.
import tkinter as tk
import threading
import queue
import datetime
import time
class MyThread(threading.Thread):
def __init__(self, queue,nameInput):
threading.Thread.__init__(self)
self.queue = queue
self.nameInput = nameInput
self.check = True
def stop(self):
self.check = False
def run(self):
while self.check:
# apex = ApexLegends("APIKey")
#player = apex.player(self.nameInput.get())
x = "Gesamt Kills: " + "player.kills" + "\n" + 'Gesamt Damage: ' + "player.damage"+ "\n"
s = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
msg = "{} datetime: {} nameInput {}".format(x,s,self.nameInput.get())
time.sleep(3)
self.queue.put(msg)
class App(tk.Frame):
def __init__(self,):
super().__init__()
self.master.title("Hello World")
self.master.protocol("WM_DELETE_WINDOW",self.on_close)
self.queue = queue.Queue()
self.my_thread = None
self.nameInput = tk.IntVar()
self.init_ui()
def init_ui(self):
self.f = tk.Frame()
w = tk.Frame()
tk.Label(w, text = "Gib einen Spielernamen ein und drücke Start").pack()
tk.Entry(w, bg='white', textvariable=self.nameInput).pack()
w.pack(side=tk.LEFT, fill=tk.BOTH, expand=0)
w = tk.Frame()
tk.Button(w, text="Start", command=self.startUp).pack()
tk.Button(w, text="Stop", command=self.stop_thread).pack()
tk.Button(w, text="Close", command=self.on_close).pack()
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=0)
self.f.pack(side=tk.LEFT, fill=tk.BOTH, expand=0)
def startUp(self):
if (threading.active_count()!=0):
self.my_thread = MyThread(self.queue,self.nameInput)
self.my_thread.start()
self.periodiccall()
def stop_thread(self):
if(threading.active_count()!=1):
self.my_thread.stop()
def periodiccall(self):
self.checkqueue()
if self.my_thread.is_alive():
self.after(1, self.periodiccall)
else:
pass
def checkqueue(self):
while self.queue.qsize():
try:
ret = self.queue.get(0)
msg = "%s"%(ret)
print(msg)
except queue.Empty:
pass
def on_close(self):
if(threading.active_count()!=0):
if self.my_thread is not None:
self.my_thread.stop()
self.master.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()

Pause and continue stopwatch

I am trying to create stopwatch. I have done it but I would like to pause and continue the time whenever I want. I have tried some things but I have no idea how to do it. Is there anybody who would explain me how to do it?
import time, tkinter
canvas=tkinter.Canvas(width=1900,height=1000,bg='white')
canvas.pack()
canvas.create_text(950,300,text=':',font='Arial 600')
def write(x_rec,y_rec,x_text,rep):
canvas.create_rectangle(x_rec,0,y_rec,750,outline='white',fill='white')
if rep<10:
canvas.create_text(x_text,400,text='0'+str(rep),font='Arial 600')
else:
canvas.create_text(x_text,400,text=str(rep),font='Arial 600')
def write_minutes(rep):
write(0,900,450,rep)
def write_seconds(rep):
write(1000,1900,1450,rep)
def time(num,remember):
while remember[0]<num:
remember[1]+=1
write_seconds(remember[1])
if remember[1]==60:
remember[0]+=1
remember[1]=0
write_seconds(remember[1])
write_minutes(remember[0])
canvas.update()
canvas.after(1000)
remember=[0,0]
num=1
write_seconds(remember[1])
write_minutes(remember[0])
time(5,remember)
I couldn't figure-out a clean way to modify your code to do what you want, so decided to implement the stop watch as a class to make the program more object-oriented and avoid the use of a bunch of global variables.
I haven't tested this thoroughly, but there's enough of it working to give you the idea. Note also that I changed a Resume button into one that toggles itself between that and being Pause button. This approach made adding a third one unnecessary.
Update
I noticed what could be potential problem because more and more objects keep getting added to the Canvas as the display is updated. This shouldn't be a problem for a short-running StopWatch instance, but might cause issues with a long-running one.
To avoid this, I modified the code to update the existing corresponding Canvas text object if there is one. I also moved the Buttons to the top, above the StopWatch.
from functools import partial
import time
import tkinter as tk
PAUSE, RESUME = 0, 1 # Button states.
# Button callback functions.
def _toggle(callback):
toggle_btn.state = 1 - toggle_btn.state # Toggle button state value.
toggle_btn.config(**toggle_btn_states[toggle_btn.state])
callback()
def _stop():
stopwatch.cancel_updates()
toggle_btn.config(state=tk.DISABLED)
stop_btn.config(state=tk.DISABLED)
class StopWatch:
def __init__(self, parent, run_time, width, height):
self.run_time = run_time
self.width, self.height = width, height
self.font = 'Arial 600'
self.canvas = tk.Canvas(parent, width=width, height=height, bg='white')
self.canvas.pack()
self.canvas.create_text(950, 300, text=':', font=self.font)
self.running, self.paused = False, False
self.after_id = None
def start(self):
self.elapsed_time = 0 # In seconds.
self._display_time()
self.after_id = self.canvas.after(1000, self._update)
self.running, self.paused = True, False
def _update(self):
if self.running and not self.paused:
if self.elapsed_time == self.run_time:
_stop() # Sets self.running to False.
self.canvas.bell() # Beep.
else:
self.elapsed_time += 1
self._display_time()
if self.running: # Keep update process going.
self.after_id = self.canvas.after(1000, self._update)
def _display_time(self):
mins, secs = divmod(self.elapsed_time, 60)
self._write_seconds(secs)
self._write_minutes(mins)
def _write_minutes(self, mins):
self._write(0, 900, 450, 'mins', mins)
def _write_seconds(self, secs):
self._write(1000, 1900, 1450, 'secs', secs)
def _write(self, x_rec, y_rec, x_text, tag, value):
text = '%02d' % value
# Update canvas text widget if it has non-empty text.
if self.canvas.itemcget(tag, 'text'):
self.canvas.itemconfigure(tag, text=text)
else: # Otherwise create it.
self.canvas.create_text(x_text, 400, text=text, tag=tag, font=self.font)
def pause_updates(self):
if self.running:
self.paused = True
def resume_updates(self):
if self.paused:
self.paused = False
def cancel_updates(self):
self.running, self.paused = False, False
if self.after_id:
self.canvas.after_cancel(self.after_id)
self.after_id = None
# main
root = tk.Tk()
# Create a Frame for Buttons (allows row of them to be centered).
button_frame = tk.Frame(root)
button_frame.pack(side=tk.TOP)
# Create StopWatch and configure buttons to use it.
stopwatch = StopWatch(root, 5, 1900, 1000)
toggle_btn = tk.Button(button_frame)
toggle_btn_states = {}
# Dictionary mapping state to button configuration.
toggle_btn_states.update({
PAUSE: dict(
text='Pause', bg='red', fg='white',
command=partial(_toggle, stopwatch.pause_updates)),
RESUME: dict(
text='Resume', bg='green', fg='white',
command=partial(_toggle, stopwatch.resume_updates))
})
toggle_btn.state = PAUSE
toggle_btn.config(**toggle_btn_states[toggle_btn.state])
toggle_btn.pack(side=tk.LEFT, padx=2)
stop_btn = tk.Button(button_frame, text='Stop', bg='blue', fg='white', command=_stop)
stop_btn.pack(side=tk.LEFT, padx=2)
stopwatch.start()
root.mainloop()
Here's a screenshot showing the stopwatch running:

How to manage Python Tkinter app multithreading

I am developing a Tkinter app with Python. I have a two background operations and one operation with user demand. Here is my sample code:
from threading import Thread
import tkinter as tk
import time
class Controller(object):
def __init__(self, master):
self.master = master
self.btn1 = tk.Button(self.master, text="Start Recording", width=16, height=5, command=lambda: self.start_background_opt())
self.btn1.grid(row=2, column=0)
self.btn3 = tk.Button(self.master, text="Fly", width=16, height=5, command=lambda: self.fly_button())
self.btn3.grid(row=3, column=0)
self.entry = tk.Entry(self.master)
self.entry.grid(row=4, column=0)
self.connect_button_clicked = False
self.thread1 = None
self.thread2 = None
self.thread3 = None
self.flight_completed = False
def background_opt1(self):
while True:
if self.connect_button_clicked:
print("Battery Fetching")
else:
return
def background_opt2(self):
while True:
if self.connect_button_clicked:
print("Signal Fetching")
else:
return
def start_background_opt(self):
if not self.connect_button_clicked:
self.connect_button_clicked = True
self.thread1 = Thread(target=self.background_opt1).start()
self.thread2 = Thread(target=self.background_opt2).start()
else:
self.connect_button_clicked = False
self.thread1 = None
self.thread2 = None
def flight_operation_controller(self):
if self.flight_completed:
self.thread3 = None
def fly_button(self):
self.flight_completed = False
self.thread3 = Thread(target=self.static_sim()).start()
def static_sim(self):
while True:
if not self.flight_completed:
for _ in range(100):
print('Simulating')
time.sleep(0.1)
print("Simulation completed")
self.flight_completed = True
else:
return
if __name__ == '__main__':
root = tk.Tk()
# Set the window size
root.geometry("900x600+0+0")
control = Controller(root)
root.mainloop()
So when user click to "start recording", it starts 2 background operations. They should run as a background. Then when user click to "fly" button, fly operation should be executed.
In order to not blocking my main UI, I have put them in seperate threads.
Actually my all operations are working properly. I have put time.sleep
for replicating my fly operation; but when it runs, it blocks my entire, even though it is running in seperate thread.
Could you please tell me why I am seeing this?
Is my interpretation okey regarding the multithreading in Pyhton tkinter?
Best Regards
Take a look at this line of code:
self.thread3 = Thread(target=self.static_sim()).start()
The above code works exactly the same way as this code;
result = self.static_sim()
self.thread3 = Thread(target=result).start()
See the problem? You are calling your function outside of the thread. Because static_sim() has an infinite loop, it never returns.
When you set the target for Thread, it must be a callable. Change the code to this (note the lack of trailing ()):
self.thread3 = Thread(target=self.static_sim).start()

Tkinter timer to start at 0 on button click

I would like to create a timer that starts at 0 when a user presses a button and stop at whatever time it has displayed when the user presses the button again. So far, all of the questions user after that looks at the current time and updates in seconds from whatever time it is like so:
def timer(self):
now = time.strftime("%H:%M:%S")
self.label.configure(text=now)
self.after(1000, self.timer)
But I would like to start at zero, display the minutes and seconds. Is there anyway to achieve this?
Here's a simple stopwatch GUI. There's some room for improvement. ;)
import tkinter as tk
from time import time
class Stopwatch:
def __init__(self):
root = tk.Tk()
root.title('Stopwatch')
self.display = tk.Label(root, text='00:00', width=20)
self.display.pack()
self.button = tk.Button(root, text='Start', command=self.toggle)
self.button.pack()
self.paused = True
root.mainloop()
def toggle(self):
if self.paused:
self.paused = False
self.button.config(text='Stop')
self.oldtime = time()
self.run_timer()
else:
self.paused = True
self.oldtime = time()
self.button.config(text='Start')
def run_timer(self):
if self.paused:
return
delta = int(time() - self.oldtime)
timestr = '{:02}:{:02}'.format(*divmod(delta, 60))
self.display.config(text=timestr)
self.display.after(1000, self.run_timer)
Stopwatch()
The toggle method toggles the stopwatch on or off. The run_timer method updates the display Label with the time since the timer started, in minutes & seconds. For more accuracy, reduce the .after delay to say, 500, or 100. That will do unnecessary (and invisible) updates to the Label, but the displayed time will be a little more accurate, and the GUI will feel a little more responsive.
import tkinter as tk
import time
class GUI:
def __init__(self, master):
self.root = master
self.parent = tk.Frame(self.root)
self.parent.pack(fill = tk.BOTH)
self.parent.config(bg = "black")
self.now = time.time()
self.buttonVar = tk.IntVar()
self.buttonCycle = False
self.buttonVar.set(0)
self.button = tk.Button(root,
textvariable = self.buttonVar,
command = self.updateButton)
self.button.pack(fill = tk.BOTH)
self.button_cycle()
def updateButton(self):
if self.buttonCycle:
self.buttonCycle = False
self.now = time.time()
elif not self.buttonCycle:
self.buttonCycle = True
def button_cycle(self):
if self.buttonCycle:
now = time.time()
timeDifference = int(now - self.now)
self.buttonVar.set(timeDifference)
self.root.after(1000, self.button_cycle)
root = tk.Tk()
myApp = GUI(root)
root.mainloop()

Launch command in Tkinter based on selected radio button?

I would like to change the function and text of a button based on which radio-button is selected. Right now what's happening is that both commands are run at the same time as soon as the application is launched rather than it being based upon which radio-button is selected.
import tkinter as tk
import time
## Time variables for the Pomodoro
pomo = 60 * 25 ## 60 seconds times number of minutes
btime = 60 * 5 ## 60
class ExampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.label = tk.Label(self, text="25:00", width=10, font="Helvetica 20")
self.label.pack()
self.remaining = 0
self.button = tk.Button(self)
self.button.pack()
pomocommand = self.button.configure(text="Pomodoro", state=tk.NORMAL, command= lambda: self.pomodoro(pomo)) #Switch back to the pomodoro timer
breakcommand = self.button.configure(text="Break", state=tk.NORMAL, command= lambda: self.breaktime(btime)) #Switch to the break timer
countercommand = self.button.configure(text="Counter", state=tk.NORMAL, command= print('cheese'))
self.radvar = tk.IntVar()
self.radvar.set('1')
self.radio = tk.Radiobutton(self, text="Pomodoro", variable = self.radvar, value=1, indicatoron=0, command = pomocommand)
self.radio.pack(anchor=tk.W)
self.radio = tk.Radiobutton(self, text="Counter", variable = self.radvar, value=2, indicatoron=0, command = countercommand)
self.radio.pack(side=tk.LEFT)
def pomodoro(self, remaining = None):
self.button.configure(state=tk.DISABLED)
if remaining is not None:
self.remaining = remaining
if self.remaining <= 0:
self.label.configure(text="Time's up!")
breakcommand
else:
self.label.configure(text= time.strftime('%M:%S', time.gmtime(self.remaining))) #Integer to 'Minutes' and 'Seconds'
self.remaining = self.remaining - 1
self.after(1000, self.pomodoro)
def breaktime(self, remaining = None):
self.button.configure(state=tk.DISABLED)
if remaining is not None:
self.remaining = remaining
if self.remaining <= 0:
self.label.configure(text="Time's up!")
pomocommand
else:
self.label.configure(text= time.strftime('%M:%S', time.gmtime(self.remaining))) #Integer to 'Minutes' and 'Seconds'
self.remaining = self.remaining - 1
self.after(1000, self.breaktime)
if __name__ == "__main__":
app = ExampleApp()
app.mainloop()
What you are doing is calling the function self.button.configure(...) instead of passing the function itself. Here is a small example:
def test(a):
return a+4
callback_1 = test(2) # callback_1 will be (2+4) = 6, because you called the function. Notice the parens ()
callback_2 = test # callback_2 will be the function test, you did not call it
# So, you can do:
callback_2(some_value) # will return (some_value + 4), here you called it
Basically what is happening is that you are using the first example, so the function is called in __init__, and what it is supposed to do is done there. What you want is something similar to the second, because Tkinter wants something to call (a function or any callable). So pomocommand and break should be functions not the result of calling a function. (You can try to do print pomocommand and see that it is not a function.)
The solution is either to create a new function, or use lambdas. Here:
def pomocommand(self):
self.button.configure(text="Pomodoro", state=tk.NORMAL, command= lambda: self.pomodoro(pomo)) #Switch back to the pomodoro timer
# and in your __init__ method:
def __init__(self):
# ...
self.radio = tk.Radiobutton(self, text="Pomodoro", variable = self.radvar, value=1, indicatoron=0, command = self.pomocommand)
# ...
And you do the same for the other button. Using a lambda here is not recommended because it is a long statement (the self.button.configure part) so your code will not be readable. But I see you are using lambdas, so you might get the idea.
As a side note, be careful when using lambda like you do. Here pomo is a global variable, but if it is not, you might get into trouble. See this question. (Right now it is alright, so you can just ignore this :D).

Categories

Resources