Create Tkinter buttons in a loop that reference each other - python

I'm trying to create a series of tkinter buttons with a loop that are .grid'd to their own respective frames. I want every button to have a function that .tkraises the next frame in the list of frames that I create. Any idea how? Here's what I've got. The buttons/ frames are created I think but the .tkraise function doesn't work. Thanks
from tkinter import *
from PIL import ImageTk, Image
## Define root and geometry
root = Tk()
root.geometry('200x200')
# Define Frames
winlist = list()
winlist = Frame(root, bg='red'), Frame(root, bg='green'), Frame(root, bg='blue')
# Configure Rows
root.grid_rowconfigure(0, weight = 1)
root.grid_columnconfigure(0, weight = 1)
# Place Frames
for window in winlist:
window.grid(row=0, column = 0, sticky = 'news')
# Raises first window 'To the top'
winlist[0].tkraise()
# Function to raise 'window' to the top
def raise_frame(window):
window.tkraise()
d = {}
count = 0
for x in range(0, 3):
d["label{0}".format(x)] = Label(winlist[x], text = "label{0}".format(x))
if count <=1:
try:
d["button{0}".format(x)] = Button(winlist[x], text = "button{0}".format(x), command = raise_frame(winlist[x+1]))
d["button{0}".format(x)].pack(side=TOP)
except:
pass
else:
d["label{0}".format(x)].pack(side=TOP)
count += 1
root.mainloop()

The issue is on the command option of the line:
d["button{0}".format(x)] = Button(winlist[x], text = "button{0}".format(x), command = raise_frame(winlist[x+1]))
It will execute raise_frame(winlist[x+1]) immediately and then assign the result (which is None) to command option. Therefore, clicking the button later does nothing.
You need to use lambda instead:
d["button{0}".format(x)] = Button(winlist[x], text="button{0}".format(x),
command=lambda x=x: raise_frame(winlist[x+1]))

I answered my own question. instead of using frames I went back to creating Tk() objects. I made a loop that runs a function that creates Tk() objects and passed in a variable that carried the count of my loop. I used that count to change information on each Tk() object and instead made the 'command =' of each button include a Tk().destroy function. This creates all the windows I wanted all at once and I can perform an action and exit the window. It's progress. Thanks,
Tim,

Related

How to delete frame on click of button? Python tkinter

import tkinter as tk
global counter
counter = 0
def addframe():
global counter
newtask_frame = tk.Frame(highlightbackground="green",highlightthickness=1, master = intrldisp_frame)
newtaskheading_label = tk.Label(newtask_frame, text="Added frame")
newtaskheading_label.grid(row=0,column=0)
newtaskdel_button = tk.Button(newtask_frame,text="del", command = newtask_frame.grid_forget())
newtaskdel_button.grid(row=1,column = 0,pady=5,padx=10)
newtask_frame.grid(row=counter,column=0,sticky = "nsew",pady=10,padx=5)
counter+=1
window = tk.Tk()
tasksinfo_frame = tk.Frame(highlightbackground="red",highlightthickness=2)
tasksinfo_frame.grid(row=0,pady=10)
addtask_button = tk.Button(tasksinfo_frame, text = "add",activebackground="blue",command = addframe)
addtask_button.pack(fill=tk.BOTH)
intrldisp_frame = tk.Frame(highlightbackground="blue",highlightthickness=1, master = window)
intrldisp_frame.grid(row=1,column=0,sticky = "n", padx=5)
window.mainloop()
So, I wrote a function for a button that creates a frame on click as can be read from the code above. I have also added a delete button in each frame that gets added, but I cannot figure out how to get the delete button to work.
How do I delete the frame when clicked on the del button? Is there any way to add id to them= frames? Do I have to use OOP for this?
Thank you.
How do I delete the frame when clicked on the del button?
You need to call the destroy method of the frame.
newtaskdel_button = tk.Button(..., command = newtask_frame.destroy)

Python tkinter updating canvas label outside the main function

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).

Tkinter - Update changes in variables in previously generated widgets, from dictionary

I need to represent in "ventana2" the pairs entered in "ventana", so that a new frame appears when the key is new. When the key already exists in the dictionary, I need to change the old value in the frame created for that key previously (the new value is adding old and new).
I can not get the frames permanently related to my dictionary partner, through the key.
Thank you very much in advance, and sorry for my english.
Here is a summary of the code:
import tkinter as tk
ventana = tk.Tk()
ventana2 = tk.Tk()
name = tk.StringVar()
tk.Entry(ventana, textvariable=name, width=30).grid(row=0, column=1)
tk.Label(ventana, text = 'Nombre').grid(row=0, column=0)
value = tk.StringVar()
tk.Entry(ventana, textvariable=value, width=30).grid(row=1, column=1)
tk.Label(ventana, text = 'Celular').grid(row=1, column=0)
contactos={}
def intro():
nom = name.get()
if nom in contactos:
cel = contactos[nom] + float(value.get())
contactos[nom] = cel
else:
cel = float(value.get())
contactos[nom] = cel
create_widget()
def create_widget():
frame = tk.Frame(ventana2)
frame.pack()
nomb = tk.Label(frame, text=name.get()).pack(side=tk.LEFT)
telf = tk.Label(frame, text=contactos[name.get()]).pack(side=tk.RIGHT)
intro_btn = tk.Button(ventana, text='Intro', command = intro)
intro_btn.grid(row=2, column=0, columnspan=2, sticky = 'ew')
ventana.mainloop()
Labels created inside the create_widget() function is in the function scope. After the function ends all references to the label is lost. So you need to think of a way to save references to the label (suggest return statement) and associate them to the dictionary of contactos.
Update - relate a frame with a value
Save a reference to the object you wish to remember and the name you want to use to recall it in a list(or dict or tuple et.). Then append all that to your global list of widgets. For example:
nomb = tk.Label( ... )
widget_parameters = ['Nomb Label', nomb]
global_widget_list.append(widget_parameters)
Then you can search the global_widget_list for any widget you have named and get a referance to that widget.
I have included some example code to illustrate one way that you can accomplish that. Play around with it until you understand it and you will be able to implement it in your own application.
from tkinter import *
import time
root = Tk()
root.geometry('300x200')
global_widget_list = [] # List for holding all widgets
def make_label(): # Create label
text_label = Label(root,text='Text input')
text_label.pack()
global_widget_list.append(['Text Label',text_label]) # Add widget to list
def make_input(): # Create entry
inputvar = StringVar()
text_input = Entry(root,width=20,textvariable=inputvar)
text_input.pack()
global_widget_list.append(['Text Input',text_input,inputvar]) # Add widget to list
make_label() # Run functions to cretae GUI
make_input()
root.update() # Take care of updating GUI
time.sleep(3) # Wait so you have time to see the original, then change it
# Now, loop through all widgets and do what you want
for widget in global_widget_list:
widget_name = widget[0]
widget_id = widget[1]
print(widget_name, 'has identity', widget_id)
if widget_name == 'Text Label':
print('Changing test label text to: ALL CAPS')
widget_id.configure(text='ALL CAPS')
if widget_name == 'Text Input':
print('Changing text in entry to: SAMPLE')
var = widget[2]
var.set('SAMPLE')
root.update() # Take care of updating GUI
time.sleep(3) # Wait so you have time to see the changes, then change it
print('Removing Entry from application GUI')
global_widget_list[1][1].pack_forget() # Remove entry form GUI

Trying to put drop down list on a code using Tkinter

I'm tring to put some drop down list on a graphic interface I'm building.
I've found the following code for a drop down list, but I'm not able to adapt it to my code.
from Tkinter import *
def print_it(event):
print var.get()
root = Tk()
var = StringVar()
var.set("a")
OptionMenu(root, var, "a","b","c", command=print_it).pack()
root.mainloop()
This is my code, it's quite simple what I've done so far. A menu shows up, it asks for how many (n) components does the users want to enter, and it shows n options to entry. The code above shows 'blank' entrys after you put the desired number of components. I want to replace those three blank entrys with three drop down list.
It's marked when I want to put those dropdown lists.
from Tkinter import *
import Image
import ImageTk
import tkFileDialog
class Planificador:
def __init__(self,master):
master.title("Planificador")
self.frameOne = Frame(master)
self.frameOne.grid(row=0,column=0)
# Logo
self.imgLabel = Label(self.frameOne, image = None)
self.imgLabel.grid(row=0,column=0)
self.img = ImageTk.PhotoImage(file = "logo.png")
self.imgLabel["image"] = self.img
self.botones()
def botones(self):
self.piezastext = Label(self.frameOne, text = " number of components ", justify="center")
self.piezastext.grid(row=1, column=0)
self.entrypiezas = Entry(self.frameOne,width=5)
self.entrypiezas.grid(row=2, column=0)
self.aceptarnumpiezas = Button(self.frameOne,text="Aceptar", command=self.aceptar_piezas,width=8)
self.aceptarnumpiezas.grid(row=6, column=0)
def aceptar_piezas(self):
num_piezas = self.entrypiezas.get()
print num_piezas
self.piezastext.grid_remove()
self.entrypiezas.grid_remove()
self.aceptarnumpiezas.grid_remove()
n = 1;
while n <= int(num_piezas):
self.textopieza = Label(self.frameOne, text = "Pieza", justify="left")
self.textopieza.grid(row=n, column=0)
// INSTEAD THESE 'n' BLANK ENTRYS, I WANT TO PUT 'n' DROP DOWN LISTS
self.entrypiezas = Entry(self.frameOne,width=5)
self.entrypiezas.grid(row=n, column=1)
self.aceptarpiezas = Button(self.frameOne,text="Aceptar",width=8)
self.aceptarpiezas.grid(row=int(num_piezas)+1, column=0)
n += 1
# Main
if __name__ == "__main__":
# create interfacE
root = Tk()
movieApp = Planificador(root)
root.mainloop()
So I want to know how can I put that drop down list on a given frame, frameOnein my case, instead of a full window. Thanks in advance.
I modified your aceptar_piezas function to do what I think you want:
def aceptar_piezas(self):
num_piezas = self.entrypiezas.get()
print num_piezas
self.piezastext.grid_remove()
self.entrypiezas.grid_remove()
self.aceptarnumpiezas.grid_remove()
# Create a list of tuples to hold the dynamically created Optionmenus
# The first item in the tuple is the menu, the second is its variable
self.optionmenus = list()
n = 1
while n <= int(num_piezas):
self.textopieza = Label(self.frameOne, text = "Pieza", justify="left")
self.textopieza.grid(row=n, column=0)
# Variable for the Optionmenu
var = StringVar()
# The menu
menu = OptionMenu(self.frameOne, var, "a","b","c")
menu.grid(row=n, column=1)
# Set the variable to "a" as default
var.set("a")
# Add the menu to the list of Optionmenus
self.optionmenus.append((menu, var))
n += 1
def clicked():
"""This function was made just to demonstrate. It is hooked up to the button"""
for optionmenu in self.optionmenus:
print optionmenu[1].get()
print self.optionmenus
# This button doesn't need to be in the while loop
self.aceptarpiezas = Button(self.frameOne, text="Aceptar", command=clicked, width=8)
self.aceptarpiezas.grid(row=int(num_piezas)+1, column=0)
The tuples in the list are in the order that the Optionmenus were created. So, the first tuple contains the data for the first Optionmenu, the second for the second, and so forth.

Simple gui that display a repeated set of images when a button is pushed

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)

Categories

Resources