I've been creating a program for myself and my company recently wants to use it. However the end-users have no python experience so I'm making a GUI for them using EasyGUI. All they have to do is click a shortcut on their desktop (I'm using pythonw.exe so no box shows up). The Process takes roughly 10 seconds to run but there is a blank screen when doing so.
My question is: Can i have a message box that says "Running..." while the function runs and then close when the entire process completes?
Bonus points: to have a progress bar while process runs.
Now I've done some searching but some of this stuff is over my head (I'm fairly new to Python). I'm not sure how to incorporate these parts into my code. Is there anything that is easy like EasyGUI to solve my problem? Thanks!
Related posts:
Python- Displaying a message box that can be closed in the code (no user intervention)
How to pop up a message while processing - python
Python to print out status bar and percentage
If you absolutely need to see my code i can try and re-create it without giving away information. The higher-ups would appreciate me not giving away information about this project - Security is tight here.
I've written a little demo for you. Don't know if it's exactly what you wanted...
The code uses threading to update the progressbar while doing other stuff.
import time
import threading
try:
import Tkinter as tkinter
import ttk
except ImportError:
import tkinter
from tkinter import ttk
class GUI(object):
def __init__(self):
self.root = tkinter.Tk()
self.progbar = ttk.Progressbar(self.root)
self.progbar.config(maximum=10, mode='determinate')
self.progbar.pack()
self.i = 0
self.b_start = ttk.Button(self.root, text='Start')
self.b_start['command'] = self.start_thread
self.b_start.pack()
def start_thread(self):
self.b_start['state'] = 'disable'
self.work_thread = threading.Thread(target=work)
self.work_thread.start()
self.root.after(50, self.check_thread)
self.root.after(50, self.update)
def check_thread(self):
if self.work_thread.is_alive():
self.root.after(50, self.check_thread)
else:
self.root.destroy()
def update(self):
#Updates the progressbar
self.progbar["value"] = self.i
if self.work_thread.is_alive():
self.root.after(50, self.update)#method is called all 50ms
gui = GUI()
def work():
#Do your work :D
for i in range(11):
gui.i = i
time.sleep(0.1)
gui.root.mainloop()
Let me know if that helps :)
Related
Why does in the code below button1 hang until the time.sleep(10) has completed.
I can only assume tKinter is waiting for the click event to finish before updating it's paint function.
I want on button1 click the state to change to DISABLED as in the code straight away, not when mainformbutton1press() has finished.
I have put time.sleep(10) to mimic rest of code functions - but the actual programme will be many minutes instead.
EDIT! - sleep is just there to show how tkinter hangs. My real programme has lots more code and no sleep function - and it takes a long time to process data with the hung GUI as mentioned. No more sleep suggestions please :)
import tkinter as tk
from tkinter import ttk
from tkinter.constants import DISABLED, NORMAL
import time
# ==================================================
class App:
def __init__(self, tk, my_w):
self.button1 = tk.Button(my_w, text="START", width=34, command = self.mainformbutton1press)
self.button1.grid(columnspan=3, row=6, column=1,padx=10,pady=20, ipadx=20, ipady=20)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def mainformbutton1press(self):
self.button1.config(text="PLEASE WAIT...")
self.button1['state'] = DISABLED
# DO REST OF PROCESSING
# TO MIMIC THIS:
time.sleep(10)
print("doing...")
# ==================================================
if __name__ == "__main__":
my_w = tk.Tk()
my_w.geometry("430x380")
my_w.resizable(False, False)
app = App(tk, my_w)
my_w.mainloop() # Keep the window open
Tk.mainloop is a sort of while loop. time.sleep() stops the loop for a particular period of time. That makes the window unresponsive. You might use .after function:
class App:
def __init__(self, tk, my_w):
self.my_w=my_w
....
def continue_next(self):
print("Doing")
....
def mainformbutton1press(self):
self.button1.config(text="PLEASE WAIT...")
self.button1['state'] = DISABLED
# DO REST OF PROCESSING
# TO MIMIC THIS:
self.my_w.after(10000,self.continue_next)
The only change you need to make to your code is to insert an update to your button.
The 10 second delay might need to be shortened (10 seconds is a long time to wait)
self.button1.config(text="PLEASE WAIT...")
self.button1['state'] = DISABLED
# INSERT UPDATE HERE
self.button1.update()
# DO REST OF PROCESSING
# TO MIMIC THIS:
time.sleep(1)
print("doing...")
I'm working on a tkinter GUI in Python to produce error messages in a new window. When running the code as shown below, the error noise plays, then it pauses for several seconds before opening the window. If I comment out the line with winsound, it opens it just fine.
import tkinter as tk
import winsound
class Error_Window:
def __init__(self, txt):
self.root = tk.Tk()
self.root.title("Error")
self.lbl = tk.Label(self.root, text=txt)
self.lbl.pack()
winsound.PlaySound("SystemExit", winsound.SND_ALIAS)
self.root.mainloop()
I suspect that it may be due to the error noise playing in full before reaching the mainloop command. One solution to this could be running the sound in a separate thread, but I've heard multithreading with tkinter should be avoided. Any tips on getting it to open smoothly at the same time as the noise is played?
Try this, the reason why it does that is the whole program is should we say in ONE THREAD/ MAIN THREAD so it would do first or execute first the sound then pop up the window. I think there's no problem with working with threads in tkinter just like what #jasonharper said
import tkinter as tk
import winsound
import threading
class Error_Window:
def __init__(self, txt):
self.root = tk.Tk()
self.root.title("Error")
self.lbl = tk.Label(self.root, text=txt)
th = threading.Thread(target=self.__play_sound,args=[])
th.start()
self.lbl.pack()
self.root.mainloop()
def __play_sound(self):
winsound.PlaySound("SystemExit", winsound.SND_ALIAS)
Error_Window("Hi")
I decided I want to learn how to make GUIs with something more than entry fields and buttons, so I'm starting off with tabs. After a little bit of research I made myself a program, but don't know why it doesn't work.
# --IMPORTS--
from tkinter import *
import tkinter.ttk as ttk
import time
# --CLASSES--
class Gui:
def __init__(self):
self.root = Tk()
self.root.title("tab test")
def setup(self):
# tabs
tabc = ttk.Notebook(self.root)
tab1 = ttk.Frame(tabc)
tabc.add(tab1, text="test 1")
tabc.grid()
def run(self):
self.root.mainloop()
# --MAIN--
if __name__ == "__main__":
gui = Gui()
gui.run()
When I run the program I just get a blank screen (see screenshot) and there is no way to see if there is a tab, let alone which one is selected.
Like I said, I don't see the reason why it isn't working. There are no error messages to point me in the right direction and I'm not 100% sure on how tabs work to begin with, so I thought I'd ask here. I've tried changing .grid() to .pack() but I think it's more of an error on my end than a bug with tkinter. Thanks in advance!
you have to run your setup method.
# --MAIN--
if __name__ == "__main__":
gui = Gui()
gui.setup()
gui.run()
sorry please bear with me i'm struggling. i've searched and i dont fully understand where i'm going wrong even though there are already posts covering the exact subject.
The short story is im running a process and is it runs through said process i want the text to change to red from black as it runs through. Now the way i am trying to do this is from outside of the classs.
If i can draw your attention to def C():
once the program has started from the start button, variable start is set to 1. i want the text in Application.createWidgets() under message statusStartTk to change to red. I am trying to do this in C() with Application.statusStartTk.config(fg='red')
Now its probably obvious to you clever guys where i'm going wrong and would appreciate being put right. i've spent a few hours trying different things and searching to no avail. You can probably see where i've commented bits and bobs out where i've been trying things.
Also i'd just like to say i appreciate the very fast and helpful responses you guys are coming back with so far, so thank you, again, and in anticipation.
anyway, here's the code:
import time
from tkinter import *
import tkinter as Tk
import tkinter
#sets up Tkinter GuI
root = Tk.Tk()
#Set Adjustable Variables for GUI
startText=StringVar()
startText.set("Start Brew!")
def a():
#print('a')
print('start = ', start)
def b():
print('b')
def c(): #GUI ASSISTANCE STATUS WINDOW
print('c')
if start==1:
Application.statusStartTk.config(fg='red')
def main():
a()
b()
c()
class Application(Tk.Frame):
def __init__(self, master=None):
Tk.Frame.__init__(self, master)
self.createWidgets()
def createWidgets(self):
#Set start buttons and sink and disable if brew started
self.startButton = Tk.Button(root, textvariable=startText, command=Application.startBrew)
self.startButton.place(x=902,y=60)
#Plant Status List
statusTitle = "Plant Status Update:"
statusTitleTk = Message(root,width=200,font=("Tk Default Font",16), text=statusTitle)
statusTitleTk.place(x=1090,y=40)
statusStart = "- Brew Started"
statusStartTk = Message(root,width=200, text=statusStart)
statusStartTk.place(x=1100,y=70)
self.onUpdate()
def startBrew():
print('brew has started')
global start,startButton
start=1
startText.set("Brew Started")
Application()
def onUpdate(self):
root.update_idletasks()
main()
#str(variable1.get())
self.after(5, self.onUpdate)
app = Application(master=root)
root.mainloop()
I'm using the slider to update my visualization, but the command updateValue is sent everytime I move the slider thumb, even for intermediate values.
Instead I want to trigger it only when I release the mouse button and the interaction is complete.
self.slider = tk.Scale(self.leftFrame, from_=0, to=256, orient=tk.HORIZONTAL, command=updateValue)
How can I trigger the function only once, when the interaction is ended ?
This is quite an ancient question now, but in case anyone stumbles upon this particular problem just use the bind() function and the "ButtonRelease-1" event like so:
import Tkinter as tk
class App:
def __init__(self):
self.root = tk.Tk()
self.slider = tk.Scale(self.root, from_=0, to=256,
orient="horizontal")
self.slider.bind("<ButtonRelease-1>", self.updateValue)
self.slider.pack()
self.root.mainloop()
def updateValue(self, event):
print self.slider.get()
app=App()
Hope this helps anyone!
You can't.
What you can do instead is have your command delay any real work for a short period of time using 'after'. Each time your command is called, cancel any pending work and reschedule the work. Depending on what your actual requirements are, a half second delay might be sufficient.
Another choice is to not use the built-in command feature and instead use custom bindings. This can be a lot of work to get exactly right, but if you really need fine grained control you can do it. Don't forget that one can interact with the widget using the keyboard in addition to the mouse.
Here's a short example showing how to schedule the work to be done in half a second:
import Tkinter as tk
#create window & frames
class App:
def __init__(self):
self.root = tk.Tk()
self._job = None
self.slider = tk.Scale(self.root, from_=0, to=256,
orient="horizontal",
command=self.updateValue)
self.slider.pack()
self.root.mainloop()
def updateValue(self, event):
if self._job:
self.root.after_cancel(self._job)
self._job = self.root.after(500, self._do_something)
def _do_something(self):
self._job = None
print "new value:", self.slider.get()
app=App()