Passing text from a function to a gui - python-2.7

I've got a GUI function in a file 'test_gui.py', which is adapted from one of Bryan Oakley's answers to a question regarding getting text from a Tkinter entry box.
import sys
import os
import Tkinter as tk
class testing(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.label1 = tk.Label(self, text = "Enter benchmark version")
self.label2 = tk.Label(self, text = "Enter test_suite (a for all)")
self.label3 = tk.Label(self, text = "Enter sub_suite t or w")
self.entry1 = tk.Entry(self)
self.entry2 = tk.Entry(self)
self.entry3 = tk.Entry(self)
self.button = tk.Button(self, text="Get", command=self.on_button)
self.button.grid(row = 4, column = 0)
self.label1.grid(row = 1, column = 0)
self.label2.grid(row = 2, column = 0)
self.label3.grid(row = 3, column = 0)
self.entry1.grid(row = 1, column = 1)
self.entry2.grid(row = 2, column = 1)
self.entry3.grid(row = 3, column = 1)
def on_button(self):
benchmark = self.entry1.get()
test_suite = self.entry2.get()
sub_suite = self.entry3.get()
home_path=os.path.dirname(os.path.abspath(__file__))
path = os.path.join(home_path, sub_suite, 'results')
sys.path.insert(0, path)
import compare_data as compare
compare.compare_results(benchmark, test_suite)
self.label4 = tk.Label(self, text=fil)
self.button2.grid(row = 5, column = 10)
app = testing()
app.mainloop()
and I need to pass it 'fil' from a different function which is run after pressing button through the function compare_results. In this function I've got:
import test_gui
test_gui.testing(fil)
To do this I think I need to define on_button as
def on_button(self, fil)
But then this returns the error that on_button requires two arguments. If I give fil a default value it will pass that to the label on pressing the button.
Is there a way of passing text from a function run through a gui back to the gui?

You can use lambda to pass more arguments to the button command.
So instead of
command = self.on_button
You could use
command = lambda: self.on_button(fil)
To pass 'fil' to the on_button function.
Is this what you had in mind?

Related

Tkinter assign buttons to entries in dynamically created widgets

How can I access an Entry content with pressing the corresponding Button in dynamically created widgets?
Below is the best I come up with so far. Thank you for any help.
from Tkinter import *
class App(object):
def __init__(self, master):
self.master = master
self.mf = Frame(self.master)
self.l = ["white", "red", "blue", "brown"]
self.font = ("Arial", 30)
self.c, self.r = 1, 0
self.cc, self.rr = 0, 0
self.bel = []
for c in self.l:
action = self.print_entry
self.e = Entry(self.mf, bg=c, width=10, font=self.font)
self.e.grid(row=self.r, column=self.c)
self.b = Button(self.mf, bg=c, text=c, font=self.font)
self.b.grid(row=self.rr, column=self.cc)
self.b.config(command=action)
self.bel.append((self.b, self.e))
self.rr += 1
self.r += 1
self.mf.pack()
def print_entry(self): # this function prints the content of the entry
pass
def main():
root = Tk()
display = App(root)
root.mainloop()
if __name__=="__main__":
main()
You can pass a reference to the entry widget into the command, using lambda or functools.partial. For example:
self.b.config(command= lambda entry=self.e: action(entry))
...
def print_entry(self, entry):
print("the entry is '%s'" % entry.get())
By the way, using self.b and self.e is pointless, since those variables will only ever hold references to the last button and last entry. You should either use a local variable, and/or append the values to a list.

Cannot populate radio button correctly - Tkinter

I am trying out few things with Tkinter as an exercise.
In my example app i want user to select one option from drop down list. Based on this selection i want to populate the list of radio button. I have created a list of values to populate in dropdown list and a dictionary for radio button. please check the code.
See below some working and not working examples:
As you can see from the picture. The first window works well. Second also. However the third window is not quite correct. The option '410' remains. I am making subsequent selections without closing the application.
I think perhaps i am not declaring the variables at the right place. It would be very helpful if some one can have a look at the code and rectify it.
Code:
from Tkinter import *
import ttk
class App(Frame):
def __init__(self,parent):
Frame.__init__(self)
self.parent = parent
self.v = IntVar()
#self.radio_value = []
#self.i = 0
self.GUI()
def GUI(self):
self.master.title('Example')
self.pack(fill = BOTH, expand = 1)
self.options = ['a1','a2','a3','a4','a5']
self.box_value = StringVar()
self.box = ttk.Combobox( self, textvariable=self.box_value)
self.box.bind("<<ComboboxSelected>>", self.set_Radio)
self.box['values'] = self.options
self.box.current(0)
self.box.grid(row = 0, column = 0 )
self.choices = {'a1':['30', '70', '140', '410'], 'a2': ['a', 'b', 'c'], 'a3': ['x', 'y', 'z'], 'a4':['p', 'q', 'r'], 'a5': ['l', 'm', 'n']}
def set_Radio(self,parent):
i = 0
radio_value = []
if self.box_value.get() in self.choices.keys():
radio_value = self.choices[self.box_value.get()]
print radio_value
for t in radio_value:
i = i+1
b = Radiobutton(self, text=t, variable=self.v, value=t)
b.grid(row = i, column = 0)
def main():
root = Tk()
root.geometry('250x250')
app1= App(root)
root.mainloop()
if __name__ == '__main__':
main()
The problem is that you don't delete the old radiobuttons before creating the new radiobuttons. One solution is to put them in an invisible frame. When you delete the frame, the radiobuttons will automatically be destroyed. Another solution is to keep a reference to them so that you can destroy them individually later.
Here's an example of keeping a reference:
def __init-_(self, parent):
...
self.radios = []
def set_Radio(self,parent):
for widget in self.radios:
widget.destroy()
self.radios = []
...
for t in radio_value:
...
b = Radiobutton(...)
self.radios.append(b)
...

Using Python Tkinter .config() method

I am trying to use the Python Tkinter .config() method to update some message text. I can't get it to work. What might I be doing wrong (see the update_message method):
#!/usr/bin/python
import alsaaudio as aa
import audioop
import Tkinter as tk
import tkFont
import threading
import Queue
# styles
BACKROUND_COLOR = '#000000'
TYPEFACE = 'Unit-Bold'
FONT_SIZE = 50
TEXT_COLOR = '#777777'
TEXTBOX_WIDTH = 400
# text
TITLE = 'listen closely'
SCORE_MESSAGE = 'your score:\n '
END_MESSAGE = 'too loud!\ntry again'
# configuration
DEVICE = 'hw:1' # hardware sound card index
CHANNELS = 1
SAMPLE_RATE = 8000 # Hz // 44100
PERIOD = 256 # Frames // 256
FORMAT = aa.PCM_FORMAT_S8 # Sound format
NOISE_THRESHOLD = 3
class Display(object):
def __init__(self, parent, queue):
self.parent = parent
self.queue = queue
self._geom = '200x200+0+0'
parent.geometry("{0}x{1}+0+0".format(
parent.winfo_screenwidth(), parent.winfo_screenheight()))
parent.overrideredirect(1)
parent.title(TITLE)
parent.configure(background=BACKROUND_COLOR)
parent.displayFont = tkFont.Font(family=TYPEFACE, size=FONT_SIZE)
self.process_queue()
def process_queue(self):
try:
score = self.queue.get(0)
self.print_message(score)
except Queue.Empty:
pass
self.parent.after(100, self.update_queue)
def update_queue(self):
try:
score = self.queue.get(0)
self.update_message(score)
except Queue.Empty:
pass
self.parent.after(100, self.update_queue)
def print_message(self, messageString):
print 'message', messageString
displayString = SCORE_MESSAGE + str(messageString)
self.message = tk.Message(
self.parent, text=displayString, bg=BACKROUND_COLOR,
font=self.parent.displayFont, fg=TEXT_COLOR, width=TEXTBOX_WIDTH, justify="c")
self.message.place(relx=.5, rely=.5, anchor="c")
def update_message(self, messageString):
print 'message', messageString
displayString = SCORE_MESSAGE + str(messageString)
self.message.config(text=displayString)
def setup_audio(queue, stop_event):
data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NONBLOCK, 'hw:1')
data_in.setchannels(2)
data_in.setrate(44100)
data_in.setformat(aa.PCM_FORMAT_S16_LE)
data_in.setperiodsize(256)
while not stop_event.is_set():
# Read data from device
l, data = data_in.read()
if l:
# catch frame error
try:
max_vol = audioop.rms(data, 2)
scaled_vol = max_vol // 4680
print scaled_vol
if scaled_vol <= 3:
# Too quiet, ignore
continue
queue.put(scaled_vol)
except audioop.error, e:
if e.message != "not a whole number of frames":
raise e
def main():
root = tk.Tk()
queue = Queue.Queue()
window = Display(root, queue)
stop_event = threading.Event()
audio_thread = threading.Thread(target=setup_audio,
args=[queue, stop_event])
audio_thread.start()
try:
root.mainloop()
finally:
stop_event.set()
audio_thread.join()
pass
if __name__ == '__main__':
main()
I don't want to be laying down a new message every time I update. If the .config() doesn't work, is there another method to update the text configuration of the message?
I would use string variables, first create your string variable then set it to want you want it to display at the start next make your object and in text put the sting variable then when you want to change the text in the object change the string variable.
self.messaget = StringVar()
self.messaget.set("")
self.message = tk.Message(
self.parent, textvariable=self.messaget, bg=BACKROUND_COLOR,
font=self.parent.displayFont, fg=TEXT_COLOR,
width=TEXTBOX_WIDTH, justify="c").grid()
#note renember to palce the object after you have created it either using
#.grid(row = , column =) or .pack()
#note that it is textvariable instead of text if you put text instead it will run but
#but will show PY_Var instead of the value of the variable
edit
to change the text without recreating the object you do the name of the string variable you have used and .set
self.messaget.set("hi")

Setting and Retrieving Data with TkInter

I just ran into some strange behavior that has me stumped. I'm writing a simple little GUI for some in-house data processing. I want to allow a user to switch between a few different data-processing modes and input some parameters which define how the data is processed for each mode. The problem is that when the user inputs new parameters, the app ignores requests to switch modes.
The code below replicates the issue. I apologize for the size, this was the shortest code that replicates the problem.
import Tkinter as Tk
class foo(Tk.Frame):
def __init__(self):
self.master = master =Tk.Tk()
Tk.Frame.__init__(self,self.master) #Bootstrap
#Here mode and parameters as key, value pairs
self.data = {'a':'Yay',
'b':'Boo'
}
self.tex = Tk.Text(master=master)
self.tex.grid(row=0,column=0,rowspan=3,columnspan=4)
self.e = Tk.Entry(master=master)
self.e.grid(row=3,column=0,columnspan=4)
self.sv =Tk.StringVar()
self.sv.set('a')
self.b1 = Tk.Radiobutton(master=master,
text = 'a',
indicatoron = 0,
variable = self.sv,
value = 'a')
self.b2 = Tk.Radiobutton(master=master,
text = 'b',
indicatoron = 0,
variable = self.sv,
value = 'b')
self.b3 = Tk.Button(master = master,
text='Apply',command=self.Apply_Func)
self.b4 = Tk.Button(master = master,
text='Print',command=self.Print_Func)
self.b1.grid(row=4,column=0)
self.b2.grid(row=4,column=1)
self.b3.grid(row=4,column=2)
self.b4.grid(row=4,column=3)
def Apply_Func(self):
self.innerdata = self.e.get()
def Print_Func(self):
self.tex.insert(Tk.END,str(self.innerdata)+'\n')
#This is how I'm retrieving the user selected parameters
#property
def innerdata(self):
return self.data[self.sv.get()]
#And how I'm setting the user defined parameters
#innerdata.setter
def innerdata(self,value):
self.data[self.sv.get()] = value
if __name__ == "__main__":
app = foo()
app.mainloop()
Expected behavior:
1) Press button 'a' then 'print' prints:
Yay
2) Press button 'b' then 'print' prints:
Boo
3) Type 'Zep Rocks' into the entry field and press apply
4) Pressing 'print' now yields
Zep Rocks
5) Pressing 'a' then 'print' should yield
Yay
But instead yields
Zep Rocks
Which might be true, but not desired right now. What is going on here?
Edit: I have some new information. Tk.Frame in python 2.7 is not a new-style class. It isn't friendly with descriptors, so rather than interpreting the '=' as a request to use the foo.innerdata's __set__ method, it just assigns the result of self.e.get() to innerdata.
ARGLEBARGLE!!!
Still an open question: how do I get this to do what I want in a clean manner?
So the core problem is that Tk.Frame doesn't subclass from object, so it is not a new-style python class. Which means it doesn't get down with descriptors like I was trying to use. One solution that I found is to subclass my app from object instead.
Code that solves my problem is below:
import Tkinter as Tk
class foo(object):
def __init__(self,master):
self.master = master #Bootstrap
self.mainloop = master.mainloop
self.data = {'a':{'value':7,'metavalue':False},
'b':{'value':'Beeswax','metavalue':True}
}
self.tex = Tk.Text(master=master)
self.tex.grid(row=0,column=0,rowspan=3,columnspan=4)
self.e = Tk.Entry(master=master)
self.e.grid(row=3,column=0,columnspan=4)
self.sv =Tk.StringVar()
self.sv.set('a')
self.b1 = Tk.Radiobutton(master=master,
text = 'a',
indicatoron = 0,
variable = self.sv,
value = 'a')
self.b2 = Tk.Radiobutton(master=master,
text = 'b',
indicatoron = 0,
variable = self.sv,
value = 'b')
self.b3 = Tk.Button(master = master,text='Apply',command=self.Apply_Func)
self.b4 = Tk.Button(master = master,text='Print',command=self.Print_Func)
self.b1.grid(row=4,column=0)
self.b2.grid(row=4,column=1)
self.b3.grid(row=4,column=2)
self.b4.grid(row=4,column=3)
def Apply_Func(self):
self.innerdata = self.e.get()
def Print_Func(self):
self.tex.insert(Tk.END,str(self.innerdata)+'\n')
#property
def innerdata(self):
return self.data[self.sv.get()]
#innerdata.setter
def innerdata(self,value):
self.data[self.sv.get()] = value
if __name__ == "__main__":
master = Tk.Tk()
app = foo(master)
app.mainloop()

combobox value is not selecting in python when it is created dynamically

I created a Tkinter window Form, in that based on user selection i dynamically replace the combobox with deleting the Exist one.But the Problem is when i do like this the value selected in combobox is not updated.It is always displaying the default vale. see the following code
from Tkinter import *
import ttk
final=[]
field_0=['1','0']
field_1=['1','2','23','45','6']
field_2=['2','5','7','8','9']
class header:
def __init__(self,root):
self.parent=root
self.row_number= 1
self.value=0
root.title(" Select fields ")
root.minsize(width=450, height=530)
root.maxsize(width=450, height=550)
frame=Frame(root,height=20,relief=FLAT)
frame.grid(row=0,column=0,sticky=W)
Label(frame,text="Enter value:").grid(row=0,column=0,padx=70,pady=5,sticky=W)
entryValue=StringVar()
port_e = Entry(root,width=5,textvariable=entryValue)
port_e.delete(0,END)
port_e.insert(0,'0')
port_e.grid(row=0,column=0,padx=100,pady=5)
self.value=self.value+1
self.change_row=self.row_number
self.combocreate(self.change_row,root,field_0)
self.change_row=self.change_row+1
self.combocreate(self.change_row,root,field_1)
def combocreate(self,row_number,msg_frame,field):
comboBoxValue = [] # 'request'command for sink only
self.box_value=StringVar()
self.combo=ttk.Combobox(root,textvariable=self.box_value,state='readonly')
self.combo['values'] = tuple(field)
self.combo.set(field[1])
self.combo.grid(row = row_number, column = 1)
self.combo.bind("<<ComboboxSelected>>",self.selected_field)
final.append(self.combo)
def selected_field(self,event):
global cnt_sel
print final[0].get()
if(final[0].get()=='1'):
self.control=Label(root,text="Choose one type").grid(row=self.change_row,column=0,padx=20,pady=5,sticky=W)
self.combocreate(self.change_row,root,field_1)
final[1]=final[2] #replacing combobox dynamically based on selection
del final[2]
elif(final[0].get()=='0'):
Label(root,text="Choose zerotype ").grid(row=self.change_row,column=0,padx=20,pady=5,sticky=W)
self.combocreate(self.change_row,root,field_2)
final[1]=final[2]
del final[2]
else:
pass
if __name__=="__main__":
root=Tk()
app=header(root)
root.mainloop()
The problem with your code is that every time combocreate is called, it automatically binds the Combobox to self.selected_field, so whenever you change the value of the type Combobox, you call self.selected_field, and it automatically resets the type to the default. To fix the problem, you should only bind the first Combobox. This should fix the problem:
Replace
self.change_row=self.row_number
self.combocreate(self.change_row,root,field_0)
self.change_row=self.change_row+1
self.combocreate(self.change_row,root,field_1)
With
self.change_row=self.row_number
self.combocreate(self.change_row,root,field_0)
final[0].bind("<<ComboboxSelected>>",self.selected_field)
self.change_row=self.change_row+1
self.combocreate(self.change_row,root,field_1)
And
def combocreate(self,row_number,msg_frame,field):
comboBoxValue = [] # 'request'command for sink only
self.box_value=StringVar()
self.combo=ttk.Combobox(root,textvariable=self.box_value,state='readonly')
self.combo['values'] = tuple(field)
self.combo.set(field[1])
self.combo.grid(row = row_number, column = 1)
self.combo.bind("<<ComboboxSelected>>",self.selected_field)
final.append(self.combo)
With
def combocreate(self,row_number,msg_frame,field):
comboBoxValue = [] # 'request'command for sink only
self.box_value=StringVar()
self.combo=ttk.Combobox(root,textvariable=self.box_value,state='readonly')
self.combo['values'] = tuple(field)
self.combo.set(field[1])
self.combo.grid(row = row_number, column = 1)
final.append(self.combo)