I've been attempting to make a simple GUI, and have been working my way through tkinter's various functions. However, I can't for the life of me figure out why this doesn't work.
from tkinter import Tk, Label, Button, Radiobutton,IntVar,StringVar
class TestGUI:
def __init__(self, master):
self.master = master
master.title("Test GUI")
self.mode = IntVar()
self.modetext = StringVar()
self.modetext.set("What does this do?")
self.modelabel = Label(master,textvariable=self.modetext)
self.modelabel.pack()
self.close_button = Button(master, text="Close", command=master.destroy)
self.close_button.pack()
R1 = Radiobutton(master, text="Mode 1", variable=self.mode, value=0, command=self.modeset)
R1.pack()
R2 = Radiobutton(master, text="Mode 2", variable=self.mode, value=1, command=self.modeset)
R2.pack()
R3 = Radiobutton(master, text="Mode 3", variable=self.mode, value=2, command=self.modeset)
R3.pack()
def modeset(self):
self.modetext.set("Mode is " + str(self.mode.get()))
print(self.mode.get())
root = Tk()
T_GUI = TestGUI(root)
root.mainloop()
What it should do, as far as I can tell, is display three radio buttons which set the value of mode, and display "Mode is [mode]" in the label and print the value of mode when one is selected.
Instead, the label is never displayed and choosing a radio button doesn't change the value of mode.
Can anyone give me a clue on what is happening?
Looking at your explanation, StringVar and IntVar are causing problems here.
Specifying master on them should solve your issue.
self.mode = IntVar(master=self.master)
self.modetext = StringVar(master=self.master)
Something to do with Pyzo probably because on most IDEs, omitting master don't cause any problems.
Related
I am currently writing an application that uses Tkinter to provide a graphical interface for the user.
The app has been working very well and recently I decided to add some checkboxes, the idea being that when the user checks one of the boxes, another set of text is sent through an API.
I have input boxes that I am able to get to work perfectly, however for some reason whenever I try and retrieve the value of the checkbox, I get the following error:
if check.get():
NameError: name 'check' is not defined
For the life of me I can't figure out why this error is popping up, here is the rest of my code, to make it clearer I have removed the working code for the input boxes.
from tkinter import *
class GUI:
def __init__(self, master):
check = IntVar()
self.e = Checkbutton(root, text="check me", variable=check)
self.e.grid(row=4, column=2)
self.macro_button = Button(master, text="Test Button", command=self.test)
self.macro_button.grid(row=11, column=1)
def test(self):
if check.get():
print('its on')
else:
print('its off')
root = Tk()
root.resizable(width=False, height=False)
my_gui = GUI(root)
root.mainloop()
When I run this code, and press the button labeled 'test button', that is when the error appears in my terminal.
Anyone have any idea why this is happening for my checkboxes, and not for my inputboxes?
EDIT:
what's even odder for me is that this code that I found online made to teach you how to use a tkinter checkbox works like a charm, and its almost identical to mine:
import tkinter as tk
root = tk.Tk()
var = tk.IntVar()
cb = tk.Checkbutton(root, text="the lights are on", variable=var)
cb.pack()
def showstate():
if var.get():
print ("the lights are on")
else:
print ("the lights are off")
button = tk.Button(root, text="show state", command=showstate)
button.pack()
root.mainloop()
You just need to make check an instance variable with self.
I.E
class GUI:
def __init__(self, master):
self.check = IntVar()
self.e = Checkbutton(root, text="check me", variable=self.check)
self.e.grid(row=4, column=2)
self.macro_button = Button(master, text="Test Button", command=self.test)
self.macro_button.grid(row=11, column=1)
def test(self):
if self.check.get():
print('its on')
else:
print('its off')
root = Tk()
root.resizable(width=False, height=False)
my_gui = GUI(root)
root.mainloop()
The example you found online is written in an 'inline' style - which is fine until your GUI becomes larger and you require many methods and variables to be used / passed.
I use Tkinter for make a GUI. I have a window with 2 radiobutton ('Yes' and 'No'), but when I select one, it don't run the script :
root = Tk()
Button(root, text='TEST', command=root.quit).pack()
root.mainloop()
master = Tk()
v = IntVar()
Radiobutton(master, text='Yes', variable=v, value=0).pack()
Radiobutton(master, text='No', variable=v, value=1).pack()
Button(master, text='Exit', command=master.quit).pack()
master.mainloop()
print(v.get())
if v.get() == 0:
testy = Tk()
Label(testy, text='Bad').pack()
testy.mainloop()
else:
testn = Tk()
Label(testn, text='Bad').pack()
testn.mainloop()
If I don't have the first window, it works but with it, it don't.
Somebody know how to fix this problem ?
You have initiated several Tk() systems, but there should be only one.
If you want to get a new window then use Toplevel()
No code is executed after mainloop() except for events. The code continues to "flow" after mainloop only after closing the windows.
So here is your code with fixes:
from tkinter import *
root = Tk()
Button(root, text='TEST', command=root.quit).pack()
master = Toplevel()
v = IntVar()
def check_radio():
print(v.get())
if v.get() == 0:
Label(Toplevel(), text='Bad').pack()
else:
Label(Toplevel(), text='Good').pack()
Radiobutton(master, text='Yes', variable=v, value=0, command=check_radio).pack()
Radiobutton(master, text='No', variable=v, value=1, command=check_radio).pack()
Button(master, text='Exit', command=master.quit).pack()
root.mainloop()
Check carefully, I changed the parents of widgets and other changes.
Possible duplicate of tkinter radiobutton not updating variable, but seeing as this question was asked first the answer may make more sense here.
Make sure you are assigning a master to the IntVar like self.rbv=tk.IntVar(master) #or 'root' or whatever you are using):
import Tkinter as tk
import ttk
class My_GUI:
def __init__(self,master):
self.master=master
master.title("TestRadio")
self.rbv=tk.IntVar(master)#<--- HERE! notice I specify 'master'
self.rb1=tk.Radiobutton(master,text="Radio1",variable=self.rbv,value=0,indicatoron=False,command=self.onRadioChange)
self.rb1.pack(side='left')
self.rb2=tk.Radiobutton(master,text="Radio2",variable=self.rbv,value=1,indicatoron=False,command=self.onRadioChange)
self.rb2.pack(side='left')
self.rb3=tk.Radiobutton(master,text="Radio3",variable=self.rbv,value=2,indicatoron=False,command=self.onRadioChange)
self.rb3.pack(side='left')
def onRadioChange(self,event=None):
print self.rbv.get()
root=tk.Tk()
gui=My_GUI(root)
root.mainloop()
try running that, click the different buttons (they are radiobuttons but with indicatoron=False) and you will see it prints correctly changed values!
I'm running Python 2.7.9 on a Mac. I've been unable to figure out why it is when I run my programs that only the Entry Widgets highlight each time I hit the Tab key to move to the next widget. Following is some test code. When I run the script and hit the Tab key, the first entry field is highlighted. The next time I hit the Tab key, the second entry field is highlighted. However, when I hit the tab key to move to the Button Widget, the Button is receiving the focus but there is not highlight to visually indicate to the user the focus.
The OptionMenu widget is skipped altogether, which is also a mystery. Both the radiobutton and the checkbox receives focus, just like the button widget, and again no highlight is present.
I've tried a variety of .config() arrangements to no avail. What am I missing?
from tkinter import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.grid()
#Tests to make sure that Button receives focus.
def yup(self):
print "yup"
entry1 = Entry(frame)
entry1.pack()
entry2 = Entry(frame)
entry2.pack()
button1 = Button(frame, text="Test")
button1.pack()
button1.bind('<Return>', yup)
var1 = IntVar()
c = Checkbutton(frame, text="Expand", variable=var1)
c.pack()
var2 = StringVar()
radio = Radiobutton(frame, text="Test", variable=var2, value=1)
radio.pack()
var3 = StringVar()
optionmenu1 = OptionMenu(frame, var3, "one", "two", "three")
optionmenu1.pack()
root = Tk()
root.geometry('400x400+0+0')
app = App(root)
root.mainloop()
It sounds like you need to configure OS X for "Full Keyboard Access" to allow Tab to focus on all UI controls (versus just text boxes and lists).
In Yosemite (10.10), this setting can be found under System Preferences > Keyboard > Shortcuts, and can be toggled with Control+F7. Note that has nothing to do with Python, and will occur system-wide.
EDIT
So after doing some more testing, there appears to be some issues with the actual highlighting of certain widgets using tk on a Mac. Below is a condensed version of your original sample with some minor modifications for simplicity.
import Tkinter as tk
import ttk # more on this in a minute
class App(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
entry1 = tk.Entry(self)
entry1.pack()
entry2 = tk.Entry(self)
entry2.pack()
button1 = tk.Button(self, text="Test", command=self.yup)
button1.pack()
def yup(self):
print("yup")
# ...
root = tk.Tk()
app = App(root).pack()
root.mainloop()
With full keyboard access enabled as previously mentioned, I can verify that the button does indeed receive focus: on the third tab, after the first two entry widgets, hitting <space> "clicks" the button and prints to stdout. However there is no visual indication that the button is selected.
Changing the button from a tk.Button to a ttk.Button "fixes" this, and does indeed show the normal "selection" frame around the button when tabbing through the UI.
button1 = tk.Button(self, text="Test", command=self.yup)
# change this to:
button1 = ttk.Button(self, text="Test", command=self.yup)
I have no idea why this is, and I don't know what the consensus about tkinter.ttk is, but I prefer ttk to "plain" tk as it seems to produce widgets which appear more "native" to OS X in my experience. Note I also removed the bind statement, and am reporting my result using the OS X default of space to activate UI elements with full keyboard access enabled.
More on ttk here. Note also that not all tk widgets have a ttk implementation, and that there are also some ttk widgets which do not exist in tk.
Lastly below find the "ttk" version of the original snippet.
import Tkinter as tk
import ttk
class App(ttk.Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.master = master
entry1 = ttk.Entry(self)
entry1.pack()
entry2 = ttk.Entry(self)
entry2.pack()
button1 = ttk.Button(self, text="Test", command=self.yup)
button1.pack()
def yup(self):
print("yup")
# ...
root = tk.Tk()
app = App(root).pack()
root.mainloop()
Hope this helps.
Try changing the background and highlightbackground colors as below but the problem is possibly because of the way the program is indented --> run the second code block.
top=Tk()
## active background
Button(top, text="Quit", bg="lightblue", activebackground="orange",
command=top.quit).grid(row=1)
top.mainloop()
##--------------- note the 3 lines that have been changed ---------
class App:
## function was not indented
def __init__(self, master):
frame = Frame(master)
frame.grid()
entry1 = Entry(frame)
entry1.pack()
entry1.focus_set()
entry2 = Entry(frame)
entry2.pack()
button1 = Button(frame, text="Test")
button1.pack()
## function called incorrectly
button1.bind('<Return>', self.yup)
var1 = IntVar()
c = Checkbutton(frame, text="Expand", variable=var1)
c.pack()
var2 = StringVar()
radio = Radiobutton(frame, text="Test", variable=var2, value=1)
radio.pack()
var3 = StringVar()
optionmenu1 = OptionMenu(frame, var3, "one", "two", "three")
optionmenu1.pack()
Button(frame, text="Quit", bg="orange", command=master.quit).pack()
## function indented too far
#Tests to make sure that Button receives focus.
def yup(self, args):
print "yup"
root = Tk()
root.geometry('400x400+0+0')
app = App(root)
root.mainloop()
I'm trying to use an Entry field to get manual input, and then work with that data.
All sources I've found claim I should use the get() function, but I haven't found a simple working mini example yet, and I can't get it to work.
I hope someone can tel me what I'm doing wrong. Here's a mini file:
from tkinter import *
master = Tk()
Label(master, text="Input: ").grid(row=0, sticky=W)
entry = Entry(master)
entry.grid(row=0, column=1)
content = entry.get()
print(content) # does not work
mainloop()
This gives me an Entry field I can type in, but I can't do anything with the data once it's typed in.
I suspect my code doesn't work because initially, entry is empty. But then how do I access input data once it has been typed in?
It looks like you may be confused as to when commands are run. In your example, you are calling the get method before the GUI has a chance to be displayed on the screen (which happens after you call mainloop.
Try adding a button that calls the get method. This is much easier if you write your application as a class. For example:
import tkinter as tk
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.entry = tk.Entry(self)
self.button = tk.Button(self, text="Get", command=self.on_button)
self.button.pack()
self.entry.pack()
def on_button(self):
print(self.entry.get())
app = SampleApp()
app.mainloop()
Run the program, type into the entry widget, then click on the button.
You could also use a StringVar variable, even if it's not strictly necessary:
v = StringVar()
e = Entry(master, textvariable=v)
e.pack()
v.set("a default value")
s = v.get()
For more information, see this page on effbot.org.
A simple example without classes:
from tkinter import *
master = Tk()
# Create this method before you create the entry
def return_entry(en):
"""Gets and prints the content of the entry"""
content = entry.get()
print(content)
Label(master, text="Input: ").grid(row=0, sticky=W)
entry = Entry(master)
entry.grid(row=0, column=1)
# Connect the entry with the return button
entry.bind('<Return>', return_entry)
mainloop()
*
master = Tk()
entryb1 = StringVar
Label(master, text="Input: ").grid(row=0, sticky=W)
Entry(master, textvariable=entryb1).grid(row=1, column=1)
b1 = Button(master, text="continue", command=print_content)
b1.grid(row=2, column=1)
def print_content():
global entryb1
content = entryb1.get()
print(content)
master.mainloop()
What you did wrong was not put it inside a Define function then you hadn't used the .get function with the textvariable you had set.
you need to put a textvariable in it, so you can use set() and get() method :
var=StringVar()
x= Entry (root,textvariable=var)
Most of the answers I found only showed how to do it with tkinter as tk. This was a problem for me as my program was 300 lines long with tons of other labels and buttons, and I would have had to change a lot of it.
Here's a way to do it without importing tkinter as tk or using StringVars. I modified the original mini program by:
making it a class
adding a button and an extra method.
This program opens up a tkinter window with an entry box and an "Enter" button. Clicking the Enter button prints whatever is in the entry box.
from tkinter import *
class mini():
def __init__(self):
master = Tk()
Label(master, text="Input: ").grid(row=0, sticky=W)
Button(master, text='Enter', command=self.get_content).grid(row=1)
self.entry = Entry(master)
self.entry.grid(row=0, column=1)
master.mainloop()
def get_content(self):
content = self.entry.get()
print(content)
m = mini()
If I put a radio button in a function and draw them; the first time they are drawn you cannot hover over them without making them look like they are all selected.
The same code out of a function does not exhibit this behaviour.
from Tkinter import *
def App(master):
v = StringVar()
v.set('python') # initialize
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth = 3)
runtimeFrame.pack(fill = X, pady = 5, padx = 5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=v, value=mode, indicatoron = 1 )
b.pack(side = LEFT)
if __name__ == '__main__':
master = Tk()
App(master)
#The following code chunk is the same as that in App()
#------------------------
v = StringVar()
v.set('python') # initialize
lable1 = Label(master, text=' hovering over below radio buttons will cause them to Not look selected as expected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth = 3)
runtimeFrame.pack(fill = X, pady = 5, padx = 5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=v, value=mode, indicatoron = 1 )
b.pack(side = LEFT)
#------------------------
mainloop()
Once you have made a selection this does not happen again.
Am I doing something wrong? Is there a workaround, because my code has to be in a function!
This is the second elementary bug I have found in Tkinter. Is there something better for Python GUI development?
ps: I'm using python 2.7
The place where you store the variable object (StringVar, v, in your case) must persist so that this odd behavior wont show up. My guess is we're seeing this behavior because v, goes out of scope, something is going wrong. Aside from using a global, I can't think of a way to do this from a function.
Broken code:
from Tkinter import *
def App(master):
v = StringVar()
v.set('python')
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth=3)
runtimeFrame.pack(fill=X, pady=5, padx=5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=v, value=mode, indicatoron=1)
b.pack(side=LEFT)
if __name__ == '__main__':
master = Tk()
App(master)
mainloop()
Example Fix:
from Tkinter import *
def App(master, radio_var):
radio_var.set('python')
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth=3)
runtimeFrame.pack(fill=X, pady=5, padx=5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=radio_var, value=mode, indicatoron=1)
b.pack(side=LEFT)
if __name__ == '__main__':
master = Tk()
radio_var = StringVar()
App(master, radio_var)
mainloop()
Consider that if you have more than one variable that needs to persist you can pass in a list or dictionary of variables.
Also, just in case "has to be in a function" is a homework assignment requirement, consider wrapping the code in a class. I'm not a tk expert, but that would seem the preferred manner of organizing your code.
Example fix 2:
from Tkinter import *
class App(object):
def __init__(self, master):
self.radio_var = StringVar()
self.radio_var.set('python')
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth=3)
runtimeFrame.pack(fill=X, pady=5, padx=5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, variable=self.radio_var, value=mode, indicatoron=1)
b.pack(side=LEFT)
if __name__ == '__main__':
master = Tk()
app = App(master)
mainloop()
Notice a minor change in
app = App(master)
This is required so that the App instance is not garbage collected prematurely. This would effectively pull the self.radio_var out of scope and we're back at square one.
Try this, it works for me.
v = 0 # this is global variable
def some_function():
global v
v = IntVar()
v.set(0)
rb1 = Radiobutton (parent, variable = v, value = 0)
rb1.pack()
rb2 = Radiobutton (parent, variable = v, value = 1)
rb2.pack()
Make your radio buttons and then you get your radio buttons as they should.
I know that passed a lot of time, but I've tried all the strategies showed here and none of them work with me. What worked for me was simple "rewrite" the event handler for mouse motion event. It's not a perfect solution because I print some kind of garbage to the terminal, but well, this is not a problem in my particular case.
server_name = IntVar()
server_name.set(1)
server_name_rb_1 = Radiobutton(container_3, text="Server", variable=server_name, value=1)
server_name_rb_1.select()
server_name_rb_1.pack()
server_name_rb_2 = Radiobutton(container_3, text="Local", variable=server_name, value=2)
server_name_rb_2.deselect()
server_name_rb_2.pack()
server_name_rb_2.bind('<Motion>',lambda e: print(str(server_name.get())) )
P.S.: You don't need to rewrite all functions just rewrite one of them must be enough.
i was able to fix it by simply adding state=NORMAL, to all the radio buttons. i appreciate that this is the default behavior but it looks like that tkinter kind of doesn't remember this.
I found a solution that it actually works fine so maybe it can be helpful for other desperate people like me.
First thing, I created separate radioButtons so that I can rapidly access to the variable names; finally, I binded the mouse "Leave" event, of the unselected widget, to "break" (doing this I undid the normal behaviour of the widget aka when the mouse is over the radio button now it does nothing).
As to now, I'm not having any problems and it works just fine.
Below, an example code:
...
self.__var__ = IntVar()
self.__var__.set(2)
unselected_btn = Radiobutton(self.__frame__,
text="One", padx=20,
variable=self.__var__,
value=1
)
unselected_btn.grid(row=0, column=1, sticky=E)
selected_btn = Radiobutton(self.__frame__,
text="Two",
padx=20,
variable=self.__var__,
value=2
)
selected_btn.grid(row=0, column=0, sticky=W)
unselected_btn.bind("<Leave>", lambda e: "break")
...
NOTE: here I used grid, but you can simply change it to pack or place
I will admit you found quite an odd "glitch". I'm not entirely prepared to call it a glitch seeing as I'm not sure what is causing it. It seemed to be stemming from the variable, argument. A work around would be instead of getting the variable, get the actual text of the widget via the .cget('text') method.
My changes to your App function:
def App(master):
v = StringVar()
v.set('python') # initialize
lable1 = Label(master, text=' hovering over below radio buttons will cause them to look like they are selected')
lable1.pack()
runtimeFrame = Frame(master, relief=GROOVE, borderwidth = 3)
runtimeFrame.pack(fill = X, pady = 5, padx = 5)
for mode in ['java', 'python', 'jython']:
b = Radiobutton(runtimeFrame, text=mode, value=mode, indicatoron = 1 ) # I omitted the variable argument, which seemed to be the root of your troubles.
b.pack(side = LEFT)
b.deselect() # manually deselects each instance of the Radiobutton.
b.select() # manually selects a default instance of the Radiobutton.