Using python 2.7 and Tkinter.
I am creating four lables in a loop and binding them to . I want the label to return
the name in the label's text.
The problem is that no matter which label I press it returns the name in the last label.
I found this question Python Tkinter: Bind function with labels in for loop with exactly my problem but the solution given does not work for me even if I copied the code exactly.
Please anyone? here is my original code:
# labelbind.py
from Tkinter import *
root = Tk()
root.title('Label choices')
root.geometry('1160x900+650+50')
root.option_readfile('setstyle2.txt')
def lblpress(x):
print 'Label pressed', x
names = ['AMEX', 'CIBC', 'VISA', 'BMO']
col = 150
row = 45
num = 1
for name in names:
bobo = 'lbl' + str(num)
print bobo, name
bobo = Label(root, text = name)
bobo.bind('<ButtonRelease-1>', lambda x = name : lblpress(name))
bobo.place(x = col, y = row)
row += 40
num += 1
root.mainloop()
You don't need to pass anything to the callback. The event object that is given to the callback contains a reference to the widget, and from the widget you can get the text.
For example:
import Tkinter as tk
def lblpress(event):
print 'Label pressed:', event.widget.cget("text")
root = tk.Tk()
names = ['AMEX', 'CIBC', 'VISA', 'BMO']
for name in names:
label = tk.Label(root, text=name)
label.bind("<ButtonRelease-1>", lblpress)
label.pack(side="top")
root.mainloop()
Related
I want to make a kind of chat box, and I would like the letters to be word by word, I did that function but it stays loading until the loop ends, and it gives me the final result, i see in other pages and questions, and i saw that the "after" funtion works, maybe i did something wrong when implementing it, sorry for my english
import tkinter as tk
from tkinter import ttk
import os
from PIL import ImageTk
import PIL.Image
# parent window where is an image of the chatbox
def Ventana_Text_Box(event):
#Ventana De Text box
global ventana_BT
ventana_BT = tk.Tk()
ventana_BT.geometry("300x300+"+str(200)+"+"+str(100))
ventana_BT.configure(background="gray")
I_Text_Box_Image = ImageTk.PhotoImage(I_Text_Box)
Box_Texto = tk.Label(ventana_BT, image = I_Text_Box_Image, bg="gray")
Box_Texto.pack()
Box_Texto.bind("<Button-1>", Ventana_Texto)
Box_Texto.bind("<Button-3>", escribir_texto)
#ventana_BT.wm_attributes("-topmost", 1)
ventana_BT.wm_attributes("-transparentcolor", "gray")
ventana_BT.overrideredirect(1)
ventana_BT.mainloop()
# window where the text will be
def Ventana_Texto(event):
# Ventana hija para el texto
global ventana_T
global W_texto
ventana_T = tk.Toplevel()
ventana_T.geometry("300x300+"+str(ventana_BT.winfo_rootx()-70)+"+"+str(ventana_BT.winfo_rooty()+140))
ventana_T.configure(background="gray")
W_texto = tk.Label(ventana_T, text="", bg="pink")
W_texto.config(fg="black", font=("Consola", 15))
W_texto.pack()
#escribir_texto("Hola")
#ventana_T.wm_attributes("-topmost", 1)
ventana_T.wm_attributes("-transparentcolor", "gray")
ventana_T.overrideredirect(1)
ventana_T.mainloop()
# Function that changes the text from letter to letter
def mecanografiar(texto):
for i in range(len(texto)+1):
return W_texto.config(text=texto[0:i])
# test function to see if it works write "HOLA"
def escribir_texto(event):
texto = "hola"
W_texto.after(400, mecanografiar(texto))
scriptpath = os.path.abspath(__file__)
scriptdir = os.path.dirname(scriptpath)
Text_Box = os.path.join(scriptdir, "Dialogo", "text_box.png")
#800x712
I_Text_Box = PIL.Image.open(Text_Box)
W_I = 350
H_I = W_I*712/800
I_Text_Box = I_Text_Box.resize((W_I,int(H_I)), PIL.Image.ANTIALIAS)
if __name__ == "__main__":
Ventana_Text_Box(None)
import tkinter as tk
root = tk.Tk()
root.geometry('200x200')
# this is whatever string you want to type out slowly
chat_str = 'Hello, friend!'
# storing text in a StringVar will update the label automatically
# whenever the value of the variable is changed (see 'textvariable' below)
text_var = tk.StringVar()
label = tk.Label(textvariable=text_var)
label.pack()
# index represents the character index in 'chat_str'
index = 0
# we need an empty string to store the typed out string as it updates
placeholder = ''
def type_text():
# use 'global' to allow the function to access these variables
global index
global placeholder
try:
# concat the placeholder with the next character in 'chat_str'
placeholder += chat_str[index]
# set 'text_var' to update the label automatically
text_var.set(placeholder)
# go to the next index (character) in 'chat_str'
index += 1
# call this function again after 150mS
# (change this number to modify the typing speed)
root.after(150, type_text)
except IndexError: # when you run out of characters...
return # bail
# NOTE:
# using a 'try:except' block above avoids issues stopping 'root.after()'
type_text()
root.mainloop()
I am trying to create a framework to create a label, text box and button as an object, I can extend it easily.
Idea:
Original
after extend
and it could extend to have 3, 4, 5 or more if it have more file, just declare in dictionary list, it will extend automatically.
The code I have:
def getpath(entry_box, type):
# path or file
if type == 'path':
entry_box.set(filedial.askdirectory())
elif type == 'file':
entry_box.set(filedial.askopenfilename())
MainWin = tk.Tk() # Create main windows
MainWin.title('Get file and path') # App name
# Create root container to hold Frames
mainFrame = ttk.Frame(MainWin)
mainFrame.grid(column=1, row=1)
# define the object to create in dictionary, format:
# {object name:[title name, default path, button name, file or folder path]}
obj2create ={'file1':['ABC Location: ', r'C:/', 'Get ABC','file'],
'file2': ['DEF Location:', r'C:/1/', 'Get DEF', 'file']}
ttl_obj = 0
for key in obj2create:
ttl_obj +=1
vir = obj2create[key]
# Module for get file:
obj_name = key
title = vir[0]
default_path = vir[1]
btn_name = vir[2]
get_type = vir[3]
# Create main container
pa_frame = ttk.Frame(mainFrame)
pa_frame.grid(column=1, row=10*ttl_obj, sticky=tk.W)
pa_frame.config(width = 100)
# Row 1: Label
frame_name = obj_name + '_name'
print(frame_name)
frame_name = ttk.Label(pa_frame, text= title).grid(column=1, row=10*ttl_obj,sticky=tk.W)
# Row 2: path and button
# assign type
path_loc = obj_name + '_path'
path_loc = tk.StringVar()
path_loc.set(default_path)
# put in frame
fileLocPath = obj_name + '_loc_path'
fileLocPath = ttk.Entry(pa_frame, width=70, textvariable=path_loc)
fileLocPath.grid(column=1, row=30*ttl_obj) # Assign position
# Get file button
# define button display text and assign the command / function
bt_get_file_path = obj_name + '_btn'
bt_get_file_path = ttk.Button(pa_frame, text= btn_name,
command=lambda: getpath(path_loc, get_type))
# Position Button in second row, second column (zero-based)
bt_get_file_path.grid(column=2, row=30*ttl_obj)
# Auto popup when open
MainWin.mainloop() # Let the window keep running until close
Issue:
Default file path appear in second text box only.
All the button location point to second box.
I also not sure how could I get the value in different box, the "path_loc = obj_name + '_path'" not able to get the correct object.
How should I make it work? Or the way I use is wrong?
Tkinter widgets are no different than any other python objects with respect to creating them in a loop. Your problem seems to be that you don't know how to create a unique variable for an unknown number of widgets.
The important thing to know is that you don't need a unique variable for each widget. Instead, you can use a list or dictionary, either of which can be easily extended at runtime.
Saving widget references in a dictionary
Here is a solution using a dictionary:
entries = {}
for key in obj2create:
...
entries[key] = ttk.Entry(...)
...
...
print("the value for file1 is", entries["file1"].get()
Creating a custom compound widget
If you're wanting to create sets of widgets that are to be treated as a group, it may be better to create a class. In effect, you're creating your own custom widget. This type of class is often called a "compound widget" or "megawidget".
For example:
class FileWidget(ttk.Frame):
def __init__(self, parent, name):
ttk.Frame.__init__(self, parent)
self.label = ttk.Label(self, text="%s Location" % name)
self.fileLocPath = ttk.Entry(self)
self.bt_get_file_path = ttk.Button(self, text=name, command=self.get_path)
...
def get_path(self):
return self.fileLocPath.get()
You can then create each row in your GUI like this:
widgets = {}
for key in obj2create:
widgets[key] = FileWidget(mainFrame, key)
widgets[key].pack(side="top", fill="x")
Later, you can get back the values like this:
for key in obj2create:
widget = widgets[key]
print("%s: %s" % (key, widget.get_path())
I'm creating a counter to count how many empty cells there are when a user uploads a CSV file. I am also using treeview to display the contents of the CSV. The print("There are", emptyCells.sum(), "empty cells") works and prints the number to the console but I want to display this in a label so the user can view this in the GUI. It is not displaying anything but a "row" is being added to the application after a file has been uploaded where the label should be as everything moves down but no contents are being inserted into the label.
emptyCells = (df[df.columns] == " ").sum()
# print("There are", emptyCells.sum(), "empty cells")
tree.pack(side=BOTTOM, pady=50)
messagebox.showinfo("Success", "File Uploaded Successfully")
stringVariable = StringVar()
printVariable = ("There are", emptyCells.sum(), "empty cells")
#print(printVariable)
stringVariable.set(printVariable)
lbl = Label(windowFrame, textvariable=stringVariable, font=25)
lbl.pack()
According to your question you want to update your tkinter label by a button click. You would do this with something like this:
from tkinter import *
from tkinter import messagebox
root = Tk(className="button_click_label")
root.geometry("200x200")
messagebox.showinfo("Success","Test")
emptyCells = (df[df.columns] == " ").sum()
l1 = Label(root, text="Emptycells?")
def clickevent():
txt = "there are", emptyCells
l1.config(text=txt)
b1 = Button(root, text="clickhere", command=clickevent).pack()
l1.pack()
root.mainloop()
It is not tested with the pandas library but should work for you!
The problem with the tkinter label is not happening when I try to reproduce the problem, the label shows. The cause must be somewhere else in the code.
I've not got pandas installed so I've summed a list instead. This shows a GUI with two labels when I run it.
import tkinter as tk
emptyCells = [ 1, 1, 1, 1, 1, 1, 1 ] # keep it simple.
windowFrame = tk.Tk()
old = tk.StringVar()
stringVariable = tk.StringVar()
old_print = ("There are", sum(emptyCells), "empty cells") # Returns a tuple
printVariable = "There are {} empty cells".format( sum(emptyCells) ) # Returns a string.
old.set( old_print )
stringVariable.set(printVariable)
lbl_old = tk.Label( windowFrame, textvariable = old )
lbl_old.pack()
lbl = tk.Label(windowFrame, textvariable=stringVariable, font=25)
lbl.pack()
windowFrame.mainloop()
Does this work when you run it? Does it help identify where the problem is in the code which doesn't show the labels?
Don't you have the sum you need already in the emptyCells variable? Why do you need to use the .sum() function again in the print statement?
printVariable = f"There are {emptyCells} empty cells"
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I am creating a GUI that is meant to emulate an online shop of some sort.
One part of the task is to have a button that will generate a HTML document with images of the user's chosen product category.
Below I have provided my four radio buttons along with their IntVar and commands.
Each of the RadioButton commands do the same thing but extract information from different websites, so for brevity I have only provided the command for the slipper category.
home_hobbies = Tk()
status = IntVar()
def show_slippers():
#open downloaded file to extract info
slipperfile = open('slippers.html','r',encoding = 'utf-8').read()
prices = findall("<span.*value'>(.*)</span>", slipperfile) #regex prices
titles = findall('<h3.*body ">\n\s*(.*)', slipperfile) #regex titles
select_categ.config(state=NORMAL) #make the text box edit-able
select_categ.delete(1.0, END) #delete any text already in the text box
#for loop to find first five items and print them
for i in range(5):
title = titles[i]
price = prices[i]
result = str(i+1) + ". " + title + ' - $' + price + "\n"
select_categ.insert(END, result) #write list of products
select_categ.config(state=DISABLED) #make sure the user can't edit the text box
slippers = Radiobutton(home_hobbies, command = show_slippers, indicator = 'off', variable = status, value = 1, text = 'Winter Slippers')
diy = Radiobutton(home_hobbies, command = show_diy, indicator = 'off', variable = status, value = 2, text = "DIY Supplies")
#newstock radiobuttons
sports = Radiobutton(home_hobbies, command = show_sports, indicator = 'off', variable = status, value = 3, text = "Pool Toys")
novelties = Radiobutton(home_hobbies, command = show_novelties, indicator = 'off', variable = status, value = 4, text = "Novelty Items")
select_categ = Text(home_hobbies, wrap = WORD, font = content_font, bg = widgetbg, fg = fontcolour, width = 40)
Above, I also provided the line of code that generates the Text widget as it may help in answering my question (I don't have a very deep understanding of this widget despite reading the effbot page about 20 times over).
I now have a different button whose task is to generate a HTML doc with it's own command, "show_img":
htmlshow = Button(home_hobbies, text = "View Product Images", command = show_img)
I am trying to make the show_img() command work such that I have a preamble of HTML coding, and then, depending on which radibutton has been chosen, the function will replace sections of the code with the corresponding information:
def show_img():
#in this section I write my HTML code which includes replaceable sections such as "image1" and source_url
if slipper_trig:
table = table.replace("source_url", ' Etsy - Shop Unique Gifts for Everyone')
imgfile = open('slippers.html', 'r', encoding = 'utf-8').read()
images = findall('<img\n*.*image\n*\s*src="(.*)"', imgfile)
for i in range(5):
image = images[i]
table = table.replace("image"+str(i+1), image)
I tried to add BooleanVar into the commands for my Radio Buttons like this:
slipper_trig = False
diy_trig = False
pool_trig = False
novelty_trig = False
#Function for the product category buttons
#
def show_slippers():
#make selected category true and change all others to false
slipper_trig = True
diy_trig = False
pool_trig = False
novelty_trig = False
As a way to distinguish between the categories but the GUI clearly doesn't remember the value of "slipper_trig" after its been defined as true in the "show_slippers" function.
Maybe I need to try and integrate the "show_img" command into my original functions that define the RadioButtons? Maybe I should be figuring out how to determine the category chosen by what's shown in the text box?
Any advice would be appreciated.
Thanks!
You didn't show minimal working code with your problem so I can only show some minimal example with Button and RadioButton to show how to use these widgets.
I don't know if you used command=function_name in Button.
BTW: it has to be function's name without ()
I don't know if you used .get() to get value from StringVar/Intvar/BooleanVar assigned to RadioButtons.
EDIT I added Checkbutton because probably you may need it instead of Radiobutton
import tkinter as tk
# --- functions ---
def on_click():
selected = result_var.get()
print('selected:', selected)
if selected == 'hello':
print("add HELLO to html")
elif selected == 'bye':
print("add BYE to html")
else:
print("???")
print('option1:', option1_var.get()) # 1 or 0 if you use IntVar
print('option2:', option2_var.get()) # 1 or 0 if you use IntVar
if option1_var.get() == 1:
print("add OPTION 1 to html")
if option2_var.get() == 1:
print("add OPTION 2 to html")
# --- main ---
root = tk.Tk()
result_var = tk.StringVar(root, value='hello')
rb1 = tk.Radiobutton(root, text="Hello World", variable=result_var, value='hello')
rb1.pack()
rb2 = tk.Radiobutton(root, text="Good Bye", variable=result_var, value='bye')
rb2.pack()
option1_var = tk.IntVar(root, value=0)
opt1 = tk.Checkbutton(root, text='Option 1', variable=option1_var)
opt1.pack()
option2_var = tk.IntVar(root, value=0)
opt2 = tk.Checkbutton(root, text='Option 2', variable=option2_var)
opt2.pack()
button = tk.Button(root, text='OK', command=on_click)
button.pack()
root.mainloop()
Doc on effbot.org: Button, Radiobutton, Checkbutton
I created 12 Entry boxes using a for loop with a default value of N/A. Any change in the text of entry is detected through .trace method.
I want to use reset button to make all the text on the Entry Boxes back to N/A
from tkinter import *
root = Tk()
t_diesel_price_EnF_variable = ["JanVar", "FebVar", "MarVar", "AprVar","MayVar","JuneVar","JulyVar","AugVar","SeptVar", "OctVar", "NovVar", "DecVar"]
t_diesel_price_EnF_values = ["N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A"]
def EnFChanging(*events):
for EnF in range(0,len(t_diesel_price_EnF_variable)):
t_diesel_price_EnF_values[EnF]=t_diesel_price_EnF_variable[EnF].get()
try:
t_diesel_price_EnF_values[EnF] = float(t_diesel_price_EnF_values[EnF])
except ValueError:
pass
print(t_diesel_price_EnF_values)
for EnF in range(0,len(t_diesel_price_EnF_values)):
t_diesel_price_EnF_variable[EnF] = StringVar(root , value = "N/A")
t_diesel_price = Entry(root , textvariable = t_diesel_price_EnF_variable[EnF], width = 10).pack()
t_diesel_price_EnF_variable[EnF].trace("w",EnFChanging)
def ChangeText():
for EnF in range(0, len(t_diesel_price_EnF_values)):
t_diesel_price[EnF].delete(0,END)
t_diesel_price[EnF].insert(0,"N/A")
return
b1 = Button(root, text = "Reset" , command = ChangeText).pack()
root.mainloop()
When I press the button it gives an error t_diesel_price[EnF].delete(0,END)
TypeError: 'NoneType' object is not subscriptable
What should I do now, Please ignore the basic errors of programming as I am a Mechanical Engineer with not a programming back ground. And I have to make a lot of other boxes too for my energy calculator.
You trying treat t_diesel_price as an Entry (and as a list) when your variable is None.
First of all I suggest you to install some IDE (e.g. PyCharm) and place break points to see whats wrong with variable!
Your problem occures because you create and pack your widget in one line! So t_diesel_price is None because pack() always returns None (link).
Just split your declaration and packing to:
t_diesel_price = Entry(root , textvariable = t_diesel_price_EnF_variable[EnF], width = 10)
t_diesel_price.pack()
After that it's works for me, except this fact that t_diesel_price is last created entry and the value changes only in it. So I assume that you need another list to iterate over entries:
...
# another fresh list
t_diesel_price_EnF_entries = list()
...
# declare entry
t_diesel_price = Entry(root, textvariable=t_diesel_price_EnF_variable[EnF], width=10)
# pack entry
t_diesel_price.pack()
# append entry to list
t_diesel_price_EnF_entries.append(t_diesel_price)
...
def ChangeText():
# iterate over entries
for diesel_price in t_diesel_price_EnF_entries:
diesel_price.delete(0,END)
diesel_price.insert(0,"N/A")
...
Alternatively you can iterate over StringVar's if you don't wanna store your entries at all:
def ChangeText():
# iterate over stringvars
for EnF in range(len(t_diesel_price_EnF_variable)):
t_diesel_price_EnF_variable[EnF].set('N/A')
And you can make it more readable as in example with entry iterating:
def ChangeText():
# iterate over stringvars
for string_var in t_diesel_price_EnF_variable:
string_var.set('N/A')
Cheers!