How to quit tkinter window with schedule running? - python

I am creating a scheduler for a few different python scripts and I am using a tkinter window to take in the usernames and passwords for the servers I am accessing.
I seem to be able to get the scheduler to work, but when it does, it now breaks the mainloop so the window no longer responds.
I have tried putting the mainloop inside the loop that keeps the schedule running, but that causes the scheduler to stop functioning.
import tkinter as tk
import subprocess
import schedule
import time
class MainWindow(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.e_user_label = tk.Label(self, text="Email Username")
self.e_pw_label = tk.Label(self, text="Email Password")
self.e_account_label = tk.Label(self, text="Email Account")
self.email_user = tk.Entry(self, width=30)
self.email_pw = tk.Entry(self, show="*", width=30)
self.email_account = tk.Entry(self, width=30)
self.start_scheduler = tk.Button(self, text="Start Scheduler",
command=self.check_email_request)
self.e_user_label.pack()
self.email_user.pack()
self.e_pw_label.pack()
self.email_pw.pack()
self.e_account_label.pack()
self.email_account.pack()
self.start_scheduler.pack()
def check_email_request(self):
call_email_script = 'call python email_monitor.py {} {} {}'.format(self.email_user.get(),
self.email_pw.get(), self.email_account.get())
completed = subprocess.run(call_email_script, shell=True)
print('returncode:', completed.returncode)
def print_stuff(self):
schedule.every(1).minutes.do(self.check_email_request)
while True:
schedule.run_pending()
time.sleep(1)
w.mainloop() #my failed attempt to solve this problem
w = MainWindow()
w.mainloop()
Expected the scheduler to keep calling my check_email_request function every 1 minute. If I don't include the main loop in schedule loop, the window no longer functions. When I include the mainloop, the scheduler no longer functions.

Related

How to implement Queues for Threading with Tkinter GUI

I'm working on a GUI for a simulation software and need to have two processes running at the same time. One for the simulation, and one for displaying information for the user. BOTH threads need to make changes to the GUI. When I run my code, I get the error: RuntimeError: main thread is not in main loop.
After some research, I think that there is no way you can access the GUI from a different thread than the main thread. I also think that I probably should use Queues, but I am not sure how to do this.
Here's my simplified code:
import tkinter as tk
from tkinter import END
import threading
import time
def write_to_entry1():
for i in range(20):
entry1.delete(0, END)
entry1.insert(0, i)
entry1.update()
time.sleep(1)
def write_to_entry2():
for i in range(20):
entry2.delete(0, END)
entry2.insert(0, i)
entry2.update()
time.sleep(1)
# Creating the GUI
root = tk.Tk()
root.geometry("200x100")
label1 = tk.Label(root)
label1.configure(text="Thread 1:")
label1.grid(column='0', row='0')
entry1 = tk.Entry(root)
entry1.grid(column='1', row='0')
label2 = tk.Label(root)
label2.configure(text="Thread 2:")
label2.grid(column='0', row='1')
entry2 = tk.Entry(root)
entry2.grid(column='1', row='1')
root.update()
t = threading.Thread(target=write_to_entry2)
t.daemon = True
t.start()
write_to_entry1()
root.mainloop()
Thanks for any answers.

Stop tkinter GUI with a button

I am creating a GUI with tkinter and I need help.
I have two buttons, one for starting my code and another one for exit the GUI and stop the code running. Here is part of my code:
def close_app():
window.destroy()
def run_app():
.... do something...
window = tk.Tk() #start of the app
button_run = tk.Button(frame_bottom, text = "Search", command = run_app)
button_run.grid(column=0, row=0, sticky='w', padx=100, pady=2)
button_close = tk.Button(frame_bottom, text = "Exit", command = close_app)
button_close.grid(column=1, row=0, sticky='e', padx=100, pady=2)
window.mainloop() #end of the app
The problem is that when I press "Exit" button the GUI does not respond anymore and the code still continues in the background. How can I stop the execution of the program with my Exit button?
I tried also to delete the Exit button and to stop the code simply closing the GUI, but the problem is the same, after a while "not respondind" appears. How can I solve it?
From the code you posted it's hard to tell, but if run_app is doing something that takes very long the function won't be stopped - that's why your GUI is freezing.
If run_app contains a loop, setting a global variable in close_app then checking inside the loop if that variable is set and if it is stopping the loop would be the solution:
import time
import tkinter as tk
exit_app = False
def close_app():
exit_app = True
window.destroy()
def run_app():
for i in range(100):
if exit_app:
break
time.sleep(1)
window = tk.Tk()
button_run = tk.Button(frame_bottom, text = "Search", command = run_app)
button_run.grid(column=0, row=0, sticky='w', padx=100, pady=2)
button_close = tk.Button(frame_bottom, text = "Exit", command = close_app)
button_close.grid(column=1, row=0, sticky='e', padx=100, pady=2)
window.mainloop()
(Not tested as your code does not define frame_bottom)
If it doesn't run in a loop, the last solution would be to call sys.exit in close_app which should exit yur script immediately.

Start and stop command to break a loop

I'm working on a webscraping code, and the process is quite long. Thus, I would like to start and stop it in a fancy way, with a button using tkinter. But I couldn't find any solution on the web.
The .after() function doesn't seems to be a good option as I don't want to restart my loop each time. My loop is already working based on urls readed from another document and parsing for each.
I tried the thread option with .join(). But couldn't manage to solve my problem with it. But maybe I don't write my code correctly.
My problem can be formulate as follow:
Start → Read urls doc and launch parsing loop.
Stop → Stop parsing and save content.
For instance I would like something like the following code to work:
from tkinter import *
flag=False
def test_loop():
while flag==True:
print("True")
while flag==False:
print("False")
def start():
global flag
flag=True
test_loop()
def stop():
global flag
flag=False
test_loop()
root = Tk()
root.title("Test")
root.geometry("500x500")
app = Frame(root)
app.grid()
start = Button(app, text="Start", command=start)
stop = Button(app, text="Stop", command=stop)
start.grid()
stop.grid()
root.mainloop()
Just do this:
while links and not stopped:
# scraping here
You can control the stopped bool flag with your button.
You need to import threading library as python takes only one command at a time so threading will create multiple threads to handle multiple tasks simultaneously.
This is the updated code:
from tkinter import *
import threading
flag=False
def test_loop():
while flag==True:
print("True")
if flag==False:
print("False")
def start():
global flag
flag=True
thread.start() #thread start
def stop():
global flag
flag=False
thread.join() #thread stop
#this is the point where you will create a thread
thread=threading.Thread(target=test_loop)
root = Tk()
root.title("Test")
root.geometry("500x500")
app = Frame(root)
app.grid()
start = Button(app, text="Start", command=start)
stop = Button(app, text="Stop", command=stop)
start.grid()
stop.grid()
root.mainloop()
Ok I managed to solve the problem, using a flag system indeed.
from tkinter import *
import threading
def st1():
global go1
global go2
go1=True
while go1==True:
go2=False
print('Start')
def st2():
global go1
global go2
go2=True
while go2==True:
go1=False
print('Stop')
def thread1():
threading.Thread(target=st1).start()
def thread2():
threading.Thread(target=st2).start()
root = Tk()
root.title("Test")
root.geometry("500x500")
app = Frame(root)
app.grid()
start = Button(app, text="Start", command=thread1)
stop = Button(app, text="Stop", command=thread2)
start.grid()
stop.grid()
root.mainloop()
Thanks for your help it was helpfull.

how to close a Thread from Tkinter While the Thread is running Python selenium

So, I made a small canvas window with tkinter which has 2 buttons, One is a start button, the other is a stop button. (I'll attach the GUI tkinter code down below. I wont add the Selenium part because I don't want to confuse anyone with mushed up code.) The start button calls a function thats threaded and that launches my "Reporting_Backbone.py" which is a selenium/pyautogui bot that does a bunch of stuff. My problem is that the stop button does not stop the "Reporting_Backbone.py". In the stop button function I've tried sys.exit() but the selenium and the GUI stay open (and running), I've tried daemons (which I might not have been using them correctly because that did nothing)I've tried setting the stop button function to a lambda (which just freezes the GUI, but not the selenium part) and I've tried setting up some kind of a killswitch as a last resort but honestly this thing wont die, its like Thanos fused with Majin Buu. It just keeps running. How do I make it so that the stop button works? I I'm hoping someone can help me with a solution and explanation. I am still new to coding but I am really loving it, if possible I would really like to understand what I am doing wrong. Thank you.
enter code here
import tkinter as tk
from PIL import Image, ImageTk
import time
import os
import threading
import sys
root = tk.Tk()
#Canvas for GUI
canvas = tk.Canvas(root, width=600, height=800)
canvas.grid(columnspan=3, rowspan=4)
canvas.configure(bg="#b9be9c")
#Button Starting
def start_report():
time.sleep(0.5)
start_text.set("Armed!")
os.system("python Reporting_Backbone.py")
#Button Stopping
def stop_craigslist():
stop_text.set('Stopped')
time.sleep(3)
sys.exit()
#Logo
logo = Image.open('Logo.png')
logo = ImageTk.PhotoImage(logo)
logo_label = tk.Label(image=logo)
logo_label.image = logo
#playing logo in window
logo_label.grid(column=1, row=0)
logo_label.configure(bg="#b9be9c")
#instructions
instructions = tk.Label(root, text="Click the 'Start' Button to begin.")
instructions.grid(columnspan=3, column=0, row=1)
instructions.configure(font=("Helvetica", 25) ,bg="#b9be9c")
#Start Button
start_text = tk.StringVar()
start_btn = tk.Button(root, textvariable=start_text, command=threading.Thread(target=start_report).start, font=("Helvetica", 18), fg="black", height=2, width=15)
start_text.set("Start")
start_btn.grid(column=1, row=2)
#Stop Button
stop_text = tk.StringVar()
stop_btn = tk.Button(root, textvariable=stop_text, command=threading.Thread(target=stop_craigslist).start, font=("Helvetica", 18), fg="black", height=2, width=15) #If I set this to a lambda function the Tkinter GUI Freezes up on me
stop_text.set("Stop")
stop_btn.grid(column=1, row=3)
root.mainloop()
You cannot stop the task created by threading.Thread(). Use subprocess instead:
import subprocess
...
proc = None
def start_report():
global proc
if proc and not proc.poll():
print("process is still running")
return
proc = subprocess.Popen([sys.executable, "Reporting_backbone.py"])
start_text.set("Armed!")
def stop_craigslist():
global proc
if proc:
proc.terminate()
proc = None
stop_text.set('Stopped')
...
start_btn = tk.Button(root, ..., command=start_report, ...)
...
stop_btn = tk.Button(root, ..., command=stop_craigslist, ...)
...

Python program will only run from IDLE

This is my first run at python and i am by no means a code writer. I have a project that I needed to write a GUI to command the arduino and after much googling and piecing together code I made this program which works perfectly when ran from IDLE. When I launch it from windows (double click it) or from linux (via command line python3 filler.py) it opens and closes like there is an error. I can launch other python programs by same methods with no problems. Where as its not an issue to launch it out of IDLE to make the hardware operate I just cant give up as I would like to get more knowledgeable with python for future projects. Any help would be greatly appreciated.
import tkinter as tk
import serial as s
import time as t
from tkinter import *
class Action:
def __init__(self):
def on():
ser.write(b'7')
def exit():
ser.close() # close serial port
quit()
self.window = Tk()
self.window.title("Moore Speciality Brewing")
self.window.geometry('640x480')
self.lbl = Label(self.window, text="Can Filler",fg='black',font=(None, 15))
self.lbl.place(relx=0.24, rely=0.10, height=50, width=350)
bo = Button(self.window, text="Fill Can", width=10 ,bg='red' ,command=on)
bo.place(relx=0.34, rely=0.30, height=40, width=200)
ext = Button(self.window, text="Exit", width=10, bg='white', command=exit)
ext.place(relx=0.34, rely=0.50, height=40, width=200)
class Prompt(tk.Tk):
def __init__(self):
global comm
comm = None
tk.Tk.__init__(self)
self.geometry('640x480')
self.label = tk.Label(self, text="Comm Port",fg='black',font=(None, 15))
self.entry = tk.Entry(self)
self.button = tk.Button(self, text="Get", command=self.on_button)
self.entry.place(relx=.5, rely=.5,anchor="center" )
self.label.place(relx=.5, rely=.44,anchor="center")
self.button.place(relx=.5, rely=.56,anchor="center")
def on_button(self):
comm = self.entry.get()
global ser
ser = s.Serial(comm, 9600, timeout=0) # check your com port
t.sleep(2)
Action()
self.destroy()
Prompt()
You need to call the mainloop.
Remove the call to Prompt() (last line) and substitute it with something like this (at the bottom of the script):
if __name__ == "__main__":
prm = Prompt()
prm.mainloop()
Here more on tkinter mainloop
you have already imported time in your code.
just use t.sleep(60) at the end of your code to make the cli wait for you to see if there is an error and let you debug.
at the end Prompt() is not correct. use something like this:
myPrompt = Prompt()
myPrompt.mainloop()
this part actualy calls the tkinter gui.

Categories

Resources