I'm playing around with option menu. I have a list of countries called options. The option menu is set to the first index of options. How can I update the value if a user clicks on a different country? Basically the function doesn't work even though I click on the 2nd (options[1]) country in the option menu.
def first_country():
from_country = start_clicked.get()
if from_country == options[1]:
my_pic = Image.open("usa_flag.png")
resized = my_pic.resize((200, 100), Image.ANTIALIAS)
new_pic = ImageTk.PhotoImage(resized)
flag_label = Label(root, image=new_pic)
flag_label = Label(root, text="function works")
flag_label.grid(row=3, column=0)
start_clicked = StringVar()
start_clicked.set(options[0])
dropdown = OptionMenu(root, start_clicked, *options, command=first_country())
command=first_country() should be command=first_country instead. The former one will execute the function immediately and assign None (result of the function) to command option.
Also the callback for command option of OptionMenu expects an argument which is the selected item:
def first_country(from_country):
if from_country == options[1]:
my_pic = Image.open("usa_flag.png")
resized = my_pic.resize((200, 100), Image.ANTIALIAS)
new_pic = ImageTk.PhotoImage(resized)
# better create the label once and update its image here
flag_label.config(image=new_pic)
flag_label.photo = new_pic # save a reference of the image
...
dropdown = OptionMenu(root, start_clicked, *options, command=first_country)
...
# create the label for the flag image
flag_label = Label(root)
flag_label.grid(row=3, column=0)
...
Note that I have created the label for the flag image once and update its image inside the function instead. Also you need to save the reference of the image if it is created inside a function, otherwise it will be garbage collected.
In Python, whenever you add () to the end of a function, it invokes it. (aside from declaration)
This being the case, it is usually so that when you are to pass a function to something, you need only pass a reference to the function
Effectively, just remove the ()
dropdown = OptionMenu(root, start_clicked, *options, command=first_country)
Comment from acw1668 explained it well:
command=first_country() should be command=first_country instead. The former one will execute the function immediately and assign None to command.
Related
newWindow = Toplevel(app)
newWindow.geometry("400x400+580+190")
Label(newWindow, text="choose the food").pack()
choice = Listbox(newWindow)
choice.pack()
food_f = open("food.txt")
for line in food_f:
f = {}
(f['food'], f['unit'], f['kcal'], f['standard']) = line.split(";")
choice.insert(END, f['food'])
t = StringVar()
t.set(choice.curselection())
Label(newWindow,textvariable=t).pack()
I want the label to print out in real time what is selected in the list box.
But above code can't.
You can create a binding on the virtual event <<ListboxSelect>>, and from within the bound function you can get the selected item and update the label using the configure method.
For example, given a listbox named listbox and a label named label, you can update the label like this when the selection changes:
def update_label(event):
curselection = listbox.curselection()
if curselection:
data = listbox.get(curselection[0])
label.configure(text=data)
else:
label.configure(text="")
listbox.bind("<<ListboxSelect>>", update_label)
I am trying to update the canvas label outside the function that canvas was created.
def Application_GUI():
global Scanned_serial
global label1
global label2
global window
global label_gaminys
global canvas_tk
window = Tk() # create a GUI window
window.geometry("1920x1080") # set the configuration of GUI window
canvas_tk = Canvas(window,bg='ivory2',width=1920,height=1080)
canvas_tk.pack()
label1=Label(canvas_tk,text = "SKENUOKITE BARKODA(GUID) ARBA DAIKTO RIVILINI KODA:",bg='ivory2')
entry = Entry(canvas_tk) # entry = guid
canvas_tk.create_window(960,50,window=label1)
canvas_tk.create_window(960,100,window=entry)
var = IntVar()
button = Button(canvas_tk,text="Testi operacija",width = 30,height=2,command = lambda: var.set(1))
button2 = Button(canvas_tk,text="RESTART DEVICES",width = 30,height=2,command = lambda:restart_devices(myConnection))
ota_button = Button(canvas_tk, text="OTA", width=30, height=1, command=OTA_gui)
canvas_tk.create_window(960,150,window=button)
canvas_tk.create_window(960,200,window=button2)
canvas_tk.create_window(960,250,window=ota_button)
# ********************* IMPORTANT PART *************************
label_gaminys=Label(canvas_tk,text = "GAMINIO KODAS:",bg='ivory2')
canvas_tk.create_window(960,450,window=label_gaminys)
# ***************************************************************
print("waiting...")
button.wait_variable(var)
result = entry.get()
print("result=",result)
Scanned_serial = entry.get()
label2=Label(window,text = "Vykdoma operacija:")
label2.pack()
window.update()
In the function above, I am creating my user interface using a canvas. The important line of code is there:
label_gaminys=Label(canvas_tk,text = "GAMINIO KODAS:",bg='ivory2')
canvas_tk.create_window(960,450,window=label_gaminys)
I have created a window for a text at location 960,450.
I want to update this label outside this GUI function during a operation .
def Full_operation():
#Destroy previous window
window.destroy()
#create a new GUI window
Application_GUI()
global canvas_tk
operacijos_kodas=Scanning_operation(myConnection,Scanned_serial)
elif(operacijos_kodas == 1):
insertData_komplektacija(myConnection,"fmb110bbv801.csv");
update_current_operation(myConnection);
#label2.config(text = "Take items from the box:")#update the label2
label_gaminys=Label(canvas_tk,text = "Gamninio kodas=%s"%(Scanned_serial),bg='ivory2')
canvas_tk.create_window(960,450,window=label_gaminys)
picking_operation(myConnection,label2);
The function above describes the operatio. I want to modify the label inside this function. I have described my canvas_tk as global and initialise in this function so I can access and modify it. I have managed to update the label by creating a new window as following:
label_gaminys=Label(canvas_tk,text = "Gamninio kodas=%s"%(Scanned_serial),bg='ivory2')
canvas_tk.create_window(960,450,window=label_gaminys)
picking_operation(myConnection,label2);
But that does not seem like a correct way to do that since I am not actually "updating" the label, instead I am creating a new window and assigning it to a new label.
Could someone give me some general advice on how to do that properly?
You can just create a function that does this:
label_gaminys["text'] = "" #Insert the text you want into it and then the text changes
and then you can modify it whilst being in the same window. On another note, do notice that your Full_operation has an elif in it without an if first (just notifying you, because if there is no if before it it will cause an error).
I am trying to create a program that allows the user to select any number of check boxes and hit a button to return a random result from those check boxes. Since I am basing my list off the roster of Smash bros ultimate, I am trying to avoid creating 70+ variables just to place check boxes. However, I am unable to figure out how to iterate this. The various values set for rows are just placeholders until I can figure this out. I would also like to have a reset button at the top that allows the user to automatically uncheck every box. This code is what I have so far. Any help would be greatly appreciated.
#!/usr/bin/python3
from tkinter import *
window = Tk()
#window name and header
window.title("Custom Random SSBU")
lbl = Label(window, text="Select the fighters you would like to include:")
lbl.grid(column=1, row=0)
f = [] #check boxes
ft = open("Fighters.txt").readlines() #list of all the character names
fv=[0]*78 #list for tracking what boxes are checked
ff=[] #list to place final character strings
def reset():
for i in fv:
fv[i]=0
rst = Button(window, text="Reset", command=reset)
rst.grid(column=0, row=3)
for y in range (0,77):
f[y] = Checkbutton(window, text = ft[y], variable = fv[y])
f[y].grid(column=0, row=4+y)
def done():
for j in fv:
if fv[j] == 1:
ff.append(fv[j])
result = random.choice(ff)
r=Label(window, text=result)
d = Button(window, text="Done", command=done)
d.grid(column=0, row = 80)
window.mainloop()
Unfortunately I'm afraid you are going to have to create variables for each checkbox.
tkinter has special purpose Variable Classes for holding different types of values, and if you specify an instance of one as the variable= option when you create widgets like Checkbutton, it will automatically set or reset its value whenever the user changes it, so all your program has to do is check its current value by calling its get() method.
Here's an example of the modifications to your code needed to create them in a loop (and use them in the done() callback function):
import random
from tkinter import *
window = Tk()
#window name and header
window.title("Custom Random SSBU")
lbl = Label(window, text="Select the fighters you would like to include:")
lbl.grid(column=1, row=0)
with open("Fighters.txt") as fighters:
ft = fighters.read().splitlines() # List of all the character names.
fv = [BooleanVar(value=False) for _ in ft] # List to track which boxes are checked.
ff = [] # List to place final character strings.
def reset():
for var in fv:
var.set(False)
rst = Button(window, text="Reset", command=reset)
rst.grid(column=0, row=3)
for i, (name, var) in enumerate(zip(ft, fv)):
chk_btn = Checkbutton(window, text=name, variable=var)
chk_btn.grid(column=0, row=i+4, sticky=W)
def done():
global ff
ff = [name for name, var in zip(ft, fv) if var.get()] # List of checked names.
# Randomly select one of them.
choice.configure(text=random.choice(ff) if ff else "None")
d = Button(window, text="Done", command=done)
d.grid(column=0, row=len(ft)+4)
choice = Label(window, text="None")
choice.grid(column=1, row=3)
window.mainloop()
I wasn't sure where you wanted the Label containing the result to go, so I just put it to the right of the Reset button.
variable = fv[y]
This looks up the value of fv[y] - i.e, the integer 0 - at the time the Checkbutton is created, and uses that for the variable argument.
You need to use an instance of one of the value-tracking classes provided by TKinter, instead. In this case we want BooleanVar since we are tracking a boolean state. We can still create these in a list ahead of time:
text = open("Fighters.txt").readlines()
# Let's not hard-code the number of lines - we'll find it out automatically,
# and just make one for each line.
trackers = [BooleanVar() for line in text]
# And we'll iterate over those pair-wise to make the buttons:
buttons = [
Checkbutton(window, text = line, variable = tracker)
for line, tracker in zip(text, trackers)
]
(but we can not do, for example trackers = [BooleanVar()] * len(text), because that gives us the same tracker 78 times, and thus every checkbox will share that tracker; we need to track each separately.)
When you click the checkbox, TKinter will automatically update the internal state of the corresponding BooleanVar(), which we can check using its .get() method. Also, when we set up our options for random.choice, we want to choose the corresponding text for the button, not the tracker. We can do this with the zip trick again.
So we want something more like:
result_label = Label(window) # create it ahead of time
def done():
result_label.text = random.choice(
label
for label, tracker in zip(text, trackers)
if tracker.get()
)
I have the following Entry box where due to obtaining values I have put a list option in for textvariable.
However I was wondering if it would be possible to put a default text in the background so to show which values are required in each box (like a greyscale text, 'Value 1, value 2 etc..).
self.numbers = [StringVar() for i in xrange(self.number_boxes) ] #Name available in global scope.
box=Entry(self.frame_table,bg='white',borderwidth=0, width=10, justify="center", textvariable=self.numbers[i])
Can I add in maybe something change 'textvariable' upon a mouse click inside the box or can I simply just add in another textvariable or text to set a default text?
self.box = []
for i in xrange(self.number_boxes):
self.clicked = False
self.box.append(Entry(self.frame_table,bg='white',borderwidth=0, width=10, justify="center", textvariable=self.numbers[i], fg='grey'))
self.box[i].grid(row=row_list,column=column+i, sticky='nsew', padx=1, pady=1)
self.box[i].insert(0, "Value %g" % float(i+1))
self.box[i].bind("<Button-1>", self.callback)
In order to put default text in your Entry widget, you can use the insert() method as described here.
box.insert(0, "Value 1") # Set default text at cursor position 0.
Now in order to change the contents of box after the user performs a mouse click inside the box, you will need to bind an event to the Entry object. For example, the following code deletes the contents of the box when it is clicked. (You can read about event and bindings here.) Below I show a full example of this.
Note that deleting the text in the box is probably only practical for the first click (i.e. when deleting the default contents), so I created a global flag clicked to keep track of whether it has been clicked.
from tkinter import Tk, Entry, END # Python3. For Python2.x, import Tkinter.
# Use this as a flag to indicate if the box was clicked.
global clicked
clicked = False
# Delete the contents of the Entry widget. Use the flag
# so that this only happens the first time.
def callback(event):
global clicked
if (clicked == False):
box[0].delete(0, END) #
box[0].config(fg = "black") # Change the colour of the text here.
clicked = True
root = Tk()
box = [] # Declare a list for the Entry widgets.
box.append(Entry(fg = "gray")) # Create an Entry box with gray text.
box[0].bind("<Button-1>", callback) # Bind a mouse-click to the callback function.
box[0].insert(0, "Value 1") # Set default text at cursor position 0.
box.append(Entry(fg = "gray")) # Make a 2nd Entry; store a reference to it in box.
box[1].insert(0, "Value 2")
box[0].pack() #
box[1].pack()
if __name__ == "__main__":
root.mainloop()
I made a very simple gui that has a button and shows an image(.gif). My goal is to output another .gif whenever you press the button. There are 2 .gif files in my file directory and the point is to keep switching between these two whenever you press the button.
#Using python2.7.2
import Tkinter
root = Tkinter.Tk()
try:
n
except:
n = 0
def showphoto(par):
if par%2 == 0:
try:
label2.destroy()
except:
pass
photo = Tkinter.PhotoImage(file="masc.gif")
label2 = Tkinter.Label(image=photo)
label2.image = photo
label2.pack()
else:
try:
label2.destroy()
except:
pass
photo = Tkinter.PhotoImage(file="123.gif")
label2 = Tkinter.Label(image=photo)
label2.image = photo
label2.pack()
myContainer1 = Tkinter.Frame(root, width = 100, height = 100)
myContainer1.pack()
def callback(event):
global n
showphoto(n)
n = n + 1
button1 = Tkinter.Button(myContainer1)
button1["text"]= "Next pic"
button1["background"] = "green"
button1.bind("<Button-1>", callback(n))
button1.pack()
root.mainloop()
The current code just outputs the first image (masc.gif) but when I press the button it doesn't switch to the other image(123.gif). What am I doing wrong?
This can achieved much easier with classes as the class holds all the data necessary without the use of global variables.
import Tkinter as tk
from collections import OrderedDict
class app(tk.Frame):
def __init__(self,master=None, **kwargs):
self.gifdict=OrderedDict()
for gif in ('masc.gif','123.gif'):
self.gifdict[gif]=tk.PhotoImage(file=gif)
tk.Frame.__init__(self,master,**kwargs)
self.label=tk.Label(self)
self.label.pack()
self.button=tk.Button(self,text="switch",command=self.switch)
self.button.pack()
self.switch()
def switch(self):
#Get first image in dict and add it to the end
img,photo=self.gifdict.popitem(last=False)
self.gifdict[img]=photo
#display the image we popped off the start of the dict.
self.label.config(image=photo)
if __name__ == "__main__":
A=tk.Tk()
B=app(master=A,width=100,height=100)
B.pack()
A.mainloop()
Of course, this could be done more generally ... (the list of images to cycle through could be passed in for example), and this will switch through all the images in self.gifs ...
This approach also removes the necessity to destroy and recreate a label each time, instead we just reuse the label we already have.
EDIT
Now I use an OrderedDict to store the files. (keys=filename,values=PhotoImages). Then we pop the first element out of the dictionary to plot. Of course, if you're using python2.6 or earlier, you can just keep a list in addition to the dictionary and use the list to get the keys.
button1 = Tkinter.Button(myContainer1)
button1["text"]= "Next pic"
button1["background"] = "green"
button1.bind("<Button-1>", callback(n))
First, you bind the <Button-1> event to None (that's what callback(n) evaluates to). You should bind it to callback (no parentheses a.k.a the call operator).
Second, I suggest you change callback to not accept any arguments, remove the bind call and create your button as:
button1 = Tkinter.Button(myContainer1, command=callback)