Running a thread with Tkinter object - python

When I push the button the scan_open_ports start working until the line ip_list.curselection() where it stops, this line blocks the running of the function...
I wanted to know why and how to fix that?
Thanks
def scan_open_ports():
#long runtime function
print "Asdasd\"
ip_list.curselection()
def thr_open_ports():
threading.Thread(target=scan_open_ports).start()
ip_list = Listbox()
scan_ports = Button(window, text="Scan Open Ports", command= thr_open_ports, height = 10, width = 20)

I have shamelessly stolen some code from this answer, just as a basis for some tkinter code: How to align label, entry in tkinter
The following code adapts this to have a Queue and a Thread which only runs after a button press.
The Thread communicates back to the mainloop through the Queue which is polled by calls to root.after()
from tkinter import *
from threading import Thread
from queue import Queue
from time import sleep
from random import randint
root = Tk()
root.geometry("583x591+468+158")
root.title("NOKIA _ANSI Performance")
root.configure(borderwidth="1")
root.configure(relief="sunken")
root.configure(background="#dbd8d7")
root.configure(cursor="arrow")
root.configure(highlightbackground="#d9d9d9")
root.configure(highlightcolor="black")
Label3 = Label(root)
Label3.configure(text='''Device IP Address :''')
Label3.pack()
Label5 = Label(root)
Label5.configure(text='''Username :''')
Label5.pack()
Entry5 = Entry(root)
Entry5.pack()
th = None
q = Queue()
def run_me(q):
sleep(5)
q.put(randint(1, 99))
def check_queue():
if not q.empty():
item = q.get()
Label5.configure(text=str(item))
root.after(200, check_queue)
def do_thread():
global th
th = Thread(target=run_me, args=(q,))
th.start()
Button1 = Button(root)
Button1.configure(pady="0")
Button1.configure(text='''NEXT''')
Button1.configure(command=do_thread)
Button1.pack()
root.after(200, check_queue)
mainloop()
The mainloop() is not blocked either by the Thread nor by the polling that check_queue() does.

Related

Progress bar for loop does not appear until loop calculation is done

I'm having difficulty comprehending how progress bars work in the context of a loop. My example is as follows:
import time
from tkinter import ttk,Tk, Label, Button,Label,DoubleVar
MAX = 4
root = Tk()
root.title("My Progressbar")
root.geometry('500x500')
theLabel = Label(root, text="Progress")
theLabel.pack()
progress_var = DoubleVar()
progress_var.set(0)
progressbar = ttk.Progressbar(root, variable=progress_var,
length=400,maximum=MAX,mode='determinate')
progressbar.pack()
for i in range(MAX+1):
print(i) #Where I will eventually do all my work. Here I print i and pause .5 sec
time.sleep(.5)
progress_var.set(i)
root.update_idletasks()
# Add quit button
def _quit():
root.quit()
root.destroy()
quit_button = Button(master=root, text="Quit", bg='lightgray',command=_quit)
quit_button.pack()
root.mainloop()
I'm missing something obvious as the bar does not appear until after the loop is complete. Also, is it possible to indicate the % complete somewhere on the bar?
You do the "work" before the flow of execution even has a chance to reach root.mainloop. You'll want to simulate doing the work after starting the mainloop. Maybe you could add a button which, upon being clicked, simulates the work? Try this:
# ...
def do_work():
for i in range(MAX+1):
print(i) #Where I will eventually do all my work. Here I print i and pause .5 sec
time.sleep(.5)
progress_var.set(i)
root.update_idletasks()
# ...
do_work_button = Button(master=root, text="Do Work", command=do_work)
do_work_button.pack()
root.mainloop()
If you only just want to increase the value in the progressbar each 0.5 second.Try this:
import time
from tkinter import ttk,Tk, Label, Button,Label,DoubleVar
MAX = 4
root = Tk()
root.title("My Progressbar")
root.geometry('500x500')
theLabel = Label(root, text="Progress")
theLabel.pack()
progress_var = DoubleVar()
progress_var.set(0)
progressbar = ttk.Progressbar(root, variable=progress_var,
length=400,maximum=MAX,mode='determinate')
progressbar.pack()
def add():
if progress_var.get() < MAX:
print(progress_var.get())
progress_var.set(progress_var.get()+1)
root.after(500, add)
# Add quit button
def _quit():
root.quit()
root.destroy()
quit_button = Button(master=root, text="Quit", bg='lightgray',command=_quit)
quit_button.pack()
root.after(500, add)
root.mainloop()
If it is a specific work, you need to create thread or process.

How to change tkinter label while another process is running?

I have a large code where a button press is supposed to run a code that will take roughly 15 seconds to complete. Within this time I want to display a label that says "Processing, please wait" or something of that sort. However in python, the whole GUI created using tkinter will freeze and unfreeze once the procedure is over. How do I get around to doing this? I created a smaller code so that I can explain easier.
from tkinter import *
from threading import Thread
import os
import sys
import time
master = Tk()
master.geometry("500x500")
master.resizable(False,False)
def tryout():
sign2.config(text = "AAA")
for x in range(5):
print(x)
time.sleep(1)
sign2.config(text = "BBB")
for x in range(5):
print(x)
time.sleep(1)
sign2.config(text = "CCC")
def close_window():
master.destroy()
sys.exit()
sign1 = Label(master, text = "VNA GUI").grid(pady=10, padx=10)
sign2 = Label(master, text = "Choose option to continue")
sign2.grid(pady=10, padx=10, ipadx=50)
Button(master, text='Exit', command=close_window).grid(pady=10, padx=20)
butTest = Button(master, text='test', command=tryout)
butTest.grid(pady=10, padx=20)
master.mainloop( )
So in this code I expect to see 'AAA' on the label first, followed by 'BBB' at the middle of the count from 0 to 4, and then 'CCC' at the end of the final count from 0 to 4. What happens here is the GUI freezes at the beginning, the count carries on and I just see 'CCC'. How do I get around this?
There are only a few changes necessary to do that with threading.
First create a function start_tryout:
def start_tryout():
Thread(target=tryout, daemon=True).start() # deamon=True is important so that you can close the program correctly
Then create the button with the new command:
butTest = Button(master, text='test', command=start_tryout)
Then it should no longer freeze the gui and you should be able to see the label change.
You can try threading. I've made changes below to the code and tested it here, and it worked.
from tkinter import *
from threading import Thread
import os
import sys
import time
import threading # NEW
master = Tk()
master.geometry("500x500")
master.resizable(False,False)
def tryout():
sign2.config(text = "AAA")
for x in range(5):
print(x)
time.sleep(1)
sign2.config(text = "BBB")
for x in range(5):
print(x)
time.sleep(1)
sign2.config(text = "CCC")
def close_window():
master.destroy()
sys.exit()
def thread(): # NEW
threading.Thread(target=tryout).start() # NEW
sign1 = Label(master, text = "VNA GUI").grid(pady=10, padx=10)
sign2 = Label(master, text = "Choose option to continue")
sign2.grid(pady=10, padx=10, ipadx=50)
Button(master, text='Exit', command=close_window).grid(pady=10, padx=20)
butTest = Button(master, text='test', command=thread) # Changed
butTest.grid(pady=10, padx=20)
master.mainloop( )

tkinkter thread not terminating (gif runs in endless loop)

I'm trying to link 2 actions with the pressing of a button. I'm using threading
One function 'callback' prints a message, and another one creates a label that
displays a gif animation. When I try to thread them and press the button to run
them, the gif is displayed infinitely (continously), but I'd like to have it
removed (deleted) from the GUI as soon 'callback' finishes running. Any help is appreciated.
from tkinter import *
from tkinter import ttk
from tkinter import scrolledtext
import threading
import time
root = Tk()
root.title('Title')
def callback():
print('this message')
time.sleep(0.5)
def animate():
while True:
try:
time.sleep(0.04)
img = PhotoImage(file='path',format="gif - {}".format(num))
label1 = ttk.Label(root, image=img)
label1.grid(row=0, column=1)
num+=1
except:
num=0
def thr():
t1= threading.Thread(target=callback)
t2= threading.Thread(target=animate)
t1.start()
t2.start()
button = ttk.Button(root, text='click', command=thr).grid(row=0, column=1, sticky=N)
entry = scrolledtext.ScrolledText(root, width=30, heigh=10, wrap=WORD)
entry.grid(row=0, column=0)
entry.focus()
root.mainloop()
You can use variable to inform second thread that first thread ends.
running = False
But it can be not the best method - maybe you should rather use module queue to send information from one thread to another.
You can use label1.destroy() to remove label and image but you have to create label only once and later only change image in this label.
label1['image'] = img
Code:
from tkinter import *
from tkinter import ttk
from tkinter import scrolledtext
import threading
import time
#from PIL import Image, ImageTk # for `png`
# --- functions ----
def callback():
# inform function to use external/global variable because you use `=`
global running
print('callback: start')
time.sleep(2)
print('callback: end')
# inform other thread to end running
running = False
def animate():
print('animate: start')
while running:
try:
time.sleep(0.04)
img = PhotoImage(file='path', format="gif - {}".format(num))
#img = Image.open('test/image-{}.png'.format(num)) # for `png`
#img = ImageTk.PhotoImage(img) # for `png`
#label1.img = img # solution for garbage collector problem # for `png`
label1['image'] = img
num += 1
except:
num = 0
#label1.img = None # doesn't work
#label1['image'] = None # doesn't work
# destroy label
label1.destroy()
print('animate: end')
def thr():
# inform function to use external/global variable because you use `=`
global running
global label1
# create new label
label1 = ttk.Label(root)
label1.grid(row=0, column=1)
running = True
t1 = threading.Thread(target=callback)
t2 = threading.Thread(target=animate)
t1.start()
t2.start()
# --- main ---
# create global variables
running = False
label1 = None
# - GUI -
root = Tk()
root.title('Title')
button = ttk.Button(root, text='click', command=thr)
button.grid(row=0, column=1, sticky=N)
entry = scrolledtext.ScrolledText(root, width=30, heigh=10, wrap=WORD)
entry.grid(row=0, column=0)
entry.focus()
root.mainloop()
EDIT: you could also use
while t1.is_alive():
instead of while running:
EDIT: you can also use root.after isntead of second thread.
from tkinter import *
from tkinter import ttk
from tkinter import scrolledtext
import threading
import time
#from PIL import Image, ImageTk # for `png`
# --- functions ----
def callback():
# inform function to use external/global variable because you use `=`
global running
print('callback: start')
time.sleep(2)
print('callback: end')
def animate(num=0):
try:
img = PhotoImage(file='path', format="gif - {}".format(num))
#img = Image.open('test/ball-{}.png'.format(num)) # for `png`
#img = ImageTk.PhotoImage(img) # for `png`
#label1.img = img # solution for garbage collector problem # for `png`
label1['image'] = img
num += 1
except:
num = 0
if t1.is_alive():
# execute again after 4ms
root.after(4, animate, num) # 4ms = 0.04s
else:
# destroy label
label1.destroy()
print("animate: end")
def thr():
# inform function to use external/global variable because you use `=`
global label1
global t1
# create new label
label1 = ttk.Label(root)
label1.grid(row=0, column=1)
t1 = threading.Thread(target=callback)
t1.start()
print("animate: start")
animate(0)
# --- main ---
# create global variables
label1 = None
t1 = None
# - GUI -
root = Tk()
root.title('Title')
button = ttk.Button(root, text='click', command=thr)
button.grid(row=0, column=1, sticky=N)
entry = scrolledtext.ScrolledText(root, width=30, heigh=10, wrap=WORD)
entry.grid(row=0, column=0)
entry.focus()
root.mainloop()

End multi scripts running on tkinter

I am trying to end the process of multi-scripts running on tkinter by a click of the end process created button. However, this does not seem to work when any of the scripts are running in an infinite loop to stop the process.
Here is my code:
import os
import sys
from tkinter import *
import tkinter.messagebox as tkmsg
root = Tk(className=" x") # creates root window
# all components of thw window will come here
Label(root,text='Enter Load Value or Choose a test action',font=("Helvetica", 16), width=40, height=2).pack(side=TOP)
svalue = StringVar() # defines the widget state as string
w = Entry(root,textvariable=svalue).pack(side=LEFT,padx=10) # adds a textarea widget
def act():
print ("Value entered")
print ('%s' % svalue.get())
def script_1():
os.system('python script1_1.py')
def script_2():
os.system('python script1_2.py')
global root
while 1:
root.update()
pass
def script_3():
os.system('python script1_3.py')
def script_4():
os.system('python script1_4.py')
def script_5():
os.system('python script1_5.py')
def quit():
tkmsg.showinfo("Stopped", "Stopped")
sys.exit
if __name__ == '__main__':
global root
root = Frame ()
root.pack ()
button0 = Button(root,text="Load", command=act).pack(side=LEFT,padx=10)
button1 = Button(root,text="Test 1",command=script_1).pack(side=LEFT,padx=10)
button2 = Button(root,text="Test 2",command=script_2).pack(side=LEFT,padx=10)
button3 = Button(root,text="Test 3",command=script_3).pack(side=LEFT,padx=10)
button4 = Button(root,text="Test 4",command=script_4).pack(side=LEFT,padx=10)
button5 = Button(root,text="Test 5",command=script_5).pack(side=LEFT,padx=10)
button6 = Button(root,fg="red",text="End process",command=quit).pack(side=LEFT,padx=10)
button.mainloop() # an event loop to invoke custom function "act"
root.mainloop() # To keep GUI window running

While Loop Locks Application

I have been banging my head for a while now on a application I am working on. After many hours trying to debug an issue where the interface locks up and nothing else can take place I figured out it was the dreaded While loop. See this example below and run it. When you start the while loop by clicking on the button you cannot do anything else on the screen. In this is case it is just a simple alert button that needs pressing.
from Tkinter import *
import tkMessageBox
root = Tk()
root.geometry("450x250+300+300")
root.title("Raspberry PI Test")
def myloop():
count = 0
while (count < 500):
print 'The count is:', count
count = count + 1
print "Good bye!"
def mymessage():
tkMessageBox.showinfo(title="Alert", message="Hello World!")
buttonLoop = Button(root, text="Start Loop", command=myloop)
buttonLoop.place(x=5, y=15)
buttonMessage = Button(root, text="Start Loop", command=mymessage)
buttonMessage.place(x=85, y=15)
root.mainloop()
How can I have a loop that needs to run until a count is completed and still be able to do other tasks in my application? I should also note that I have tried this same thing using a Thread and it doesn't matter. The UI is still waiting for the While loop to end before you can do anything.
now that I understand what you want better (a stopwatch) I would recommend the root.after command
from Tkinter import *
import tkMessageBox
import threading
import time
root = Tk()
root.geometry("450x250+300+300")
root.title("Raspberry PI Test")
print dir(root)
count = 0
def start_counter():
global count
count = 500
root.after(1,update_counter)
def update_counter():
global count
count -= 1
if count < 0:
count_complete()
else:
root.after(1,update_counter)
def count_complete():
print "DONE COUNTING!! ... I am now back in the main thread"
def mymessage():
tkMessageBox.showinfo(title="Alert", message="Hello World!")
buttonLoop = Button(root, text="Start Loop", command=myloop)
buttonLoop.place(x=5, y=15)
buttonMessage = Button(root, text="Start Loop", command=mymessage)
buttonMessage.place(x=85, y=15)
root.mainloop()
(original answer below)
use a thread
from Tkinter import *
import tkMessageBox
import threading
import time
root = Tk()
root.geometry("450x250+300+300")
root.title("Raspberry PI Test")
print dir(root)
def myloop():
def run():
count = 0
while (count < 500) and root.wm_state():
print 'The count is:', count
count = count + 1
time.sleep(1)
root.after(1,count_complete)
thread = threading.Thread(target=run)
thread.start()
def count_complete():
print "DONE COUNTING!! ... I am now back in the main thread"
def mymessage():
tkMessageBox.showinfo(title="Alert", message="Hello World!")
buttonLoop = Button(root, text="Start Loop", command=myloop)
buttonLoop.place(x=5, y=15)
buttonMessage = Button(root, text="Start Loop", command=mymessage)
buttonMessage.place(x=85, y=15)
root.mainloop()
note that when you show the info box that will block at the windows api level so the thread counting will wait till that closes ... to get around that you can just replace threading with multiprocessing I think
I don't really know much about TKinter, but from my reading it's clear that you need to use a some TKinter method in your while loop in order to update your text box. TKinter runs on an event loop so you have to send a signal from your code to re-enter TKinter's execution.
You've done a great job discovering that your while loop is blocking the execution of your UI's updates. So instead of threading you need to just pause your counting's execution and let TKinter update the UI.
This tutorial provides an excellent example. The key is on line 24 where he calls root.update which I believe breaks from your program to let TKinter do it's thing.
Here is the final code just to prove that the thread works. The count is displaying on the screen at the same time as it is happening. Thanks again Joran!
from Tkinter import *
import tkMessageBox
import threading
import time
root = Tk()
root.geometry("450x250+300+300")
root.title("Raspberry PI Test")
showResults = StringVar()
showResults.set('0')
print dir(root)
def myloop():
def run():
count = 0
while (count < 1000) and root.wm_state():
print 'The count is:', count
showResults.set(count)
count = count + 1
#time.sleep(1)
root.after(1,count_complete)
thread = threading.Thread(target=run)
thread.start()
def count_complete():
print "DONE COUNTING!! ... I am now back in the main thread"
def mymessage():
tkMessageBox.showinfo(title="Alert", message="Hello World!")
buttonLoop = Button(root, text="Start Loop", command=myloop)
buttonLoop.place(x=5, y=15)
buttonMessage = Button(root, text="Message", command=mymessage)
buttonMessage.place(x=85, y=15)
l2 = Label(root, width=15, height=4, font=("Helvetica", 16), textvariable=showResults, background="black", fg="green")
l2.place(x=15, y=65)
root.mainloop()

Categories

Resources