So here´s the deal. I have a button. That button triggers a while loop and opens a new window with a "cancel" button. Through that cancel-button I want to terminate the while loop.
NEW APPROACH:
def process():
parent,child = Pipe()
p = process(target=new_window(),args=(child))
p.start()
parent.send("True")
while true:
if child.recv()!="False":
do something
else:
p.join()
def new_window(p):
id = "cancel"
window = Toplevel(root)
window.geometry("240x160")
state = p.recv()
def closewindow():
global state
state = "False"
p.send(state)
window.destroy()
label = Label(window, text="Überwache...").place(x=90,y=60)
buttonc = Button(window,text="Abbrechen!",fg="black",command=closewindow).place(relx=0.35, rely=0.5)
The idea is that the parent (def process) executes the while loop as long as it doesn´t get a "False" from the child process (new_window).
Do you see any errors? Can´t test it right now, since the code replacing my placeholder "do something" is another difficult task...
OLD POST:
state variable
state = bool(True)
button that triggers loop and new window
button3 = Button(containerd,text="Starte Überwachung",fg="black",height=10,width=16,command=sound)
function new window
def new_window():
id = "cancel"
window = Toplevel(root)
window.geometry("240x160")
def closewindow():
global state
state = bool(False)
window.destroy()
label = Label(window, text="Überwache...").place(x=90,y=60)
buttonc = Button(window,text="Abbrechen!",fg="black",command=closewindow).place(relx=0.35, rely=0.5)
function while loop
def sound():
global inp
global state
new_window()
while state:
l,data = inp.read()
if l > 60:
subprocess.call(['/home/pi/RPi_Cam_Web_Interface/cycle.sh'],shell=True)
else:
continue
The problem is the new window never opens. The while loop keeps the button / programm "occupied". Any ideas how to solve this?
Related
I want to break a loop with the same button that I started the loop with.
For all intents and purposes, it does what I need it to, up until I click the button again. If I hit the button again, it crashes. Right now with the debug visual setup, I can hit q to break the loop. But down the road I obviously want to turn that off and just have it all go through the button instead.
I should also note that the button does not update unless the window is interacted with/moved. This goes for when the button is pushed. It doesn't crash until you move the window.
def bttnup():
global is_on
#starts autoaccept
if is_on:
button.config(image = selected,
bg=selected_bg
)
start()
is_on = False
else:
#Stops autoaccept
button.config(image = unselected,
bg=unselected_bg,
)
is_on = True
#Object detection and keystroke
def start():
while(True):
screenshot = wincap.get_screenshot()
points = vision_accept.find(screenshot, .325, 'rectangles')
if cv.waitKey(1) == ord('q'):
cv.destroyAllWindows()
break
#Button
button = Button(window,
image=unselected,
command=bttnup)
button.pack()
I have also tried:
def bttnup():
#starts autoaccept
if button['bg'] == 'unselected_bg':
threading.join(1)
button.config(image=selected,
bg=selected_bg
)
start()
return
#Stops autoaccept
if button['bg'] == 'selected_bg':
threading.start()
button.config(image = unselected,
bg=unselected_bg,
)
return
#start object detection
def start():
i = 1
while button['bg'] == 'selected_bg':
i = i+1
# get an updated image of the game
screenshot = wincap.get_screenshot()
# screenshot = cv.resize(screenshot, (800, 600))
points = vision_accept.find(screenshot, .325, 'rectangles')
sleep(5)
#Button!
button = Button(window,
image=unselected,
command=lambda: bttnup())
So I went and changed a majority of the code to use .after instead of using a while statement. Its not as clean looking, but it does what it needs to.
created a function for on, and one for off and have the button cycle when the button is fixed. when the on button is pushed it calls to the start function which then cycles every second.
# initialize the WindowCapture class
wincap = WindowCapture()
# Initialize the Vision class
vision_accept = Vision('accept.jpg')
#window parameters
window = Tk()
window.resizable(False, False)
window.title('AutoAccept')
unselected = PhotoImage(file='Button_unselected.png')
selected = PhotoImage(file='matching.png')
unselected_bg='white'
selected_bg='black'
is_on = True
def start_on():
global is_on
#starts autoaccept
button.config(image = selected,
bg=selected_bg,
command = start_off
)
start()
is_on = True
def start_off():
global is_on
button.config(image = unselected,
bg=unselected_bg,
command = start_on
)
is_on = False
#start object detection
def start():
if is_on:
# get an updated image of the game
screenshot = wincap.get_screenshot()
points = vision_accept.find(screenshot, .32, 'rectangles')
window.after(500, start)
button = Button(window,
image=unselected,
command=start_on
)
button.pack()
window.mainloop()
In this UI there is 2 buttons and 1 entry box. Buttons named "Start Loop" and "Print".
When i write a text into the entry box, i should able to see it's print out when pressed the related button. What i am trying to do is, put that button press in a while loop. But the interesting part is trying to print a new entry in every 10 seconds.
When you entered a entry and press the "Start Loop" button, it becomes run. Meanwhile user interface window will froze. Can't write a new entry or press "Print" button. Even i use time.sleep function it still frozen. It prints old entry, but i want to write a new entry at every iteration. Check out the code:
import time
from tkinter import *
class tkin(Frame):
def __init__(self,parent):
Frame.__init__(self, parent)
self.parent = parent
self.UI()
def UI(self):
self.down = StringVar()
self.down_entry = Entry(self, textvariable=self.down)
self.down_entry.grid(row=2, column=0)
self.start_loop_buuton = Button(text="Start Loop", command=self.loop_func)
self.start_loop_buuton.place(x=10,y=40)
self.print_func_button = Button(text="Print ", command=self.pprint)
self.print_func_button.place(x=120,y=40)
self.pack()
def loop_func(self):
start = time.time()
while True:
print("standart print out")
end = time.time()
if (end- start) >10:
time.sleep(10)
self.print_func_button.invoke() ## press print_func_button
start = time.time()
def pprint(self):
print("WHICH PRINT LINE I WANT TO PRINT IN LIKE EVERY 10 SECONDS")
print(self.down.get())
def main():
root = Tk()
tkin(root)
root.geometry("195x100+300+300")
root.mainloop()
main()
Any advice would be nice. Thanks in advance.
this is how I would redefine Your method:
def loop_func(self):
self.print_func_button.invoke() # press print_func_button
self.after(3000, self.loop_func)
time is in miliseconds so this will be 3 seconds
In this example, i just separated the looping "loop_func()" using thread in Button function's argument of command. Like that:
import threading
...
...
...
self.start_loop_buuton = Button(text="Start Loop", command=threading.Thread(target=self.loop_func).start())
So, these two loops have been separated.
I have a function that refreshes a string. It has 2 buttons, on and off. If you push on it will print 'Test' multiple times per second.
def blink():
def run():
while (switch):
print('Test')
thread = threading.Thread(target=run)
thread.start()
def on():
global switch
switch = True
blink()
def off():
global switch
switch = False
I also have a toggle function that's is one button that toggles 'True' and 'False'. It displays 'Test' when True.
def toggle():
if button1.config('text')[-1] == 'False':
button1.config(text='True')
Label(root, text='Test').place(x=30, y=0, relheight=1, relwidth=0.7)
else:
button1.config(text='False')
Label(root, text='').place(x=30, y=0, relheight=1, relwidth=0.7)
How do I combine these 2? What I want is that instead of having an on/off button, I have one toggle-able button.
I tried making a class:
class Toggle:
def blink():
def run():
while (switch):
print('Test')
Label(root, text='Test').place(x=30, y=0, relheight=1, relwidth=0.7)
else:
Label(root, text='').place(x=30, y=0, relheight=1, relwidth=0.7)
thread = threading.Thread(target=run)
thread.start()
def toggle():
if button1.config('text')[-1] == 'False':
button1.config(text='True')
global switch
switch = True
blink()
else:
button1.config(text='False')
global switch
switch = False
But I get an error:
File "C:\Users\Daniel\PycharmProjects\stockalarm test\main.py", line 29
global switch
^
SyntaxError: name 'switch' is assigned to before global declaration
I tried looking into it but I cant figure out what to do.
As mentioned in the comments, if you use a class then switch should be an attribute of the class instead of being a global variable.
Additionally, what you did is more CLI oriented and what I suggest below is to use a more tkinter oriented approach.
You want a "toggle-able" button, which is, I think, like a tk.Checkbutton with the indicatoron option set to False.
Instead of using the switch global variable, you can use a tk.BooleanVar connected to the state of the button1 checkbutton.
This depends on what you actually want to do in the run() function but in your example using threading.Thread is an overkill. You can use tkinter .after(<delay in ms>, <callback>) method instead.
I have made the Toggle class inherit from tk.Frame to put both the label and toggle button inside. Here is the code:
import tkinter as tk
class Toggle(tk.Frame):
def __init__(self, master=None, **kw):
tk.Frame.__init__(self, master, **kw)
self.label = tk.Label(self)
self.switch = tk.BooleanVar(self)
self.button1 = tk.Checkbutton(self, text='False', command=self.toggle,
variable=self.switch, indicatoron=False)
self.label.pack()
self.button1.pack()
def blink(self):
if self.switch.get(): # switch is on
print('Test')
self.after(10, self.blink) # re-execute self.blink() in 10 ms
def toggle(self):
if self.switch.get(): # switch on
self.button1.configure(text='True') # set button text to True
self.label.configure(text='Test') # set label text to Test
self.blink() # start blink function
else: # switch off
self.button1.configure(text='False')
self.label.configure(text='')
root = tk.Tk()
Toggle(root).pack()
root.mainloop()
I am Trying to make a tkinter based Cube Timer Program for which I have done a console based program before I wanted to improve on it by making a GUI for it... I have made some tkinter projects before and I have a decent knowledge of how stuff works the thing is I want the Label in the tkinter window to update in every second while listening for a spacebar key on press I couldn't figure out a way to do it can someone help me?
def on_press(key):
global chk,start,end,timer_label
print('{0} pressed'.format(key))
if key == Key.space:
if chk == True:
end = time.time()
chk = False
messagebox.showinfo('Your Time',"Your Time =>"+str(round(end-start,3)))
def on_release(key):
global chk,start,end,timer_label
if key == Key.space:
if chk == False:
start = time.time()
chk = True
with Listener(on_press=on_press,on_release=on_release) as listener:
main = tk.Tk()
main.title("Cube Timer Mark 4 Prototype")
main.resizable(0,0)
tk.Label(main, text="Cube Time Mk3 Prototype", font=['Consolas',16]).grid(row = 0,column=0)
textbox = tk.Text(main,font=['Consolas',20], height = 1,width = 27)
textbox.grid(row = 1,column=0)
textbox.insert('1.0',scrambler())
timer_frame = tk.Frame(main).grid(row=2,column=0)
global timer_label
timer_label = tk.StringVar()
timer_label.set("0.0")
tk.Label(timer_frame,text = timer_label.get()+"s",font=['Consolas',20,'bold'],pady = 25).grid(row = 2,column = 0)
main.mainloop()
This is what I have tried but didn't work
You need main.after to periodically run a function like updating the label. To listen to keyboard event, you need main.bind. See example below.
Enhancement: use tk.Label(..., textvariable=timer_label) means the label will automatically update when you set timer_label
import tkinter as tk
def key_press(event):
if event.keysym == 'space':
print('pressed')
def key_release(event):
if event.keysym == 'space':
print('released')
def update_label():
# get the time from the string
time = float(timer_label.get()[:-1])
# increment the time and put it back in timer_label
timer_label.set(str(time+1) + 's')
# calling this function again 1000ms later, which will call this again 1000ms later, ...
main.after(1000, update_label)
main = tk.Tk()
main.title("Cube Timer Mark 4 Prototype")
main.resizable(0,0)
tk.Label(main, text="Cube Time Mk3 Prototype", font=['Consolas',16]).grid(row = 0,column=0)
textbox = tk.Text(main,font=['Consolas',20], height = 1,width = 27)
textbox.grid(row = 1,column=0)
#textbox.insert('1.0',scrambler())
timer_frame = tk.Frame(main).grid(row=2,column=0)
timer_label = tk.StringVar()
timer_label.set("0.0s")
# using textvariable, you can simply update timer_label and it will be reflected
tk.Label(timer_frame,textvariable = timer_label ,font=['Consolas',20,'bold'],pady = 25).grid(row = 2,column = 0)
# bind keypress and keyrelease to the functions
main.bind("<KeyPress>", key_press)
main.bind("<KeyRelease>", key_release)
# call update label function for the first time
update_label()
main.mainloop()
I have a program that requires input to a module while it is running, so I am implementing a simple Dialog box to get input from the user to use in my Tkinter program. But I also need it to timeout when running the module as a console program, to pass over it or timeout after so many seconds when the user is not interacting with it. And not to have it just sit there and wait forever until the user interacts with it. How do I terminate the window after a timeout? Here is what I have now ...
def loginTimeout(timeout=300):
root = tkinter.Tk()
root.withdraw()
start_time = time.time()
input1 = simpledialog.askinteger('Sample','Enter a Number')
while True:
if msvcrt.kbhit():
chr = msvcrt.getche()
if ord(chr) == 13: # enter_key
break
elif ord(chr) >= 32: #space_char
input1 += chr
if len(input1) == 0 and (time.time() - start_time) > timeout:
break
print('') # needed to move to next line
if len(input1) > 0:
return input1
else:
return input1
Not entirely sure what the question is, but you could use the tkinter after() method in a similar way to this:
import tkinter as tk
root = tk.Tk()
root.geometry('100x100')
def get_entry() -> str:
"""Gets and returns the entry input, and closes the tkinter window."""
entry = entry_var.get()
root.destroy() # Edit: Changed from root.quit()
return entry
# Your input box
entry_var = tk.StringVar()
tk.Entry(root, textvariable=entry_var).pack()
# A button, or could be an event binding that triggers get_entry()
tk.Button(root, text='Enter/Confirm', command=get_entry).pack()
# This would be the 'timeout'
root.after(5000, get_entry)
root.mainloop()
So, if the User enters something they can hit confirm or launch an event binded to the entry, or after the delay the program runs get_entry anyway. Maybe this will give you an idea, you can also check out the other widget methods:
https://effbot.org/tkinterbook/widget.htm
EDIT: I'm not sure how this is arranged in your program, but root.mainloop() blocks, so once it's running, code after it won't run until the mainloop() exits. If you're function is part of a toplevel() window, you could use wait_window() to prevent the function from advancing until your timeout destroys the toplevel window or the function is called before by the User. The example, although probably not the best solution, would be:
def loginTimeout(timeout_ms: int=5000):
root = tk.Tk()
root.geometry('200x50')
# function to get input and change the function variable
input_ = ''
def get_input():
nonlocal input_
input_ = entry_var.get()
root.destroy()
# build the widgets
entry_var = tk.StringVar()
tk.Entry(root, textvariable=entry_var).pack(side='left')
tk.Button(root, text='Confirm', command=get_input).pack(side='right')
# timeout
root.after(timeout_ms, get_input)
# mainloop
root.mainloop()
# won't get here until root is destroyed
return input_
print(loginTimeout())