Tkinter: Calling a multithreaded instance - python

I am a beginner at python, and it is my first language. I have been tasked with something that is quickly becoming too large for me to grasp that I need to finish. I am almost done. At this moment, I have created a dialog box that serves as a Main Menu, a dialog box that is a selectable option from the main menu that runs a test, and a multithreaded instance which runs the test, opens up a "please wait" box and up on finishing the test, another dialog box pops up which declares the test complete.
My issue: Within the "Run Test" dialog, I am trying to create a button that will call the multithreaded instance into action. From the code I have parsed together with the help of others, I can not see which class to instantiate within the "Run Test" dialog box.
I am beginning to believe my implementation of threading is incorrect. However there must be a way.
This is the module I am trying to call on.
from slice_setup import SLICE_SETUP
import Tkinter as tk
import threading
import Queue
class GuiPart:
def __init__(self, master, queue):
self.queue = queue
self.master = master
self.master.geometry("300x100+400+250")
self.master.title("RSAM BCT")
tk.Label(master, text="REDCOM SLICE", fg="red").pack()
tk.Label(master, text="BCT - Basic Configuration Test", fg= "red").pack()
tk.Label(master, text="Please wait...", fg= "black").pack()
tk.Label(master, text="Estimated time: 3 min 6 sec", fg= "black").pack()
def processIncoming(self):
while self.queue.qsize():
try:
text = self.queue.get(0)
Complete(self.master, text)
except Queue.Empty:
pass
class ThreadedClient:
def __init__(self, master):
self.master = master
self.queue = Queue.Queue()
self.gui = GuiPart(master, self.queue)
self.running = True
self.thread = threading.Thread(target=self.workerThread1)
self.thread.start()
self.periodicCall()
def periodicCall(self):
self.gui.processIncoming()
if not self.running:
return
self.master.after(100, self.periodicCall)
def workerThread1(self):
obj_rcs = SLICE_SETUP()
obj_rcs.SLICE()
self.queue.put("Configuration Complete!")
self.running = False
class Complete(tk.Toplevel):
def __init__(self, master=None, completetext=""):
tk.Toplevel.__init__(self, master)
self.geometry("400x300+400+250")
self.title("RSAM BCT")
tk.Label(self, text="REDCOME SLICE", fg="red").pack()
tk.Label(self, text="BCT - Basic Configuration Test", fg="red").pack()
tk.Label(self, text=completetext, fg="dark green").pack()
tk.Label(self, text="Trunk 1: Port 1: Phone 1: 760-450-4500", fg="black").pack()
tk.Label(self, text="Trunk 1: Port 2: Phone 2: 760-450-4501", fg="black").pack()
tk.Button(self, text=" Exit ", command=self.destroy).pack()
if __name__ == "__main__":
root = tk.Tk()
client = ThreadedClient(root)
root.mainloop()
and this is where I am trying to call from:
import sys
import Tkinter as Tk()
from bct_pleasewait import ????
import threading
import Queue
import time
sGui = Tk()
class slice_menu:
def runtest(self):
obj_wait = ????
obj_wait.????
def slicemenu(self):
sGui.geometry("400x300+400+250")
sGui.title("RSAM BCT")
Label(sGui, text= "REDCOM SLICE", fg="red").pack()
Label(sGui, text= "BCT - Basic Configuration Test", fg= "red").pack()
Label(sGui, text= "-Ensure you are logged off of HyperTerminal", fg= "black").pack()
Label(sGui, text= "-Turn on your REDCOM SLICE unit",
fg= "black").pack()
Label(sGui, text= "-Please connect your laptop to SLICE CONSOLE", fg= "black").pack()
Label(sGui, text= "-This configuration will take 3 minutes", fg= "black").pack()
Button(sGui, text = " Run ", command = self.runtest).pack()
Button(sGui, text = " Exit test ", command = sGui.destroy).pack()
sGui.mainloop()
This class still has minor errors in it but I just want to get this issue solved first.

Not a specific answer, but your question is very broad.
Some points to keep in mind:
Tk is not thread-safe. That is, you should only invoke Tk calls from the main thread. It is OK to let other threads do non-gui work.
In CPython, only one thread at a time can be executing Python bytecode, due to the Global Interpreter Lock ("GIL"). So your non-gui thread could make the GUI unresponsive.
If you are only running one test at a time, and if you can divide that test into small pieces, you could use a timeout (also called alarm handler). This handler does a little bit of work, saves its state, updates the progress dialog and then exits, waiting to be invoked again.
If the test runs a long time and you want the GUI to remain responsive, I would suggest using multiprocessing instead of threading. Start your test in a different process, and use things like Queues and Semaphores et cetera to communicate between the GUI and non-qui processes.

Related

How to quit tkinter window with schedule running?

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.

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.

Tkinter GUI stuck till end of the task when pressing a button

When I press the "start program" button, it starts a 5 second task and blocks the GUI.
As i understand, I need to use Threading so each button will work independently from the GUI.
I've been stuck for almost a month already, can someone show me how can execute def start_Button(self): function using threading?
from tkinter import *
import time
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
self.var = IntVar()
self.master.title("GUI")
self.pack(fill=BOTH, expand=1)
quitButton = Button(self, text="Exit", command=self.client_exit)
startButton = Button(self, text="Start Program", command=self.start_Button)
quitButton.grid(row=0,column=0)
startButton.grid(row=0, column=2)
def client_exit(self):
exit()
def start_Button(self):
print('Program is starting')
for i in range (5):
print(i)
time.sleep(1)
root = Tk()
root.geometry("200x50")
app = Window(root)
root.title("My Program")
root.mainloop()
There are a lot of important questions to ask before you jump into threading for the first time, but by and large the most important question is "how do I want to communicate between my threads?" In your minimal example you don't require any communication at all, however in your real code start_Button may be doing some Work and returning data back to the GUI. If that's the case, you have more work to do. Please clarify that as a comment if that's the case.
For the minimal example, it's actually quite easy.
class Window(tkinter.Frame):
# the rest of your GUI class as written, but change...
def start_Button(self):
def f():
# this is the actual function to run
print('Program is starting')
for i in range (5):
print(i)
time.sleep(1)
t = threading.Thread(target=f)
t.start()

Access to Tkinter's Text widget by background thread cause crashes

I created a simple tkinter app, where have used two threads. Their task is to write numbers to widgets such as label and text. One thread is triggered by button (click event) and second is executed as a background thread.
import Tkinter as tk
from ttk import *
from Tkconstants import *
import threading, thread, time
def tl1(text,counter):
while True:
text.insert(END,counter)
counter += 1
time.sleep(2)
def tl2(label,counter):
while True:
label['text'] = counter
counter += 1
time.sleep(1)
class mainWindow():
def __init__(self, master):
self.master = master
self._initLayout()
def _initLayout(self):
#button
self.button = tk.Button(self.master, text="thread1_start", command = self._task1)
self.button.pack()
#label
self.label = tk.Label(self.master)
self.label.pack()
#text
self.text = tk.Text(self.master, width=30)
self.text.pack()
def _task1(self):
t1 = thread.start_new_thread(tl1,(self.text,1))
def _task2(self):
t2 = thread.start_new_thread(tl2,(self.label,1000))
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.mainWindow = mainWindow(self)
self.mainWindow._task2() #background_thread
app = App()
app.mainloop()
In this manner everything works fine, but if we change the background thread to display results on text widget, the whole app freezes.
Why background thread works fine communicating with label but causes problems with text widget? Is there any way to run it properly?
Tkinter isn't thread safe. You can only access widgets from the thread that created them. Your threads will need to put data on a thread-safe queue, and your GUI thread will need to poll the queue.
In your particular case you don't need threads at all. You can use the tkinter after method to run code periodically.

Tkinter can't insert into Text Widgets with pack_forget()

My application can receive multiple jobs which it processes using threads. I've created Tabs which contain a Text Widget for each job, but I'm having trouble inserting text into the text widgets.
No error the application just hangs.
Tabs are generated using the script found here: http://code.activestate.com/recipes/577261-python-tkinter-tabs/
The tabs are a subclass of Frames that use pack_forget() to hide when they're not selected.
A simplified version of my App
server.py
class supply_thread(threading.Thread):
def __init__(self, _sock, app):
threading.Thread.__init__(self)
self.app = app
def run(self):
def close_tab():
print 'Terminating supply.'
new_supply.kill()
# Create new tab
self.tab = Frame(self.app)
self.tab.pack()
#self.tab.pack_forget() # <-- inserting this causes the app to hang
# Scrollbar
self.scrollbar = Scrollbar(self.tab)
self.scrollbar.pack(side=RIGHT, fill=Y)
# Text
self.text = Text(self.tab, yscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.text.yview)
self.text.pack(expand=YES, fill=BOTH)
# Close
self.button = Button(self.tab, text="CLOSE", command=close_tab)
self.button.pack(side=BOTTOM, fill=BOTH, expand=YES)
print 'Starting thread' , data[0]['job'] , data[0]['supply']['dir_name'] , self.getName()
logging.info(data[0])
new_supply = supply.supply(data, self.app, self.text)
new_supply.run()
print 'Closing Thread' , data[0]['job'] , data[0]['supply']['dir_name'] , self.getName()
main.py
class App(Tk):
def __init__(self, master=None):
Tk.__init__(self, master)
tab1 = Frame(self)
tab1.pack()
self.scrollbar = Scrollbar(tab1)
self.scrollbar.pack(side=RIGHT, fill=Y)
self.text1 = Text(tab1, yscrollcommand=self.scrollbar.set)
self.scrollbar.config(command=self.text1.yview)
self.text1.pack(expand=YES, fill=BOTH)
if __name__ == "__main__":
app = App()
server = server(app)
server.daemon = True
server.start()
app.mainloop()
I think this is what is causing the issue... If so is there an alternative?
Thank you in advance for any help.
pack_forget has no effect on whether you can insert into a text widget or not. There is not enough information in your question to give you an answer.
If you're using threads, is it possible that you are trying to insert text from within a thread? You can only call widget methods from the main thread. Though, typically the result of this is that the program crashes rather than hangs.
When a tkinter app hangs, that is sometimes a symptom of trying to use both pack and grid in the same container widget. Are you doing that?

Categories

Resources