how to hide labels in python until a button is pressed - python

I'm building a flashcard app and want a label to pop up when the user gets the answer correct. I was wondering how to make it so that both labels are hidden until one of the buttons is pressed and then only one would show up(and preferably disappear when the next button is pressed). My code so far is below.
from tkinter import *
from PIL import ImageTk, Image
from random import randint
import random
root = Tk()
root.title('Chemistry Flashcards')
root.geometry("500x500")
def balancing():
hide_all_frames()
balancing_frame.pack(fill="both", expand=1)
global show_balancing
show_balancing = Label(balancing_frame)
show_balancing.pack(pady=15)
global balancing_list
balancing_list = ['balanced1', 'balanced2', 'balanced3', 'balanced4', 'balanced5', 'unbalanced1', 'unbalanced2', 'unbalanced3', 'unbalanced4', 'unbalanced5']
global balanced_list
balanced_list = balancing_list[:5]
global unbalanced_list
unbalanced_list = balancing_list[5:10]
global rando_image
rando_image = random.choice(balancing_list)
global balancing_image
balancing1 = "C:/Users/Kisitu/Desktop/project/balancing/" + rando_image + ".png"
balancing_image = ImageTk.PhotoImage(Image.open(balancing1))
show_balancing.config(image=balancing_image)
global balanced_button
balanced_button = Button(balancing_frame, text = 'balanced', command = balancing_answer).pack()
global unbalanced_button
unbalanced_button = Button(balancing_frame, text = 'unbalanced', command = balancing_answer).pack()
global balanced_label
balanced_label = Label(balancing_frame, text='It was balanced', font=("Helvetica",18), bg='#B3FDFF')
balanced_label.pack(pady=15)
global unbalanced_label
unbalanced_label = Label(balancing_frame, text='It was unbalanced', font=("Helvetica",18), bg='#B3FDFF')
unbalanced_label.pack(pady=15)
balancing()
def hide_all_frames():
for widget in balancing_frame.winfo_children():
widget.destroy()
balancing_frame.pack_forget()
balancing_frame = Frame(root, width=500, height=500, )
my_menu = Menu(root)
root.config(menu=my_menu, bg='#B7F7BB')
#menu options(elements and compound)
lesson_menu = Menu(my_menu)
my_menu.add_cascade(label="Lesson", menu=lesson_menu)
lesson_menu.add_command(label="balancing", command=balancing)
lesson_menu.add_separator()
lesson_menu.add_command(label="Exit", command=root.quit)
'''
end
'''
root.mainloop()

add functions to both buttons
def balancing_answer(): #this will make the label to show and hide
balanced_label.pack(pady=15)
unbalanced_label.pack_forget()
def unbalancing_answer(): #this will make the label to show and hide
balanced_label.pack_forget()
unbalanced_label.pack(pady=15)
global balanced_button
balanced_button = Button(balancing_frame, text = 'balanced', command = balancing_answer).pack()
global unbalanced_button
unbalanced_button = Button(balancing_frame, text = 'unbalanced', command = unbalancing_answer).pack() # change the command to unbalancing_answer
global balanced_label
balanced_label = Label(balancing_frame, text='It was balanced', font=("Helvetica",18), bg='#B3FDFF')
global unbalanced_label
unbalanced_label = Label(balancing_frame, text='It was unbalanced', font=("Helvetica",18), bg='#B3FDFF')
# balancing() # no need for this
If I get your question correctly; this will make the labels to pop up and disappear.

from tkinter import *
from PIL import ImageTk, Image
from random import randint
import random
root = Tk()
root.title('Chemistry Flashcards')
root.geometry("500x500")
def balancing_answer():
balancing_frame.pack()
balanced_label.pack(pady=15)
unbalanced_label.pack_forget()
show_balancing.config(image=balancing_image)
def unbalancing_answer():
balancing_frame.pack_forget()
balanced_label.pack_forget()
unbalanced_label.pack(pady=15)
show_balancing.config(image=balancing_image)
#two frames-unbalancing frame and balancing frame
balancing_frame = Frame(root)
unbalancing_frame=Frame(root)
#this is the show balancing frame
show_balancing = Label(balancing_frame)
show_balancing.pack()
#only one image is specified. there can be no random images.but the image will flash up at a click and disappear at another click
img = Image.open('p.jpg').convert("RGBA")
w, h = img.size
left = w/5
right = 3*w/5
upper = h/5
lower = 3*h/5
img2 = img.crop([ left, upper, right, lower]) #this part is used to crop the image. you can choose to ignore
balancing_image= ImageTk.PhotoImage(img2)
#two buttons to click
balanced_button = Button(unbalancing_frame, text = 'balanced', command = balancing_answer).pack()
unbalanced_button = Button(unbalancing_frame, text = 'unbalanced', command = unbalancing_answer).pack()
#the two labels balanced and unbalanced
balanced_label = Label(balancing_frame, text="it was balanced", font=("Helvetica",9), bg='#B3FDFF')
unbalanced_label = Label(unbalancing_frame, text='It was unbalanced', font=("Helvetica",9), bg='#B3FDFF')
#when the user click balancing.. a new frame appear
def balancing():
unbalancing_frame.pack()
#========the menu=========
my_menu = Menu(root)
root.config(menu=my_menu, bg='#B7F7BB')
#menu options(elements and compound)
lesson_menu = Menu(my_menu)
my_menu.add_cascade(label="Lesson", menu=lesson_menu)
lesson_menu.add_command(label="balancing", command=balancing)
lesson_menu.add_separator()
lesson_menu.add_command(label="Exit", command=root.quit)
root.mainloop()

Related

Displaying multiple images side by side with tkinter

I wrote a program for facial recognition and now I have to make a GUI for it. What I need to do is to display 5 images of recognized person. But I can only display one of them. The others are shown empty. Here is the code:
root = Tk()
root.title("Test state")
def train_button():
os.system('python3 extract_embeddings.py --dataset dataset --embeddings output/embeddings.pickle --detector face_detection_model --embedding-model openface_nn4.small2.v1.t7')
os.system('python3 train_model.py --embeddings output/embeddings.pickle --recognizer output/recognizer.pickle --le output/le.pickle')
messagebox.showinfo("INFO","Training completed")
def select_photo():
global my_image
root.filename = filedialog.askopenfilename(initialdir="test",title ="Select a photo",filetypes=(("all files","*.*"),("png files","*.png"),("jpeg files","*.jpeg")))
output = subprocess.check_output("python3 recognize.py --detector face_detection_model --embedding-model openface_nn4.small2.v1.t7 --recognizer output/recognizer.pickle --le output/le.pickle --image "+root.filename, shell=True)
output = output.decode('utf-8')
pic_name = output.split('\n')[0]
my_image = ImageTk.PhotoImage(Image.open("images/"+pic_name+"/"+pic_name+"1.jpeg"))
my_image_label = Label(image = my_image).grid(row = 1 ,column = 0)
name_label = Label(text = pic_name).grid(row=2,column=0)
my_image1 = ImageTk.PhotoImage(Image.open("images/"+pic_name+"/"+pic_name+"2.jpeg"))
my_image_label1 = Label(image = my_image1).grid(row =1 ,column=1)
my_image2 = ImageTk.PhotoImage(Image.open("images/"+pic_name+"/"+pic_name+"3.jpeg"))
my_image_label2 = Label(image = my_image2).grid(row = 1,column = 2)
button1 = Button(root,text = "Train" , command = train_button).grid(row=0 ,column=0)
button2 = Button(root,text = "Recognise from image", command = select_photo).grid(row = 0 ,column=1)
root.mainloop()
This is how the program shows images
Resulting program
Thank you for your time
It is bug in PhotoImage which removes image when it is assigned to local variable in function.
You have to assing it to global variable (it can be list if you have many images) or assign to widgets which you use to display it - label.photo = image
I can test it but here is version which uses label.photo = image to resolve this problem
It also uses for-loop to create labels and keep them on list.
But when you use label.photo = image then list is not necessary. List is only useful to access labels to remove old labels before you create new labels.
import tkinter as tk # PEP8: `import *` is not preferred
from tkinter import filedialog
# --- functions ---
def train_button():
os.system('python3 extract_embeddings.py --dataset dataset --embeddings output/embeddings.pickle --detector face_detection_model --embedding-model openface_nn4.small2.v1.t7')
os.system('python3 train_model.py --embeddings output/embeddings.pickle --recognizer output/recognizer.pickle --le output/le.pickle')
messagebox.showinfo("INFO","Training completed")
def select_photo():
#global all_labels
root.filename = filedialog.askopenfilename(initialdir="test", title ="Select a photo", filetypes=(("all files","*.*"),("png files","*.png"),("jpeg files","*.jpeg")))
output = subprocess.check_output("python3 recognize.py --detector face_detection_model --embedding-model openface_nn4.small2.v1.t7 --recognizer output/recognizer.pickle --le output/le.pickle --image "+root.filename, shell=True)
output = output.decode('utf-8')
pic_name = output.split('\n')[0]
# remove previous labels
for label in all_labels:
label.destroy()
for number in range(3):
#filename = f"images/{pic_name}/{pic_name}{number+1}.jpeg" # f-string (Python 3.6+)
filename = "images/{}/{}{}.jpeg".format(pic_name, pic_name, number+1) # (Python 2.7, 3.0+)
image = ImageTk.PhotoImage(Image.open(filename))
label = tk.Label(image=image)
label.photo = image # assign to class variable to resolve problem with bug in `PhotoImage`
label.grid(row=1, column=number)
all_labels.append(label)
# --- main ---
all_labels = []
root = tk.Tk()
button1 = tk.Button(root, text="Train", command=train_button)
button1.grid(row=0, column=0)
button2 = tk.Button(root, text="Recognise from image", command=select_photo)
button2.grid(row=0, column=1)
root.mainloop()

reading and playing a word in the message window

I have a problem with the code and an error message appears in word -> word = "" word = str (Entry.get (input_text)) which obviously refers to printing full text which does not work the way I want it when I want it to work in a non-root window window but reading window.
This is the codes :
from tkinter import *
import sys
import tkinter as tk
from tkinter import messagebox
#---------
root = Tk()
root.title("Window")
#----------
#toiminnot root ikkunassa
functions = Label(root, text = "Select an action!")
functions.place(x = 70, y= 10)
#lue toimonto koodi alkaa
def read():
reading_window = Tk()
reading_window.title("Read function")
frame = Frame(reading_window)
frame.pack()
read_the_text = Label(reading_window, text = "Enter text in the box!")
read_the_text.place(x = 70, y = 10)
word = ""
word = str(Entry.get(input_text))
#frame johon kirjoitetaan
input_text = Entry(reading_window)
input_text.place(x=55, y=30)
#lueikkuna koko ja sijoitus
reading_window.geometry("300x300+100+100")
#lue sana painike joka tuo viestin
print_button = tk.Button(reading_window, text = 'Show typed text', height = 1, width = 15) #, command = print1)
print_button.place(x=80, y=60)
text_a_tip = Label(reading_window, text ="The typed text is displayed in\nthe message window!")
text_a_tip.place(x = 50, y = 90)
def print1():
tk.messagebox.showinfo('Kirjoitetun tekstin tulostus viestiikkunaan', (Entry.get(input_text)))
def close():
reading_window.destroy()
read_close_button = tk.Button(reading_window, text = 'Close the reading function', height = 1, width = 20, command = close)
read_close_button.place(x = 60, y = 270)
read.mainloop()
#lue toiminto koodi loppuu
read_function = tk.Button(root, text='Read function', height = 1, width = 15, command = read)
read_function.place(x = 55,y = 35)
#ohjleman lopettamisen koodi alkaa
def quit_prog():
MsgBox = tk.messagebox.askquestion('Quit program', ' Are you sure you want to close the program?',icon = 'warning')
if MsgBox == 'yes':
root.destroy()
sys.exit(0)
else:
tk.messagebox.showinfo('Back','Now you are back!')
quit_programbutton = tk.Button(root, text='Close program', height = 1, width = 15, command = quit_prog)
quit_programbutton.place(x=50, y=220)
#ohjelman lopettamisen koodi loppuu tähän
#----------
#----------
root.geometry("250x250+20+60") #"450x450=leveysxkorkeus"+"20+40=vasenreuna+yläreuna"
root.mainloop()
source
I don't really see what is "word" variable for, but I assume that You need it. You can't use variable defined under line in which you use it.
word = ""
word = str(Entry.get(input_text)) # input_text is defined below, interpreter doesn't know what is it "input_text" yet.
input_text = Entry(reading_window)
input_text.place(x=55, y=30)
It should be rather.
input_text = Entry(reading_window)
input_text.place(x=55, y=30)
word = ""
word = str(Entry.get(input_text)) # now there is no error
It doesn't solve your problem, because I see that in function print1 you use again imput_text but it's defined in different function so it won't work.
def print1():
# input_text is local variable in read function.
tk.messagebox.showinfo('Kirjoitetun tekstin tulostus viestiikkunaan', (Entry.get(input_text)))
To solve your problem you can pass input_text as argument of print1, like in this working example.
def read():
reading_window = Tk()
reading_window.title("Read function")
frame = Frame(reading_window)
frame.pack()
read_the_text = Label(reading_window, text = "Enter text in the box!")
read_the_text.place(x = 70, y = 10)
input_text = Entry(reading_window)
input_text.place(x=55, y=30)
word = ""
word = str(Entry.get(input_text))
reading_window.geometry("300x300+100+100")
# pass input_text to print1
print_button = tk.Button(reading_window, text = 'Show typed text', height = 1, width = 15, command = lambda: print1(input_text))
print_button.place(x=80, y=60)
text_a_tip = Label(reading_window, text ="The typed text is displayed in\nthe message window!")
text_a_tip.place(x = 50, y = 90)
def print1(input_text):
# print1 takes now one argument and has reference to input_text
tk.messagebox.showinfo('Kirjoitetun tekstin tulostus viestiikkunaan', (Entry.get(input_text)))
def close():
reading_window.destroy()
read_close_button = tk.Button(reading_window, text = 'Close the reading function', height = 1, width = 20, command = close)
read_close_button.place(x = 60, y = 270)
read.mainloop()
Consider also using different geometry manager than place, it is even written in effbot doc:
It is usually not a good idea to use place for ordinary window and dialog layouts; its simply to much work to get things working as they should. Use the pack or grid managers for such purposes.
Place Geometry Manager

Closing TK window after button is pressed (regardless of button) while the button still performs its action

I'm trying to have my Tk window perform a function when a button is pressed, and they automatically close itself. I assume I need some sort of destroy() function inside of the action function, but I don't know how to word it.
Here is what I am trying to do
import pandas as pd
from tkinter import *
import numpy as np
from functools import partial
fake data
test = pd.DataFrame(columns = ["id", 'sent', "O1", "O2", "O3", "O4"])
results = []
for i in range(5):
test.loc[i,:] = [i,"this is test "+ str(i), .2, .5, .1, .1]
levels = [["Baby"], ["Dinos"], ["bad"], ["Spoons"]]
###
This is the action I want it to take. It needs to record what was pressed, then delete the window afterwards. I think this is where my destroy() function needs to go, but I'm not sure how to word it.
def Add_results(option):
results.append(option)
My window maker
def Window_maker(sent, choices):
root = Tk()
topFrame = Frame(root)
topFrame.pack()
botFrame = Frame(root)
botFrame.pack()
label = Label(topFrame, text =sent)
label.pack()
indi= 0
button1 = Button(botFrame, text = choices[0], command = lambda: Add_results(option = choices[0]))
button1.pack()
button2 = Button(botFrame, text = choices[1], command = lambda: Add_results(option = choices[1]))
button2.pack()
root.mainloop()
return(results)
The implementation
for i in range(test.shape[0]):
index = get_params(test.iloc[i, 2:])
choices = [levels[x] for x in index.values]
pred = Window_maker(test.iloc[i,1], choices)
I found a fix.
I change Add_results to:
def Add_results(option):
results.append(option)
root.quit()
And it worked!

change Tkinter frame color with for loop from list of colors

I have the following code snippet. What i need to code that when i click the button i need the frame color to change one by one from the list of colors defined.
from tkinter import *
from tkinter import ttk
def ChangeColor():
colors = ['red','green', 'orange','blue']
for color in colors:
#color = entry.get()
frame.config(bg = color)
root = Tk()
root.title("Title")
frame = Frame (root, width = 260, height = 200)
frame.pack()
btn = ttk.Button(frame, text = 'Change color', command = ChangeColor)
btn.place (x = 80, y = 100)
entry = ttk.Entry (frame, width = 20)
entry.place(x = 80, y = 70)
root.mainloop()
You can use the cycle iterator from itertools for this.
from tkinter import *
from tkinter import ttk
from itertools import cycle
root = Tk()
root.title("Title")
frame = Frame (root, width = 260, height = 200)
frame.pack()
colors = ['red','green', 'orange','blue']
color_gen = cycle(colors)
def ChangeColor():
frame.config(bg = next(color_gen))
btn = ttk.Button(frame, text = 'Change color', command = ChangeColor)
btn.place (x = 80, y = 100)
entry = ttk.Entry (frame, width = 20)
entry.place(x = 80, y = 70)
root.mainloop()
One thing I need to mention: please avoid doing "star" imports. When you do
from tkinter import *
it puts 135 Tkinter names into your namespace; in Python 2 you get 175 names. This creates needless clutter in the namespace and it can cause name collisions: if you accidentally name one of your variables with one of the imported names that can lead to mysterious bugs. It's even worse when you do star imports with multiple modules since they can stomp over each others' names. Also, star imports make the code harder to read since you have to remember which names are defined locally and which are imported.
I would change your app to a class so you can store variables and access them easily, also I bound the enter key to the entry widget so that works too. This way when you create an instance of class app it is an instance of a Tk() root, but you don't have to call it root
import tkinter as tk
from tkinter import ttk
class app(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("Title")
self.frame = tk.Frame(self, width = 260, height = 200)
self.frame.pack()
self.btn = ttk.Button(self.frame, text = 'Change color', command = self.ChangeColor)
self.btn.place (x = 80, y = 100)
self.entry = ttk.Entry (self.frame, width = 20)
self.entry.place(x = 80, y = 70)
self.entry.bind("<Return>",self.ChangeColorEntry)
self.colors = ['red','green','orange','blue']
self.current_color = -1
self.standard_bg = self.frame['background']
def ChangeColor(self,event=None):
if self.current_color == len(self.colors) - 1:
self.frame.config(bg = self.standard_bg)
self.current_color = -1
return
else:
self.current_color += 1
color = self.colors[self.current_color]
self.frame.config(bg = color)
def ChangeColorEntry(self,event=None):
entered = self.entry.get().lower().strip()
if entered == "none":
self.frame.config(bg = self.standard_bg)
else:
try:
self.current_color = self.colors.index(entered)
self.frame.config(bg = entered)
except:
pass
#PM 2Rings answer is cleaner but since I was working on this I thought I'd still post it incase you wanted to implement it manually
from tkinter import *
from tkinter import ttk
colors = ['red', 'green', 'orange', 'blue']
colors_it = iter(colors)
def get_next_color():
try:
global colors_it
return next(colors_it)
except StopIteration:
colors_it = iter(colors)
return next(colors_it)
def ChangeColor():
frame.config(bg=get_next_color())
root = Tk()
root.title("Title")
frame = Frame (root, width = 260, height = 200)
frame.pack()
btn = ttk.Button(frame, text = 'Change color', command = ChangeColor)
btn.place (x = 80, y = 100)
entry = ttk.Entry (frame, width = 20)
entry.place(x = 80, y = 70)
root.mainloop()

Button image not appearing in tkinter

I'm new to tkinter and am running python 3.4.2 with Tk version 8.5.17 using IDLE 3.4.2 under Yosemite. This is a modified demo from Liang's book Introduction to Programming Using Python 3. I was expecting the following code to display four buttons:
from tkinter import *
class ButtonsDemo:
def __init__(self):
window = Tk()
window.title("Buttons Demo")
frame0 = Frame(window)
frame0.pack()
plusImage = PhotoImage(file = "image/plus.gif")
minusImage = PhotoImage(file = "image/minus.gif")
timesImage = PhotoImage(file = "image/times.gif")
divideImage = PhotoImage(file = "image/divide.gif")
Button(frame0, image = plusImage, command =
self.add).grid(row = 1, column = 1, sticky = W)
Button(frame0, image = minusImage, command =
self.subtract).grid(row = 1, column =2)
Button(frame0, image = timesImage, command =
self.multiply).grid(row = 1, column = 3)
Button(frame0, image = divideImage, command =
self.divide).grid(row = 1, column = 4)
window.mainloop()
def add(self):
print("add pressed")
def subtract(self):
print("subtract pressed")
def multiply(self):
print("multiply pressed")
def divide(self):
print("divide pressed")
ButtonsDemo()
When I run the code, only the first three buttons are displayed. The button with divideImage on it does not appear.
If I click where the divide button should be, I do get the message "divide pressed". It's as if the button is there, but it's invisible.
I see the same behavior running the program from the command line
instead of from IDLE.
There's nothing wrong with the gif. If I use divide.gif for plusImage, I can see divide.gif in the first button position. Also, when I put text = "Divide" on the divide button instead of image = DivideImage, the button becomes visible.
Why is the divide button invisible when I use an image? How can I fix it?
EDIT: Here's the code with everything moved to the main program. I think this should have fixed a garbage collection problem if there were one. Is this correct?
from tkinter import *
def add():
print("add pressed")
def subtract():
print("subtract pressed")
def multiply():
print("multiply pressed")
def divide():
print("divide pressed")
window = Tk()
window.title("Buttons Demo")
frame0 = Frame(window)
frame0.pack()
plusImage = PhotoImage(file = "image/plus.gif")
minusImage = PhotoImage(file = "image/minus.gif")
timesImage = PhotoImage(file = "image/times.gif")
divideImage = PhotoImage(file = "image/divide.gif")
Button(frame0, image = plusImage, command =
add).grid(row = 0, column = 0, sticky = W)
Button(frame0, image = minusImage, command =
subtract).grid(row = 0, column = 1)
Button(frame0, image = timesImage, command =
multiply).grid(row = 0, column = 2)
Button(frame0, image = divideImage, command =
divide).grid(row = 0, column = 3)
window.mainloop()

Categories

Resources