I'm trying to take the textvariable from an entry(a) and then put it in a label in a new window(w).
from Tkinter import *
def abind(avar):
print avar
w=Toplevel()
at=Label(w, text=avar).pack()
w.mainloop()
app=Tk()
at=StringVar()
a=Entry(app,textvariable=at)
avar=at.get()
a.pack()
a.focus()
b=Button(app, command=abind(avar)).pack()
app.mainloop()
It either prints blank, if I take the avar out of the parantheses after abind, or opens a new window immeadiatley and doesn't display the button widget, if I leave the avar.
There are two main problems with your code:
with avar=at.get(), the avar variable has the value of the text variable at that point in time, i.e. it is just the empty string
with Button(app, command=abind(avar)), you are calling the function abind(avar) and using its result as a command, i.e. None
Also, by doing b=Button(...).pack(), b is the result of pack(), i.e. None. This is not related to your problem, but it's probably not what you intended, either. Try this:
b = Button(app, command=lambda: abind(at.get()))
b.pack()
This uses lambda to create a new anonymous function that will get the current value from at using at.get() and call abind with that value, setting the text of the Label accordingly.
If you want the Label to be updated as you type additional text into the Entry, try this:
def abind(avar):
...
at = Label(w, textvariable=avar) # use textvariable
at.pack # pack again
...
...
b = Button(app, command=lambda: abind(at)) # pass at itself
...
Related
Generator functions promise to make some code easier to write. But I don't always understand how to use them.
Let's say I have a Fibonacci generator function fib(), and I want a tkinter application that displays the first result. When I click on a button "Next" it displays the second number, and so on. How can I structure the app to do that?
I probably need to run the generator in a thread. But how do I connect it back to the GUI?
You don't need a thread for this particular problem. You just need to instantiate the generator, then use next to get a new value each time the button is pressed.
Here's a simple example:
import tkinter as tk
def fibonacci_sequence():
'''Generator of numbers in a fibonacci sequence'''
a,b = 1,1
while True:
yield a
a,b = b, a+b
def do_next():
'''Updates the label with the next value from the generator'''
label.configure(text=next(generator))
root = tk.Tk()
label = tk.Label(root, text="")
button = tk.Button(root, text="Next", command=do_next)
label.pack(side="top")
button.pack(side="bottom")
# Initialize the generator, then us it to initialize
# the label
generator = fibonacci_sequence()
do_next()
root.mainloop()
Not expert in tkinter but one way is to use function StringVar to be able to update its value and using text box for output.
Try this out, hope it gives you a clue.
import tkinter as tk
fibo_list = [1,1]
def fibo():
fibo_list.append(fibo_list[-1] + fibo_list[-2])
variable.set(fibo_list[-1])
show_fibo_value()
def show_fibo_value():
text_box.insert(index='end',chars=variable.get()+'\n')
root = tk.Tk()
variable = tk.StringVar()
text_box = tk.Text()
text_box.pack()
button = tk.Button(root, text="Next", command=fibo)
button.pack()
root.mainloop()
I'm trying to make a tkinter script that will print a lot of buttons with functions like this.
from tkinter import *
def printbutton(x:int):
print(x)
root = Tk()
root.geometry('500x500')
for x in range(1,100):
Button(root, text = str(x), command = lambda:printbutton(x)).pack(side="left")
root.mainloop()
But it print all button in a line and expanded outside of my GUI. I want my button automatically go to the next line for convenient, but I don't know how to do it.
Firstly, I would suggest use grid() instead of pack() for more clarity in such cases.
Next, to pass the value of the button in function, bind your printbutton(b) with your button itself. This is because at the end of for loop, the value of x is 99 and this x will be then passed with every button.
Now, to print buttons in a new line after you reach up to the width of the window, I used a simple logic, you can edit the value of width variable according to your window and button size.
So, here's what you are looking for:
from tkinter import *
def printbutton(btn):
print(btn["text"])
root = Tk()
root.geometry('500x500')
r,c = 0,0
width = 21
for x in range(1,100):
b = Button(root, text = str(x))
b.grid(row=r, column=c, sticky=N+S+E+W) #use grid system
b["command"] = lambda b=b: printbutton(b) #now b is defined, so add command attribute to b, where you attach your button's scope within a lambda function
#printbutton(b) is the function that passes in the button itself
c+=1
if c==width:
r+=1
c=0
root.mainloop()
A have a Grid of 38 buttons (num0-num38).
I'm trying to pass the button attribute (text) to the print function.
Code:
def collect_num(num):
print(num)
num0 = tk.Button(buttons, text="0", padx=10, pady=5, fg="white", bg="green", command=collect_num(thisItem.text))
Is there something similar to thisItem.text in Python?
So basically, I would like to print the button name (text="0" - text="38") when the correspondent button is pressed.
Any help with that is appreciated.
Since you asked for an example, take a look here:
from tkinter import *
root = Tk()
MAX_BUTTON = 25 # Maximum number of buttons to be made
def collect_num(num):
print(num) # Print the number
btns = [] # A list to add all the buttons, to make them reusable later
for i in range(MAX_BUTTON): # Looping 25 times or maximum number of buttons, times
btns.append(Button(root,text=i,width=10)) # Creating a button and adding it to list
btns[-1]['command'] = lambda x=btns[-1].cget('text'): collect_num(x) # Changing the command of the button
btns[-1].pack() # Packing it on
root.mainloop()
Here cget() returns the current properties of the widget.
It is highly recommended to do this, instead of creating buttons one by one manually. There is a principle in software engineering called DRY which means Don't Repeat Yourself, which usually means if your repeating yourself, there will possibly be a way to not repeat your self and reduce the code written. And this looping and creating a widget and using lambda to make the command is quiet used frequently when needed to make more than one widget with a pattern.
you need pass the button text using partial(<function>, *args) function
from functools import partial
def collect_num(num):
print(num)
# create the button first and add some specs like `text`
button = tk.Button(
buttons, text="0", padx=10, pady=5, fg="white", bg="green"
)
# add command after creation with the above function and button's name
button.config(command=partial(collect_num, button["text"]))
with the help of this stack question:
text = button["text"]
this is the method with which you can get the text.
or
text = button.cget('text')
or (with the help of #TheLizzard)
text = button.config("text")[-1]
I am trying to create a window with a line label, an entry field, a current value label, and an "Update Value" button.
Here is an example:
This is what I have so far. I can get the entered value to print to console, but I can't seem to work out how to get an entered value and change the currentValue Label to reflect that value by pressing the button:
from tkinter import*
main=Tk()
#StringVar for currentValue in R0C2
currentValue = StringVar(main, "0")
#Called by the setValues button, looks for content in the entry box and updates the "current" label
def setValues():
content = entry.get()
print(content)
#This kills the program
def exitProgram():
exit()
#Title and window size
main.title("Title")
main.geometry("350x200")
#Descriptions on the far left
Label(main, text="Duration (min): ").grid(row=0, column=0)
#Entry boxes for values amidship
entry=Entry(main, width=10)
entry.grid(row=0, column=1)
#Displays what the value is currently set to.
currentValue = Label(textvariable=currentValue)
currentValue.grid(row=0,column=2)
#Takes any inputted values and sets them in the "Current" column using def setValues
setValues=Button(text='Set Values',width=30,command=setValues)
setValues.grid(row=9, column=0, columnspan=2)
#Red button to end program
exitButton=Button(main, text='Exit Program',fg='white',bg='red',width=30, height=1,command=exitProgram)
exitButton.grid(row=20, column = 0, columnspan=2)
main.mainloop()
There are a couple of problems with your code.
Firstly, you are overwriting the setValues function with the setValues Button widget, and similarly, you are overwriting the currentValue StringVar with the currentValue Label.
To set a StringVar, you use its .set method.
Don't use plain exit in a script, that's only meant to be used in an interactive interpreter session, the proper exit function is sys.exit. However, in a Tkinter program you can just call the .destroy method of the root window.
Here's a repaired version of your code.
import tkinter as tk
main = tk.Tk()
#StringVar for currentValue in R0C2
currentValue = tk.StringVar(main, "0")
#Called by the setValues button, looks for content in the entry box and updates the "current" label
def setValues():
content = entry.get()
print(content)
currentValue.set(content)
#This kills the program
def exitProgram():
main.destroy()
#Title and window size
main.title("Title")
main.geometry("350x200")
#Descriptions on the far left
tk.Label(main, text="Duration (min): ").grid(row=0, column=0)
#Entry boxes for values amidship
entry = tk.Entry(main, width=10)
entry.grid(row=0, column=1)
#Displays what the value is currently set to.
currentValueLabel = tk.Label(textvariable=currentValue)
currentValueLabel.grid(row=0,column=2)
#Takes any inputted values and sets them in the "Current" column using def setValues
setValuesButton = tk.Button(text='Set Values',width=30,command=setValues)
setValuesButton.grid(row=9, column=0, columnspan=2)
#Red button to end program
exitButton = tk.Button(main, text='Exit Program',fg='white',bg='red',width=30, height=1,command=exitProgram)
exitButton.grid(row=20, column = 0, columnspan=2)
main.mainloop()
BTW, it's a Good Idea to avoid "star" imports. Doing from tkinter import * dumps 130 names into your namespace, which is unnecessary and creates the possibility of name collisions, especially if you do star imports from several modules. It also makes the code less readable, since the reader has remember which names you defined and which ones came from the imported module(s).
In my opinion the easiest way to do this would be using an object orientated method. This way you could declare a button with a command that calls a def which runs self.label.configure(text=self.entry.get()).
This can be seen below:
import tkinter as tk
class App:
def __init__(self, master):
self.master = master
self.label = tk.Label(self.master)
self.entry = tk.Entry(self.master)
self.button = tk.Button(self.master, text="Ok", command=self.command)
self.label.pack()
self.entry.pack()
self.button.pack()
def command(self):
self.label.configure(text=self.entry.get())
root = tk.Tk()
app = App(root)
root.mainloop()
The above creates a label, entry and button. The button has a command which calls a def within the class App and updates the value of the label to be the text contained within the entry.
This all works very smoothly and cleanly and more importantly is drastically easier (in my opinion) to read and update in the future.
From your code you are setting the 'currentValue', which is a StringVar:
#StringVar for currentValue in R0C2
currentValue = StringVar(main, "0")
to an object Label further down in your code. You cannot do this!
#Displays what the value is currently set to.
currentValue = Label(textvariable=currentValue) ** this line is wrong
currentValue.grid(row=0,column=2)
You should name the label something different like:
#Displays what the value is currently set to.
lblCurrentValue = Label(textvariable=currentValue)
lblCurrentValue.grid(row=0,column=2)
Then in your "setValues" method you should use 'StringVar.set(value) to update the label like so:
def setValues():
content = entry.get()
currentValue.set(entry.get())------------------Here I set the value to the entry box value
print(content)
I tend to avoid stringVar and just use:
Label.config(text='*label's text*')
If you need more help I can post you my solution but try and solve it first becasue its the best way to learn. My tip is to make sure you are using correct naming conventions. In tkinter I tend to use lbl..., entryBox... etc before widgets so I know what they are and not to confuse them with variables.
I am trying to make a simple GUI calculator just for addition/subtraction in the beginning. I am able to print the result to the console but I want to print it to the Entry box like the First Name entry box for example but not able to do it. I would really appreciate if you could help.(*Please neglect the alignment right now of the buttons I am focusing on the functioning, trying to get it right)
from Tkinter import *
import tkMessageBox
import sys
class scanner:
list1 = []
def __init__(self,parent):
self.entrytext = StringVar()
self.entrytext1 = StringVar()
Label(root, text="first name", width=10).grid(row=0,column=0)
Entry(root, textvariable=self.entrytext, width=10).grid(row=0,column=1)
Label(root, text="last name", width=10).grid(row=1,column=0)
Entry(root, textvariable=self.entrytext1, width=10).grid(row=1,column=1)
Button(root, text="ADD", command=self.add).grid()
Button(root, text="SUBTRACT", command=self.subtract).grid()
def add(self):
global a
global b
self.a=int(self.entrytext.get())
self.b=int(self.entrytext1.get())
print "result is", self.a+self.b
def subtract(self):
global a
global b
self.a=int(self.entrytext.get())
self.b=int(self.entrytext1.get())
print "result is", self.a-self.b
root= Tk()
root.geometry("300x300")
calc = scanner(root)
root.mainloop()
If you want to show the result of the operation as a label's text, just create a new label and configure it with the text option and the string you are printing as its value. As a side note, you don't need the global statements, and the use of instance variables is not necessary either. However, it is very important to check that the content of the entries are actually valid numbers:
def __init__(self,parent):
# ...
self.result = Label(root, text='')
self.result.grid(row=4, column=0)
def add(self):
try:
a = int(self.entrytext.get())
b = int(self.entrytext1.get())
self.result.config(text=str(a+b))
except ValueError:
print("Incorrect values")
To add entry text to the widget, use the insert method. To replace the current text, you can call delete before you insert the new text.
e = Entry(master)
e.pack()
e.delete(0, END)
e.insert(0, "a default value")
The first parameter in the delete method is which number character to delete from and the second parameter is where to delete too. Notice how END is a tkinter variable.
The parameters for the insert function is where the text will be inserted too, and the second parameter is what will be inserted.
In the future, I recommend going to Effbot and reading about the widget that you are trying to use to find out all about it.