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.
Related
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.
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, ...)
...
im working on a selenium project using tkinter in python. I want to execute many selenium task at one time(could be up to 100), so I used threading to accomplish this goal, but I came to a problem, due to the fact that I need all individual selenium task to wait a couple seconds between instructions, I used the '.after' method but for some reason my program is still freezing up, I've done a lot of research but I cant seem to find the answer yet. Isn't there a way to use tkinter, threading and some sort of sleeping in python? Do I need to switch to multiprocessing? but I would need all selenium processes to finish within the same 2 minutes and each process takes about a minute.(e.g lets say i start 100, 1 minute processes at 6:00 all task would need to be finished by 6:02)
I created minimal code which mimics my selenium script, so its easier to read, here it is:
from tkinter import *
import tkinter as tk
import time
root = tk.Tk()
root.geometry('700x700')
import threading
class Make:
def __init__(self,num):
self.num = num.get()
Label(root,text='HELLO WORLD WILL PRINT: '+str(self.num)+' times, press go to confirm').pack()
Button(root, text='go', command=lambda: self.starting()).pack()
def starting(self):
for count in range(self.num):
t = threading.Thread(target=gogogo())
t.start()
def gogogo():
tk.Label(root,text='HELLO WORLD').pack()
root.after(2000,print('Please wait 2 seconds'))
tk.Label(root,text='You are inside my world').pack()
Label(root,text='How many times should I print: HELLO WORLD').pack()
num = IntVar()
Entry(root, textvariable=num).pack()
Button(root, text='Start', command=lambda: Make(num)).pack()
root.mainloop()
You main problem is that after() and Thread() similar to command= needs only function's name without () and it will later use () to execute it.
Other problem is that after() doesn't stop code but it only send information to mainloop to execute function after 2000ms and later Python runs at once next line after after(). You have to add Label in fucntion executed by after()
def gogogo():
tk.Label(root, text='HELLO WORLD').pack()
print('Please wait 2 seconds')
root.after(2000, next_message) # function's name without ()
def next_message():
tk.Label(root, text='You are inside my world').pack()
# from tkinter import * # PEP8: `import *` is not preferred
import tkinter as tk
import threading
import time
class Make:
def __init__(self, num):
self.num = num.get()
text = 'HELLO WORLD WILL PRINT: {} times, press go to confirm'.format(self.num)
tk.Label(root, text=text).pack()
tk.Button(root, text='go', command=self.starting).pack()
def starting(self):
for count in range(self.num):
t = threading.Thread(target=gogogo) # function's name without ()
t.start()
def gogogo():
tk.Label(root, text='HELLO WORLD').pack()
print('Please wait 2 seconds')
root.after(2000, next_message) # function's name without ()
def next_message():
tk.Label(root, text='You are inside my world').pack()
# --- main ---
root = tk.Tk()
root.geometry('700x700')
tk.Label(root, text='How many times should I print: HELLO WORLD').pack()
num = tk.IntVar()
tk.Entry(root, textvariable=num).pack()
tk.Button(root, text='Start', command=lambda:Make(num)).pack()
root.mainloop()
EDIT: Because gogogo() runs in separated thread so you can also use time.sleep() because it doesn't block mainloop() in main thread
def gogogo():
tk.Label(root, text='HELLO WORLD').pack()
print('Please wait 2 seconds')
time.sleep(2)
tk.Label(root, text='You are inside my world').pack()
I am writing a program to record audio with the use of a Tkinter GUI. For recording audio itself, I use this code: https://gist.github.com/sloria/5693955 in nonblocking mode.
Now I want to implement something like a start/stop button, but feel like I am missing out on something. Following suppositions:
I can't use a while Truestatement either a time.sleep()function because it's going to break the Tkinter mainloop()
As a result, I will probably have to use a global bool to check whether my start_recording()function is running
I will have to call stop_recording in the same function as start_recordingbecause both have to use the same object
I can not use root.after() call because I want the recording to be user-defined.
Find a code snippet of the problem below:
import tkinter as tk
from tkinter import Button
import recorder
running = False
button_rec = Button(self, text='Aufnehmen', command=self.record)
button_rec.pack()
button_stop = Button(self, text='Stop', command=self.stop)
self.button_stop.pack()
rec = recorder.Recorder(channels=2)
def stop(self):
self.running = False
def record(self):
running = True
if running:
with self.rec.open('nonblocking.wav', 'wb') as file:
file.start_recording()
if self.running == False:
file.stop_recording()
root = tk.Tk()
root.mainloop()
I know there has to be a loop somewhere, but I don't know where (and how).
Instead of with I would use normal
running = rec.open('nonblocking.wav', 'wb')
running.stop_recording()
so I would use it in two functions - start and stop - and I wouldn't need any loop for this.
I would need only global variable running to have access to recorder in both functions.
import tkinter as tk
import recorder
# --- functions ---
def start():
global running
if running is not None:
print('already running')
else:
running = rec.open('nonblocking.wav', 'wb')
running.start_recording()
def stop():
global running
if running is not None:
running.stop_recording()
running.close()
running = None
else:
print('not running')
# --- main ---
rec = recorder.Recorder(channels=2)
running = None
root = tk.Tk()
button_rec = tk.Button(root, text='Start', command=start)
button_rec.pack()
button_stop = tk.Button(root, text='Stop', command=stop)
button_stop.pack()
root.mainloop()
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.