I am using a python 2.7 tkinter gui on a raspberry pi to automate some material testing. For the testing, multiple samples must be tested and it takes time to swap out the samples. I want to prompt text saying something like "Please insert sample one then press enter on keyboard" and have the function pause until enter has been pressed. Instead of pressing enter I could also use a tkinter button. Any ideas without using external libraries? I have tried a while loop in which I try and exit the loop once a button is pressed, but since the loop is running the button does not register.
Sample code (removed lots of code and left what is relevant):
class App:
def __init__(self,master):
#self.WILTRON = Wiltron_54128A_GPIB()
self.var = tk.StringVar()
self.var.trace("w",self.getTest)
self.okbutton = tk.IntVar()
self.okbutton.trace("w",self.OKbutton)
frame = Frame(master)
frame.pack()
#Initial GUI values
self.var.set('Choose Test')
testChoices = ['TEST']
App.testOption = tk.OptionMenu(frame, self.var, *testChoices)
App.testOption.grid(row=0, column=0)
okButton = tk.Button(frame, text=" OK ", command=self.OKbutton).grid(row=2, column=1)
#Test routine functions
def getTest(self, *args):
test = self.var.get()
sf = "IC Network Analyzer"
root.title(sf)
#####
if test == "TEST":
sample1 = self.WILTRON.Sample_Data()
print 'Change out sample then press OK'
#This is where I need to pause until the next sample has been inserted
sample2 = self.WILTRON.Sample_Data()
#ect.
#####
def OKbutton(self):
#Whatever I need to do to make the button exit the pause
Here's a working example that uses a callback to start the test, and a callback to advance each sample.
import Tkinter as tk
class App:
def __init__(self, master):
self.master = root
self.frame = tk.Frame(self.master)
self.okLabel = tk.Label(self.frame, text="Change out sample then press OK")
self.okButton = tk.Button(self.frame, text=" OK ", command=self.nextSample)
self.var = tk.StringVar()
self.var.set('Choose Test')
self.var.trace("w",self.beginTest)
testChoices = ['TEST']
self.testOption = tk.OptionMenu(self.frame, self.var, *testChoices)
self.sampleNumber = 1
self.maxSamples = 5
self.testing = False
self.samples = []
self.updateGUI()
def testSample(self):
# Do whatever you need to do to test your sample
pass
def beginTest(self, *args): # This is called when the user clicks the OptionMenu to begin the test
self.testing = True
sf = "IC Network Analyzer"
self.master.title(sf)
self.testOption.config(state=tk.DISABLED)
self.okButton.config(state=tk.NORMAL)
self.okLabel.config(text="Ready first sample, then press OK")
self.updateGUI()
def nextSample(self): # This is called each time a new sample is tested.
if self.sampleNumber >= self.maxSamples: # If the maximum # of samples has been reached, end the test sequence
self.testing = False
self.sampleNumber = 1
self.testOption.config(state=tk.NORMAL)
self.okButton.config(state=tk.DISABLED)
# You'll want to save your sample data to a file or something here
self.samples = []
self.updateGUI()
else:
self.sampleNumber += 1
if self.var.get() == "TEST":
self.samples.append(self.WILTRON.Sample_Data())
self.okLabel.config(text="Switch to sample #"+str(self.sampleNumber)+" and press OK")
#At this point, the GUI will wait politely for you to push the okButton, before it runs this method again.
#ect.
#####
def updateGUI(self):
self.frame.grid()
self.testOption.grid(row=1, column=1)
if self.testing:
self.okLabel.grid(row=2, column=1)
self.okButton.grid(row=3, column=1)
else:
self.okLabel.grid_remove()
self.okButton.grid_remove()
self.master.update_idletasks()
root = tk.Tk()
a = App(root)
root.mainloop()
Use tkMessageBox
import Tkinter
import tkMessageBox
print "Sample #1"
tkMessageBox.showinfo("Message", "Insert sample and press OK")
print "Sample #2"
Related
I want to re-enable a button in other window, the idea is that in my principal window press a button and start the second window, in this moment this button will be disable, and once the second window will be close, the button will need to be enable again.
I am trying to use root.protocol("WN_DELET_WINDOW",self.on_closing) but when I closed that window nothing happens. Actually I put a print sentence trying to identify if the program enter on this part, but the result is the same Nothing happens.
class SerialTkWindow():
def __init__(self,lock) -> None:
lock.configure(state='disable')
root =Tk()
root.geometry('300x100')
root.title("Reset HU ?")
root.resizable(0,0)
#region Label
welcomeLbl = Label(root,text="Select COM Port",fg="black",bg=root["bg"],font="Helvetica 12")
welcomeLbl.pack()
#endregion
root.protocol("WN_DELET_WINDOW",self.on_closing)
root.mainloop()
def on_closing(self, lock):
print("Regresamos")
lock.configure(state=NORMAL)
lock is refered in SerialTkWindow(self.sendCanBtn) and self.sendCanBtn is refered in self.sendCanBtn = Button(frame3,text="Send CAN Msgs",command=self.CANMsgBtn)
Instead of using root.protocol("WN_DELETE_WINDOW",self.on_closing), you can call on_closing after the mainloop returns.
from tkinter import *
class SerialTkWindow:
def __init__(self, lock) -> None:
self.lock = lock
self.lock.configure(state='disable')
self.root = Tk()
self.root.geometry('300x100')
self.root.title("Reset HU ?")
self.root.resizable(0, 0)
# region Label
self.welcomeLbl = Label(self.root, text="Select COM Port", fg="black", bg=root["bg"], font="Helvetica 12")
self.welcomeLbl.pack()
# endregion
def on_closing(self):
print("Regresamos")
self.lock.configure(state=NORMAL)
root = Tk()
def open_serial_tk():
win = SerialTkWindow(winbutton)
while True:
try:
win.root.update()
win.root.update_idletasks()
except TclError:
win.on_closing()
break
winbutton = Button(root, text="open", command=open_serial_tk)
winbutton.pack()
root.mainloop()
I'm new to TKinter. I need to change the text of a button and its state when its clicked, then do some actions, and finally change again its text and state.
The problem is the changes only apply once the function has ended, skipping the first change of state and text. It never changes the Buttons text to "loading" and the button is never disabled.
Here is the code for the problem i'm experiencing:
#!/usr/bin/env python
import tkinter as tk
import time
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.pack(fill=tk.BOTH, expand=1)
self.create_widgets()
def create_widgets(self):
self.master.title("CW POS")
cierre = tk.Button(
self.master,
command=self.re_imprimir_ultimo_cierre)
cierre["text"] = "foo"
cierre.pack(fill=tk.BOTH, expand=1)
self._cierre = cierre
salir = tk.Button(self.master, text="quit", command=self.salir)
salir.pack(fill=tk.BOTH, expand=1)
def salir(self):
exit()
def re_imprimir_ultimo_cierre(self):
self._cierre["text"] = "Loading..."
self._cierre["state"] = tk.DISABLED
# TODO: magic
time.sleep(2)
self._cierre["text"] = "foo"
self._cierre["state"] = tk.NORMAL
root = tk.Tk()
root.geometry("240x180")
root.resizable(False, False)
app = Application(root)
root.mainloop()
How do I make the button show text="loading" and state=DISABLED, while the button is doing my calculations?
There is a pretty quick fix to this problem, you just need to update the button, once you change it's text to "Loading" (self._cierre["text"] = "Loading...")
def re_imprimir_ultimo_cierre(self):
self._cierre["text"] = "Loading..."
self._cierre["state"] = tk.DISABLED
self._cierre.update() # This is the line I added
# TODO: magic
time.sleep(2)
self._cierre["text"] = "foo"
self._cierre["state"] = tk.NORMAL
This just simply updates the buttons state after you change the text and state.
From what I understand this is because a button will run all the code within its command, before updating anything on the screen, so you essentially have to force the button to update itself within its command.
Hope this helps :)
I am learning GUI development using Tkinter. I want to show multiple messages on the label which I have stored in a string. I used sleep to view the changes.However only the last message string is shown at execution.
from tkinter import *
import time
master = Tk()
def onClick():
for i in range(0,len(list_of_str)):
w.configure(text=list_of_str[i])
time.sleep(5)
list_of_str = ['first','second','third','fourth','fifth']
w = Label(master, text="Hello, world!")
b = Button(master,text='Click me',command = onClick)
w.pack()
b.pack()
mainloop()
I am a noobie. So thanks for helping !
A simple solution to your problem is to use a combination of the try/except method and using after().
In tkinter sleep() will pause the application instead of providing a timer. For tkinter you want to use the after() method to scheduled an event after a set amount of time instead. The after() method is meant for this exact problem and is what you will always use in tkinter for a delayed event.
In my below example I modified your onClick function to take 1 argument and to use that in our after() method to select the next item in the list after 5 seconds. Note that for the after() method time is done in milliseconds so 5000 is 5 seconds.
from tkinter import *
master = Tk()
def onClick(ndex):
try:
w.configure(text=list_of_str[ndex])
master.after(5000, onClick, ndex+1)
except:
print("End of list")
list_of_str = ['first','second','third','fourth','fifth']
w = Label(master, text="Hello, world!")
b = Button(master,text='Click me',command = lambda: onClick(0))
w.pack()
b.pack()
mainloop()
I think you want this:
from tkinter import *
import time
master = Tk()
global i
i = 0
def onClick():
master.after(1, change)
def change():
global i
if i == len(list_of_str):
pass
else:
w.configure(text=list_of_str[i])
i += 1
master.after(1000, onClick)
list_of_str = ['first','second','third','fourth','fifth']
w = Label(master, text="Hello, world!")
b = Button(master,text='Click me',command = onClick)
w.pack()
b.pack()
mainloop()
time.sleep is a no-no in tkinter. I advise you make your gui in a class and it wil be easier.
example with class:
import tkinter as tk
from tkinter import *
class GUI:
def __init__(self, master):
self.list_of_str = ['first','second','third','fourth','fifth']
self.count = 0
self.master = master
self.w = Label(master, text="Hello, world!")
self.w.pack()
self.b = Button(master,text='Click me',command = self.onClick)
self.b.pack()
def onClick(self, event=None):
if self.count == len(self.list_of_str):
pass
else:
self.w.configure(text=self.list_of_str[self.count])
self.count += 1
self.master.after(1000, self.onClick)
def main():
root = tk.Tk()
app = GUI(root)
root.mainloop()
if __name__ == '__main__':
main()
I was writing a program with a start page, and two programs that are called from that start page. Both of the subprograms work by themselves. However, when I put them into my start page, the stopwatch timing label doesn't show up. If you are wondering, I put them into my program by doing:
import program
program.function()
Here is my start page program:
from Tkinter import *
class start_page:
def __init__(self,master):
self.master = master
self.frame = Frame(self.master)
self.countdown = Button(master, text = "Timer", command = self.c).pack()
self.stopwatch_butt = Button(master,text="Stopwatch",command=self.g).pack()
def g(self):
import stopwatch
stopwatch.f()
def c(self):
import timer_prog
timer_prog.timer()
self.master.after_cancel(timer_prog)
def main():
root = Tk()
s = start_page(root)
root.title("Timer Suite: Brian Ton")
root.mainloop()
main()
If I run this program, the timer program works fine, but the stopwatch doesn't show its label, only its buttons. I tried to clear all Tk after functions, and that didn't work, and I also tried to run the stopwatch program first, to no avail.
Here is my stopwatch program:
from Tkinter import *
import datetime
def s():
start.config(state='disabled')
stop.config(state="normal")
reset.config(state='disabled')
Start()
def Start():
if reset['state'] == 'disabled' and stop['state'] == 'normal':
hidden.set(str(int(hidden.get())+1))
root.update()
root.after(1000,Start)
curr = hidden.get()
g.set(str(datetime.timedelta(seconds=int(curr))))
print g.get()
else:
return None
def Stop():
start.config(state='disabled')
stop.config(state='disabled')
reset.config(state="normal")
def Reset():
start.config(state="normal")
stop.config(state="disabled")
reset.config(state='disabled')
hidden.set('0')
g.set(str(datetime.timedelta(seconds=0)))
def f():
global root,frame,master,hidden,g,timelabel,start,stop,reset
root = Tk()
frame = Frame(root)
master = root
hidden = StringVar()
g = StringVar()
hidden.set('0')
timelabel = Label(master,textvariable=g)
g.set(str(datetime.timedelta(seconds=int(0))))
timelabel.grid(row=1,column=2)
start = Button(master,text="Start",command = s,state="normal")
stop = Button(master,text="Stop",command = Stop,state = "disabled")
reset = Button(master,text="Reset",command = Reset,state = "disabled")
start.grid(row=2,column=1)
stop.grid(row=2,column=2)
reset.grid(row=2,column=3)
root.update()
root.mainloop()
And here is my timer program:
from Tkinter import *
import datetime
def get_seconds(h,m,s):
hr_sec = h * 3600
m_sec = m * 60
return hr_sec+m_sec+s
def timerstartstop():
hours = hour_entry.get()
minutes = minute_entry.get()
sec = second_entry.get()
if hours == "":
hours = 0
hour_entry.insert(0,"0")
if minutes == "":
minutes = 0
minute_entry.insert(0,"0")
if sec == "":
sec = 0
second_entry.insert(0,"0")
c = get_seconds(int(hours), int(minutes), int(sec))
global s
s = StringVar(master)
s.set(c)
if startstop['text'] == 'Stop':
global curr
curr = shown
s.set(-1)
if startstop['text'] == 'Reset':
startstop.config(text="Start")
s.set(c)
root.update()
shown.set(str(datetime.timedelta(seconds=int(s.get()))))
return None
countdown()
import winsound
def countdown():
startstop.config(text="Stop")
global shown
good = True
shown = StringVar(master)
shown.set(str(datetime.timedelta(seconds=int(s.get()))))
L = Label(master,textvariable=shown).grid(row=1,column=2)
if int(s.get()) == 0:
startstop.config(text="Reset")
while startstop['text'] != "Start":
root.update()
winsound.Beep(500,500)
elif int(s.get()) < 0:
good = False
shown.set(curr.get())
startstop.config(text="Reset")
else:
if good:
s.set(str(int(s.get())-1))
root.after(1000,countdown)
def ex():
root.after_cancel(countdown)
root.destroy()
def timer():
global root
global master
global frame
root = Tk()
master = root
frame = Frame(master)
global hour_entry
hour_entry = Entry(master,width=3)
hour_entry.grid(row=0,column=0)
colon_l = Label(master,text=':').grid(row=0,column=1)
global minute_entry
minute_entry = Entry(master,width=2)
minute_entry.grid(row=0,column=2)
colon_l2 = Label(master,text=':').grid(row=0,column=3)
global second_entry
second_entry = Entry(master,width=2)
second_entry.grid(row=0,column=4)
global startstop
startstop = Button(master,text="Start",command=timerstartstop)
e = Button(master,text="Exit",command=ex).grid(row=1,column=3)
startstop.grid(row=0,column=5)
root.mainloop()
In addition, I tried to run these two programs from a different starting menu that used the console, which worked.
The console program is:
import timer_prog
timer_prog.timer()
raw_input('next')
import stopwatch
stopwatch.f()
Attached are some screenshots of what the stopwatch program should look like vs what it does look like when called from the starting program.
Note: I can tell the program is running from the starting page, as it prints the current time each second. Also, I attached some screenshots
Stopwatch Program Run Directly
Stopwatch Program Run From The Start Page
Tkinter program should use only one Tk() - to create main window - and one mainloop() - to control all windows and widgets. If you use two Tk() and two mainloop() then it has problem - for example get()/set() may not work.
Subwindows should use Toplevel() instead of Tk().
Function which starts program (ie. run()) could run with parameter window (def run(window)) and then you can execute it as standalone program with
root = Tk()
run(root)
root.mainloop()
or after importing
run(Toplevel())
(without maniloop())
You can use if __name__ == "__main__" to recognize if program starts as standalone.
Example
main.py
from Tkinter import *
class StartPage:
def __init__(self, master):
self.master = master
master.title("Timer Suite: Brian Ton")
Button(master, text="Timer", command=self.run_timer).pack()
Button(master, text="Stopwatch", command=self.run_stopwatch).pack()
def run_stopwatch(self):
import stopwatch
window = Toplevel()
stopwatch.run(window)
def run_timer(self):
import timer_prog
window = Toplevel()
timer_prog.timer(window)
self.master.after_cancel(timer_prog)
def main():
root = Tk()
StartPage(root)
root.mainloop()
main()
stopwatch.py
from Tkinter import *
import datetime
def pre_start():
start_button.config(state='disabled')
stop_button.config(state='normal')
reset_button.config(state='disabled')
start()
def start():
global current_time
# stop_button['state'] can be 'normal' or 'active' so better use ` != 'disabled'`
if reset_button['state'] == 'disabled' and stop_button['state'] != 'disabled':
current_time += 1
time_var.set(str(datetime.timedelta(seconds=current_time)))
print(time_var.get())
master.after(1000, start)
def stop():
start_button.config(state='disabled')
stop_button.config(state='disabled')
reset_button.config(state='normal')
def reset():
global current_time
start_button.config(state='normal')
stop_button.config(state='disabled')
reset_button.config(state='disabled')
current_time = 0
time_var.set(str(datetime.timedelta(seconds=0)))
def run(window):
global master
global current_time, time_var
global start_button, stop_button, reset_button
master = window
current_time = 0
time_var = StringVar()
time_var.set(str(datetime.timedelta(seconds=0)))
time_label = Label(window, textvariable=time_var)
time_label.grid(row=1, column=2)
start_button = Button(master, text='Start', command=pre_start, state='normal')
stop_button = Button(master, text='Stop', command=stop, state='disabled')
reset_button = Button(master, text='Reset', command=reset, state='disabled')
start_button.grid(row=2, column=1)
stop_button.grid(row=2, column=2)
reset_button.grid(row=2, column=3)
if __name__ == '__main__':
# it runs only in standalone program
root = Tk()
run(root)
root.mainloop()
I am trying to retrieve the word before everytime space is entered. For example, If a user types " iam a" i want to retrieve "iam" and then if user types "iam a girl" i want to retrieve "a" .
Following is my code:
import tkinter as tk
import time
class ExampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
lsum = tk.Label(self,borderwidth=2,text="suggestions")
lsum.grid(row=2, column=5, sticky=tk.W)
def func3():
contents = self.text.get("1.0", tk.END)
lsum["text"] = contents
def func4():
self.text.bind("<space>",func3())
self.text= tk.Text(self, height=5, width=30,borderwidth=2)
self.text.pack(side=tk.RIGHT)
self.text.grid(row=0, column=4)
self.text.insert(tk.END, "")
self.v = tk.IntVar()
self.d = tk.IntVar()
self.e = tk.IntVar()
self.radio1=tk.Radiobutton(self, text="آيک گرآم مآڈل ", variable=self.v, value=1,command=func2).grid(column=0,row=1,columnspan=2)
self.radio2=tk.Radiobutton(self, text="دو گرآم مآڈل ", variable=self.d, value=2,command=func3).grid(column=0,row=2,columnspan=2)
self.radio3=tk.Radiobutton(self, text="تین گرآم مآڈل", variable=self.e, value=3).grid(column=0,row=3,columnspan=2)
if __name__ == '__main__':
run = ExampleApp()
run.mainloop()
Kindly Help please.
Assuming that I understand what you're asking (which is tricky enough) you can call .bind() on the Text widget with the event being "<space>".
This would look something like the below:
from tkinter import *
root = Tk()
text = Text(root)
text.pack()
def callback(*args):
print(text.get("1.0", END).split(" ")[len(text.get("1.0", END).split(" "))-1])
text.bind("<space>", callback)
root.mainloop()
This means that everytime the space key is pressed in the Text widget we get a callback which prints the word you're looking for in the widget.