Tkinter, Trying to generate Checkbutton in loop - python

I am trying to generate standalone Checkbuttons in loop but who has the same name Checkbutton works together. I don't know where is my mistake...
#!/usr/bin/env python
#-*-coding:utf-8-*-
import os
from Tkinter import *
import ttk
def checkBoxText(st):
if st == 0:
st="Disabled"
if st == 1:
st="Enabled"
return st
root = Tk()
winSt={1:1,2:1,3:0,4:0}
cbTexts={}
cbVariables={}
cb={}
cb_x={ "1":"0.0", "2":"0.0", "3":"0.6", "4":"0.6" }
cb_y={"1": "0.1", "2": "0.8", "3": "0.1", "4": "0.8"}
for i in sorted(winSt.keys()):
cbTexts[i] = StringVar()
cbTexts[i].set(checkBoxText(winSt[i]))
cbVariables[i] = IntVar()
cbVariables[i].set(winSt[i])
cb[i] = Checkbutton(root, text=cbTexts[i].get(), variable=cbVariables[i].get())
cb[i].place(relx=cb_x[str(i)], rely=cb_y[str(i)], relheight=0.1,relwidth=0.4)
mainloop()

The problem is in this line:
cb[i] = Checkbutton(..., variable=cbVariables[i].get())
When you use the variable attribute, you must give it a reference to a variable object, not the value contained in the object. Change the code to this:
cb[i] = Checkbutton(..., variable=cbVariables[i])
You're making a somewhat similar mistake with the checkbutton text. You are creating a StringVar, but then using the value of the StringVar for the checkbutton text instead of the actual variable. Syntactically that's correct when used with the text attribute, but it's doing more work than it needs to. You should either use the textvariable attribute, or simply not create a StringVar.
Here's how to use the textvariable attribute instead of the text attribute:
cb[i] = Checkbutton(root, textvariable=cbTexts[i], ...)
You don't need the StringVar at all if this text will never change. If that's the case, you can just do this and save a couple lines of code:
cb[i] = Checkbutton(root, text=checkBoxText(winSt[i]), ...)

Related

tkinter checkbutton not setting variable

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.

Why is it saying not defined?

first off im very new to programing Python 3.4.3, im trying to call 5 entrys and put them into and array and display that, but im stuck at GPA1 not defined, but it is defined in for the entry app
import sys
from tkinter import *
def save_data():
fileD.write("GPA1:\n")
fileD.write("%s\n" % GPA1.get())
fileD.write("GPA2:\n")
fileD.write("%s\n" % GPA2.get())
fileD.write("GPA3:\n")
fileD.write("%s\n" % GPA3.get("1.0", END))
app = Tk()
app.title('Student Grade Report')
gpa1=str(GPA1.get())
gp1=float(gpa1)
gpa2=str(GPA2.get())
gp2=float(gpa2)
gpa3=str(GPA3.get())
gp3=float(gpa3)
gpa4=str(GPA4.get())
gp4=float(gpa4)
gpa5=str(GPA5.get())
gp5=float(gpa5)
gpas=gp1+gp2+gp3+gp4+gp5
avg=gpas/5
def DisplayMsg():
DL=str("Dean's list")
AP=str("Academic Probation")
note=("No Message")
if (avg>3.5):
note=DL
else:
if (avg<2.0):
note=AP
return str(note)
classi = StringVar()
classi.set(None)
Label(app, text = "Classification:").pack()
Classification = StringVar()
Classification.set(None)
Radiobutton(app, variable = classi, text = "freshman", value = "freshman").pack()
Radiobutton(app, variable = classi, text = "sophmore", value = "sophmore").pack()
Radiobutton(app, variable = classi, text = "junior", value = "junior").pack()
Radiobutton(app, variable = classi, text = "senior", value = "senior").pack()
Label(app, text = "GPA1:").pack()
GPA1= StringVar()
GPA1.set(None)
GPA1= Entry(app,textvariable=Gpa1).pack()
Label(app, text = "GPA2:").pack()
GPA2= StringVar()
GPA2.set(None)
GPA2 = Entry(app)
Label(app, text = "GPA3:").pack()
GPA3= StringVar()
GPA3.set(None)
GPA3= Entry(app)
Label(app, text = "GPA4:").pack()
GPA4= StringVar()
GPA4.set(None)
GPA4= Entry(app)
Label(app, text = "GPA5:").pack()
GPA5= StringVar()
GPA5.set(None)
GPA5= Entry(app)
Button(app, text= "Message", command = DisplayMsg).pack()
Button(app, text = "Save", command = save_data).pack()
app.mainloop()
The program published above does have some problems with indentation, but I suspect that this happened when inserting into the editor.
Also, I suspect your error says Gpa1 undefined, not GPA1. This is because you assign a text variable to the Entry which is not initialized yet (i.e. it does not exist).
The way you pack() the widgets, make them appear vertically. If you want them in a spreadsheet like table, you have to use a packer such as .grid(), and specify the column and row where you want the widget to appear.
Here's the code, slightly cleaned up (Note that I had to use Tkinter to find the module - not tkinter):
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from Tkinter import *
def save_data():
fileD.write("GPA1:\n")
fileD.write("%s\n" % GPA1.get())
fileD.write("GPA2:\n")
fileD.write("%s\n" % GPA2.get())
fileD.write("GPA3:\n")
fileD.write("%s\n" % GPA3.get("1.0", END))
app = Tk()
app.title('Student Grade Report')
gpa1=str(GPA1.get())
gp1=float(gpa1)
gpa2=str(GPA2.get())
gp2=float(gpa2)
gpa3=str(GPA3.get())
gp3=float(gpa3)
gpa4=str(GPA4.get())
gp4=float(gpa4)
gpa5=str(GPA5.get())
gp5=float(gpa5)
gpas=gp1+gp2+gp3+gp4+gp5
avg=gpas/5
def DisplayMsg():
DL=str("Dean's list")
AP=str("Academic Probation")
note=("No Message")
if (avg>3.5):
note=DL
elif (avg<2.0):
note=AP
return str(note)
def main():
app = Tk()
app.title('Student Grade Report')
classi = StringVar()
classi.set(None)
Label(app, text = "Classification:").pack()
Classification = StringVar()
Classification.set(None)
Radiobutton(app, variable = classi, text = "freshman", value = "freshman").pack()
Radiobutton(app, variable = classi, text = "sophmore", value = "sophmore").pack()
Radiobutton(app, variable = classi, text = "junior", value = "junior").pack()
Radiobutton(app, variable = classi, text = "senior", value = "senior").pack()
Gpa1 = None
Label(app, text = "GPA1:").pack()
GPA1= StringVar()
GPA1.set(None)
GPA1= Entry(app,textvariable = Gpa1).pack()
Label(app, text = "GPA2:").pack()
GPA2= StringVar()
GPA2.set(None)
GPA2 = Entry(app)
Label(app, text = "GPA3:").pack()
GPA3= StringVar()
GPA3.set(None)
GPA3= Entry(app)
Label(app, text = "GPA4:").pack()
GPA4= StringVar()
GPA4.set(None)
GPA4= Entry(app)
Label(app, text = "GPA5:").pack()
GPA5= StringVar()
GPA5.set(None)
GPA5= Entry(app)
Button(app, text= "Message", command = DisplayMsg).pack()
Button(app, text = "Save", command = save_data).pack()
app.mainloop()
main()
NOTE: Sorry! I added a NOTE to the original post, instead of my answer. Here's the correct note: You are not done yet! There are still issues to be resolved. Eg. The fileD.write won't work if you don't open the file first.
You need to fix all the problems described in jcoppens' very thorough answer, but the actual problem you're asking about is another one, that you also need to fix. (I'm assuming that your actual indentation and structure looks like what he's guessed in his answer. If this doesn't apply to your actual code… well, it applies to the code in his answer, which your code will hopefully be a lot like once you fix all the other problems.)
Inside save_data, you do this:
fileD.write("%s\n" % GPA1.get())
But there's no global variable named GPA1, and no local variable inside that function named GPA1.
There is a local variable inside main with that name, but that doesn't help; the whole point of local variables is that they're local—the only live inside one function.
You could fix this by making GPA1 global, by just adding global GPA1 to the top of your main function. This is the quickest solution, but usually not the best.
A better solution is to create a class, and make GPA1 an attribute of that class. Whichever Tkinter docs or tutorial you're using will probably have lots of good examples of this.
You could also change save_data to take GPA1 as a parameter, and then use functools.partial(save_data, GPA1) instead of just save_data, as a callback.
Or you could put all of these functions inside another function and make GPA1 a nonlocal variable instead of a global one. (I wouldn't recommend this, just including it for completeness…)
Of course whatever you do, you need to do the same for GPA2 or you'll just get the same error again two lines later.
Needless to say, there's no guarantee this is the last bug in your program.

Tkinter. StringVar(). How I can update the Label in my code.

I have a problem with updating the Label widget (lab1_datum_in).
I would like to change the Label text every time I change radiobuttons.
Really do not know what is wrong.
Could anyone help?
My interpreter is Python 3.4
Thanks in advance!
import tkinter
root = tkinter.Tk()
# Creating radiobuttons
v = tkinter.IntVar()
v.set(1) # initializing the choice
transformation_types = [
("WGS84 --> ED50(North of 62N)",1),
("WGS84 --> ED50(South of 62N)",2),
("ED50(North of 62N) --> WGS84",3),
("ED50(South of 62N) --> WGS84",4),
]
datum_in_text = tkinter.StringVar()
datum_in_text.set('WGS84')
def ChangeDatumText():
if v.get() == 1:
global datum_in_text
datum_in_text.set('WGS84')
elif v.get() == 2:
global datum_in_text
datum_in_text.set('WGS84')
elif v.get() == 3:
global datum_in_text
datum_in_text.set('ED50(North of 62N')
elif v.get() == 4:
global datum_in_text
datum_in_text.set('ED50(North of 62N')
for txt, val in transformation_types:
tkinter.Radiobutton(root,
text=txt,
padx=20,
variable=v,
command=ChangeDatumText,
value=val).grid()
lab1_datum_in = tkinter.Label(root, text=datum_in_text.get()).grid(row=9, column=1)
root.mainloop()
As #jonrsharpe indicated in a comment, to slave the text displayed on the Label widget you need to set the textvariable option to a control variable of class StringVar, which in your case is datum_in_text. The text option you have is for displaying a one or more lines of static text.
This means you need to use:
lab1_datum_in = tkinter.Label(
root, textvariable=datum_in_text).grid(row=9, column=1)
instead of what you have.
BTW, all those global datum_in_text declarations you have in the ChangeDatumText() function are unnecessary and all but the first produce non-fatal syntax warnings when they're encountered. Since you're only calling one of the global variable's methods, rather than assigning a value to the variable name itself, you don't even need do this, but if you do put one in—it doesn't hurt—just declare it global once at the very beginning of the function.
Another thing to be aware of is that the variable lab1_datum_in will be set ti None by the assignment because the grid() method doesn't return anything — so you should split it up into two separate steps.

How do I create a button in Python Tkinter to increase integer variable by 1 and display that variable?

I am trying to create a Tkinter program that will store an int variable, and increase that int variable by 1 each time I click a button, and then display the variable so I can see that it starts out as 0, and then each time I click the button it goes up by 1. I am using python 3.4.
import sys
import math
from tkinter import *
root = Tk()
root.geometry("200x200")
root.title("My Button Increaser")
counter = 0
def nClick():
counter + 1
def main_click():
mLabel = Label(root, text = nClick).pack()
mButton1 = Button(text = "Increase", command = main_click, fg = "dark green", bg = "white").pack()
root.mainloop()
Ok so there are a few things wrong with your code so far. My answer basically changes what you have already into the easiest way for it to do what you want.
Firstly you import libraries that you don't need/use (you may need them in your whole code, but for this question include a minimal example only). Next you must deifne the counter variable as a global variable, so that it will be the same in the function (do this inside the function as well). Also you must change counter + 1 to counter += 1 so it increments the variable
Now the code will be able to count, but you have set variables as Buttons, but then made them None type objects, to change this .pack() the variable on the next line. You can get rid of the second function as you only need one, then you change the command of the button and its text to counter. Now to update the text in the button, you use .config(text = counter) which configures the button.
Here is the final solution (changes button value and has no label, but this is easily changed):
from tkinter import *
root = Tk()
root.geometry("200x200")
root.title("My Button Increaser")
global counter
counter = 0
def nClick():
global counter
counter += 1
mButton1.config(text = counter)
mButton1 = Button(text = counter, command = nClick, fg = "darkgreen", bg = "white")
mButton1.pack()
root.mainloop()
hope that helps!
You could use Tkinter variables. They are specially useful when you need to modify a data that other widgets might interact with. Here is a look alike code to the one in the question, but instead of defining counter as a normal variable, it is a variable from Tkinter.
import tkinter
import sys
root = tkinter.Tk()
root.geometry("200x200")
root.title("His Button Increaser")
counter = tkinter.IntVar()
def onClick(event=None):
counter.set(counter.get() + 1)
tkinter.Label(root, textvariable=counter).pack()
tkinter.Button(root, text="Increase", command=onClick, fg="dark green", bg = "white").pack()
root.mainloop()
Instead of passing the value this variable holds to the text attribute of the Label, we assign the variable to textvariable attribute, so when the value of the variable gets updated, Label would update the displayed text accordingly.
When you want to change the value of the variable, you'd need to call the set() method of the variable object (see onClick) instead of assigning the value directly to it.

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