tkinter checkbutton not setting variable - python

Whatever I do to my checkbutton, it does not seem to set the variable.
Here's the parts of the code that are involved:
class Window:
def __init__(self):
self.manualb = 0 #to set the default value to 0
def setscreen(self):
#screen and other buttons and stuff set here but thats all working fine
manual = tkr.Checkbutton(master=self.root, variable=self.manualb, command=self.setMan, onvalue=0, offvalue=1) #tried with and without onvalue/offvalue, made no difference
manual.grid(row=1, column=6)
def setMan(self):
print(self.manualb)
#does some other unrelated stuff
It just keeps printing 0. Am I doing something wrong? Nothing else does anything to manual.

You're looking for IntVar()
IntVar() has a method called get() which will hold the value of the widget you assign it to.
In this particular instance, it will be either 1 or 0 (On or off).
You can use it something like this:
from tkinter import Button, Entry, Tk, Checkbutton, IntVar
class GUI:
def __init__(self):
self.root = Tk()
# The variable that will hold the value of the checkbox's state
self.value = IntVar()
self.checkbutton = Checkbutton(self.root, variable=self.value, command=self.onClicked)
self.checkbutton.pack()
def onClicked(self):
# calling IntVar.get() returns the state
# of the widget it is associated with
print(self.value.get())
app = GUI()
app.root.mainloop()

This is because you need to use one of tkinter's variable classes.
This would look something like the below:
from tkinter import *
root = Tk()
var = IntVar()
var.trace("w", lambda name, index, mode: print(var.get()))
Checkbutton(root, variable=var).pack()
root.mainloop()
Essentially IntVar() is a "container" (very loosely speaking) which "holds" the value of the widget it's assigned to.

Related

Updating Entry and Label widget in Tkinter

i have defined a GUI class that creates a tkinter window with a couple of entries. I would like that every time that the user overwrites the Entries and press Enter, there is some operation done in the background. In addition, i would like that the entries are checking regularly certain values and updating them, so the user could see if they changed; In the example below i use a static dictionary, but normally those parameters are obtained from a camera and could fluctuate.
However, i am not even able to get the correct value printed in the label. I am not a tkinter expert so any idea would be appreciated
from tkinter import *
class GUI():
def __init__(self, window, window_title,input_dict):
self.window = window
self.window.title(window_title)
self.window.geometry('400x200')
top_frame = Frame(self.window)
top_frame.pack(side=TOP, pady=5)
Label(top_frame, text="Frame rate (fps)").grid(row=0)
Label(top_frame, text="Exposure time (ms)").grid(row=2)
self.labeling=Label(top_frame, text="Result").grid(row=3)
self.e1_var = StringVar() # or StringVar(top)
self.e1_var.set(str(round(input_dict['frameRate'])))
self.e2_var = StringVar() # or StringVar(top)
# print(type(self.e2_var))
self.e2_var.set(str(round(input_dict['Exp_time'])))
self.fps_entry = Entry(top_frame,textvariable=self.e1_var)
self.exptime_entry = Entry(top_frame,textvariable=self.e2_var)
self.fps_entry.bind("<Return>",self.my_tracer)
self.exptime_entry.bind("<Return>",self.my_tracer)
self.fps_entry.grid(row=0, column=1)
self.exptime_entry.grid(row=2, column=1)
self.window.mainloop()
def my_tracer(self,event):
val1=int(self.e1_var.get())
val2=int(self.e2_var.get())
self.labeling.configure(text=str(val1*val2))
input_dict = {
'frameRate': 50,
'Exp_time': 5000}
video_object=GUI(Tk(),"Test",input_dict)
The error your code produces is AttributeError: 'NoneType' object has no attribute 'configure', right?
Look at this line:
self.labeling=Label(top_frame, text="Result").grid(row=3)
self.labeling will be None because grid() returns None. It is indeed bad practice to 'chain' a geometry manager to the creation of a widget. Change to:
self.labeling=Label(top_frame, text="Result")
self.labeling.grid(row=3)
Now the labels are updating when the user enters a new value.

Allow user to change default text in tkinter entry widget.

I'm writing a python script that requires the user to enter the name of a folder. For most cases, the default will suffice, but I want an entry box to appear that allows the user to over-ride the default. Here's what I have:
from Tkinter import *
import time
def main():
#some stuff
def getFolderName():
master = Tk()
folderName = Entry(master)
folderName.pack()
folderName.insert(END, 'dat' + time.strftime('%m%d%Y'))
folderName.focus_set()
createDirectoryName = folderName.get()
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
return
b = Button(master, text="OK and Close", width=10, command=callback)
b.pack()
mainloop()
return createDirectoryName
getFolderName()
#other stuff happens....
return
if __name__ == '__main__':
main()
I know next to nothing about tkInter and have 2 questions.
Is over-riding the default entry using global createDirectoryName within the callback function the best way to do this?
How can I make the button close the window when you press it.
I've tried
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
master.destroy
but that simply destroys the window upon running the script.
I don't know how experienced are you in Tkinter, but I suggest you use classes.
try:
from tkinter import * #3.x
except:
from Tkinter import * #2.x
class anynamehere(Tk): #you can make the class inherit from Tk directly,
def __init__(self): #__init__ is a special methoed that gets called anytime the class does
Tk.__init__(self) #it has to be called __init__
#further code here e.g.
self.frame = Frame()
self.frame.pack()
self.makeUI()
self.number = 0 # this will work in the class anywhere so you don't need global all the time
def makeUI(self):
#code to make the UI
self.number = 1 # no need for global
#answer to question No.2
Button(frame, command = self.destroy).pack()
anyname = anynamehere() #remember it alredy has Tk
anyname.mainloop()
Also why do you want to override the deafult Entry behavior ?
The solution would be to make another button and bind a command to it like this
self.enteredtext = StringVar()
self.entry = Entry(frame, textvariable = self.enteredtext)
self.entry.pack()
self.button = Button(frame, text = "Submit", command = self.getfolder, #someother options, check tkitner documentation for full list)
self.button.pack()
def getfolder(self): #make the UI in one method, command in other I suggest
text = self.enteredtext.get()
#text now has whats been entered to the entry, do what you need to with it

Changing state of tkinter entry box depending on value of OptionMenu

I am using python 2.7 and trying to change the state of a tkinter entry box depending on the value of an OptionMenu widget. I found an example of how to do it online here, it's for python 3 but I don't think that's the issue (correct me if I am wrong). Some example code is below,
from Tkinter import *
class App:
def _disable_f2(self):
if self.filt.get() == 'bandpass':
self.filter_menu.configure(state='normal')
else:
self.filter_menu.configure(state='disabled')
def __init__(self, master):
self.f2var = Tkinter.StringVar()
self.f2var.set('5.0')
self.f2_entry = Tkinter.Entry(master, textvariable=self.f2var,
width=5)
self.f2_entry.pack()
self.filt = Tkinter.StringVar()
self.filt.set('bandpass')
self.filter_menu = Tkinter.OptionMenu(master, self.filt,
'bandpass', 'lowpass ',
'highpass',
command=self._disable_f2)
self.filter_menu.pack(ipadx=50)
root = Tk()
app = App(root)
root.mainloop()
however, I keep getting the following error even though I am not passing two arguments. Anyone know what the cause is?
TypeError: _disable_f2() takes exactly 1 argument (2 given)
If you just accept one more argument and print it, you can find out what the argument is that is passed by the OptionMenu:
def _disable_f2(self, arg):
print arg
You will see it prints the new value of the OptionMenu. Because this argument is passed you need the function to accept it, and you actually are using it (with self.filt.get()) so it's fine that it's passed.
You can rewrite your _disable_f2 function to:
def _disable_f2(self, option):
if option == 'bandpass':
self.f2_entry.configure(state='normal')
else:
self.f2_entry.configure(state='disabled')
In your original code you disabled the optionmenu when the option was not 'bandpass', but I assume you want to disable the entry right? That's what this code does.
Also, if you use from Tkinter import *, you don't have to use Tkinter.StringVar(), but you can just use StringVar(). Same goes for Entry(...), OptionMenu(...) and Tk().
Allthough I would advise to use import Tkinter as tk, and use tk.StringVar() etc.
If _disable_f2 is being given two arguments, let it have what it wants.. try below...
:)
from Tkinter import *
class App:
def _disable_f2(self, master):
if self.filt.get() == 'bandpass':
self.filter_menu.configure(state='normal')
else:
self.filter_menu.configure(state='disabled')
def __init__(self, master):
self.f2var = StringVar()
self.f2var.set('5.0')
self.f2_entry = Entry(master, textvariable=self.f2var,
width=5)
self.f2_entry.pack()
self.filt = StringVar()
self.filt.set('bandpass')
self.filter_menu = OptionMenu(master, self.filt,
'bandpass', 'lowpass ',
'highpass',
command=self._disable_f2)
self.filter_menu.pack(ipadx=50)
root = Tk()
app = App(root)
root.mainloop()

How to automatically insert several tkinter items in Tk() window using classes

I apologize in advance if this is a stupid simple question, but i am really bad att python classes and can't seem to get it to work!
Here is my code:
from tkinter import *
a = Tk()
class toolsGUI():
def __init__(self, rootWin):
pass
def frame(self):
frame = Frame(rootWin)
frame.configure(bg = 'red')
frame.grid()
def button(self, binding, text):
btn = Button(rootWin, text=text)
btn.configure(bg = 'orange', fg = 'black')
btn.bind('<'+binding+'>')
btn.grid(row=1, sticky = N+S+E)
I simply want the button() or frame() to understand that rootWin is the same as in __init__, in this case rootWin should be variable a, thus placing the button in the Tk() window. After looking around, I understand that this is not the way to do it. Do anyone have another suggestion that might work?
You're pretty close. You are passing a to the toolsGUI initializer which is the right first step. You simply need to save this as an instance variable, then use the variable whenever you need to reference the root window:
def __init__(self, rootWin):
...
self.rootWin = rootWin
...
def frame(self):
frame = Frame(self.rootWin)
...
An alternative is to have toolsGUI inherit from Frame, in which case you can put all of the widgets in the frame instead of the root window. You then need the extra step of putting this frame inside the root window.
class toolsGUI(Frame):
def __init__(self, rootWin):
Frame.__init__(self, rootWin)
def frame(self):
frame = Frame(self)
...
a = Tk()
t = toolsGUI(a)
t.pack(fill="both", expand=True)
a.mainloop()
As a final bit of advice: don't user variables that are the same name as methods if you can avoid it. "frame" is a poor choice of function names. Instead, call it "create_frame" or something, otherwise it could be confused with class Frame and the local variable frame

How to set the text/value/content of an `Entry` widget using a button in tkinter

I am trying to set the text of an Entry widget using a button in a GUI using the tkinter module.
This GUI is to help me classify thousands of words into five categories. Each of the categories has a button. I was hoping that using a button would significantly speed me up and I want to double check the words every time otherwise I would just use the button and have the GUI process the current word and bring the next word.
The command buttons for some reason are not behaving like I want them to. This is an example:
import tkinter as tk
from tkinter import ttk
win = tk.Tk()
v = tk.StringVar()
def setText(word):
v.set(word)
a = ttk.Button(win, text="plant", command=setText("plant"))
a.pack()
b = ttk.Button(win, text="animal", command=setText("animal"))
b.pack()
c = ttk.Entry(win, textvariable=v)
c.pack()
win.mainloop()
So far, when I am able to compile, the click does nothing.
You might want to use insert method. You can find the documentation for the Tkinter Entry Widget here.
This script inserts a text into Entry. The inserted text can be changed in command parameter of the Button.
from tkinter import *
def set_text(text):
e.delete(0,END)
e.insert(0,text)
return
win = Tk()
e = Entry(win,width=10)
e.pack()
b1 = Button(win,text="animal",command=lambda:set_text("animal"))
b1.pack()
b2 = Button(win,text="plant",command=lambda:set_text("plant"))
b2.pack()
win.mainloop()
If you use a "text variable" tk.StringVar(), you can just set() that.
No need to use the Entry delete and insert. Moreover, those functions don't work when the Entry is disabled or readonly! The text variable method, however, does work under those conditions as well.
import Tkinter as tk
...
entry_text = tk.StringVar()
entry = tk.Entry( master, textvariable=entry_text )
entry_text.set( "Hello World" )
You can choose between the following two methods to set the text of an Entry widget. For the examples, assume imported library import tkinter as tk and root window root = tk.Tk().
Method A: Use delete and insert
Widget Entry provides methods delete and insert which can be used to set its text to a new value. First, you'll have to remove any former, old text from Entry with delete which needs the positions where to start and end the deletion. Since we want to remove the full old text, we start at 0 and end at wherever the end currently is. We can access that value via END. Afterwards the Entry is empty and we can insert new_text at position 0.
entry = tk.Entry(root)
new_text = "Example text"
entry.delete(0, tk.END)
entry.insert(0, new_text)
Method B: Use StringVar
You have to create a new StringVar object called entry_text in the example. Also, your Entry widget has to be created with keyword argument textvariable. Afterwards, every time you change entry_text with set, the text will automatically show up in the Entry widget.
entry_text = tk.StringVar()
entry = tk.Entry(root, textvariable=entry_text)
new_text = "Example text"
entry_text.set(new_text)
Complete working example which contains both methods to set the text via Button:
This window
is generated by the following complete working example:
import tkinter as tk
def button_1_click():
# define new text (you can modify this to your needs!)
new_text = "Button 1 clicked!"
# delete content from position 0 to end
entry.delete(0, tk.END)
# insert new_text at position 0
entry.insert(0, new_text)
def button_2_click():
# define new text (you can modify this to your needs!)
new_text = "Button 2 clicked!"
# set connected text variable to new_text
entry_text.set(new_text)
root = tk.Tk()
entry_text = tk.StringVar()
entry = tk.Entry(root, textvariable=entry_text)
button_1 = tk.Button(root, text="Button 1", command=button_1_click)
button_2 = tk.Button(root, text="Button 2", command=button_2_click)
entry.pack(side=tk.TOP)
button_1.pack(side=tk.LEFT)
button_2.pack(side=tk.LEFT)
root.mainloop()
Your problem is that when you do this:
a = Button(win, text="plant", command=setText("plant"))
it tries to evaluate what to set for the command. So when instantiating the Button object, it actually calls setText("plant"). This is wrong, because you don't want to call the setText method yet. Then it takes the return value of this call (which is None), and sets that to the command of the button. That's why clicking the button does nothing, because there is no command set for it.
If you do as Milan Skála suggested and use a lambda expression instead, then your code will work (assuming you fix the indentation and the parentheses).
Instead of command=setText("plant"), which actually calls the function, you can set command=lambda:setText("plant") which specifies something which will call the function later, when you want to call it.
If you don't like lambdas, another (slightly more cumbersome) way would be to define a pair of functions to do what you want:
def set_to_plant():
set_text("plant")
def set_to_animal():
set_text("animal")
and then you can use command=set_to_plant and command=set_to_animal - these will evaluate to the corresponding functions, but are definitely not the same as command=set_to_plant() which would of course evaluate to None again.
One way would be to inherit a new class,EntryWithSet, and defining set method that makes use of delete and insert methods of the Entry class objects:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
class EntryWithSet(tk.Entry):
"""
A subclass to Entry that has a set method for setting its text to
a given string, much like a Variable class.
"""
def __init__(self, master, *args, **kwargs):
tk.Entry.__init__(self, master, *args, **kwargs)
def set(self, text_string):
"""
Sets the object's text to text_string.
"""
self.delete('0', 'end')
self.insert('0', text_string)
def on_button_click():
import random, string
rand_str = ''.join(random.choice(string.ascii_letters) for _ in range(19))
entry.set(rand_str)
if __name__ == '__main__':
root = tk.Tk()
entry = EntryWithSet(root)
entry.pack()
tk.Button(root, text="Set", command=on_button_click).pack()
tk.mainloop()
e= StringVar()
def fileDialog():
filename = filedialog.askopenfilename(initialdir = "/",title = "Select A
File",filetype = (("jpeg","*.jpg"),("png","*.png"),("All Files","*.*")))
e.set(filename)
la = Entry(self,textvariable = e,width = 30).place(x=230,y=330)
butt=Button(self,text="Browse",width=7,command=fileDialog).place(x=430,y=328)

Categories

Resources