Looping in Tkinter isnt working as I intended - python

I am trying to update some buttons at the for loop in the end, which buttons I don't want to create individually.
I tried using:
While True:
root.update()
and the other type of update and also each one individually but no luck.
This is the code:
from tkinter import *
from tkinter.ttk import *
from PIL import Image
from functools import partial
import random
#define variables
#main_window_width = 500
#main_window_height = 500
#def functions
buttons_list = []
def InitButtons(n, k):
if k == 0:
k = n
for i in range(n):
InitButtons_column = i
if i//k > 0:
InitButtons_column = i%k
buttons_list.append(Label(main_window, image=button_img))
buttons_list[i].grid(row=(i//k), column=InitButtons_column)
def Hovering(e):
button_img = PhotoImage(file = '/home/klet/Desktop/projects/Python/GUI/button2.png')
buttons_list_button.config(image = button_img)
buttons_list_button.image = button_img
def Clicking(e):
button_img = PhotoImage(file = '/home/klet/Desktop/projects/Python/GUI/button3.png')
buttons_list_button.config(image = button_img)
buttons_list_button.image = button_img
def NotHovering(e):
button_img = PhotoImage(file = '/home/klet/Desktop/projects/Python/GUI/button1.png')
buttons_list_button.config(image = button_img)
buttons_list_button.image = button_img
def UpdatingButtons(n):
n.bind("<Enter>", Hovering)
n.bind("<Leave>", NotHovering)
n.bind("<Button-1>", Clicking)
n.bind("<ButtonRelease-1>", Hovering)
root = Tk()
button_img = PhotoImage(file = "/home/klet/Desktop/projects/Python/GUI/button1.png")
main_window = Frame(root)
main_window.place(relx=0.1, rely=0.1, relwidth=0.8, relheight=0.8)
InitButtons(10, 5)
#buttons_list_tmp1 = 0
#buttons_list_tmp1 += 1
#buttons_list_button =
#UpdatingButtons(buttons_list_tmp1)
#print(buttons_list_tmp1)
for i in range(len(buttons_list)):
buttons_list_button = buttons_list[i]
UpdatingButtons(buttons_list_button)
print(i)
root.mainloop()
The for loop above is only repeating once and i want it to be constantly updating, I also tried putting the whole code (within root and root.mainloop()) in a while loop but didnt work. I also searched some questions asked but they didnt seem to help.
I want to update the buttons(labels in this case) when I enter the button and when i click. Heres an example of what currently happening:
Link to vid
I made this account just to ask this question because I really dont like asking questions.

Related

Python tkinter image can't change immediately?

My code:
from tkinter import *
import random
from PIL import ImageTk, Image
import time
class App():
def __init__(self, root, dice_image):
self.dice_label = Label(root, image=dice_image)
self.dice_label.pack()
self.button = Button(root, text="Roll", font=("Fixedsys", 25) , width=20, height=20, bg="red" , command=rolling)
self.button.pack(pady=70)
I want to make the image change fast to make it look like rolling. But I don't know why it change nothing before the for loop end.
def rolling():
global dices
for i in range(1, 10 + 1):
time.sleep(0.3)
random.shuffle(dices)
app.dice_label.configure(image=dices[0])
if __name__ == '__main__':
root = Tk()
root.geometry("800x600")
# Var
dice_image = ImageTk.PhotoImage(Image.open("Dices.jpg"))
dice1 = ImageTk.PhotoImage(Image.open("Dice1.jpg"))
dice2 = ImageTk.PhotoImage(Image.open("Dice2.jpg"))
dice3 = ImageTk.PhotoImage(Image.open("Dice3.jpg"))
dice4 = ImageTk.PhotoImage(Image.open("Dice4.jpg"))
dice5 = ImageTk.PhotoImage(Image.open("Dice5.jpg"))
dice6 = ImageTk.PhotoImage(Image.open("Dice6.jpg"))
dices = [dice1, dice2, dice3, dice4, dice5, dice6]
app = App(root, dice_image)
root.mainloop()
Tkinter and sleep() are not friends. The problem is that Tkinter functions inside of a mainloop and sleep pauses that loop until all the sleep time is over.
This will always freeze your application.
Try using after() and a refactored function instead.
I have not tested this but I believe something like this should fix your problem or at least get rid of the problem that sleep() will cause.
I have changed your button command to send a call to the function using lambda so that the call wont go out at runtime but only when pressing the button.
from tkinter import *
import random
from PIL import ImageTk, Image
class App():
def __init__(self, root, dice_image):
self.dice_label = Label(root, image=dice_image)
self.dice_label.pack()
self.button = Button(root, text="Roll", font=("Fixedsys", 25),
width=20, height=20, bg="red" ,
command=lambda: rolling(1,11))
self.button.pack(pady=70)
I have updated your rolling function to handly a timed loop.
root.after(300, lambda: rolling(stop, counter)) will call the function it is in only if the counter has not finished counting down and only once every 0.3 seconds. Again lambda is used here to make sure the call to rolling does not happen at runtime.
def rolling(s, e, start=False):
global dices
counter = 0
stop = 0
if start:
counter = s
stop = e
if counter > stop:
random.shuffle(dices)
app.dice_label.configure(image=dices[0])
counter -= 1
root.after(300, lambda: rolling(stop, counter))
I have also updated this portion to use a loop so you don't have to repeat yourself when crating a list of images.
if __name__ == '__main__':
root = Tk()
root.geometry("800x600")
dice_image = ImageTk.PhotoImage(Image.open("Dices.jpg"))
dices = []
for i in range(6):
dices.append(ImageTk.PhotoImage(Image.open(f"Dice{i+1}.jpg")))
app = App(root, dice_image)
root.mainloop()

how to hide labels in python until a button is pressed

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

tkinter - I'm trying to disable the forward / backward button but it doesn't seem to work

I'm trying to build an image viewer that loads the images from a folder. It should have forward / backward and quit buttons. It seems to work just fine with one issue:
So I just take the image paths with this:
def get_files_from_folder(path, allowed_extensions):
found_paths = []
for (dir_path, _, file_names) in os.walk(path):
for file_name in file_names:
for allowed_extension in allowed_extensions:
if file_name.lower().endswith(allowed_extension.lower()):
found_paths.append(os.path.join(dir_path, file_name))
break
return found_paths
I have the tkinter UI for the image viewer:
class UI:
def __init__(self, icon_path, images_folder_path):
self.current_index = 0
self.root = tk.Tk()
self.root.title('Images')
self.root.iconbitmap(icon_path)
self.button_quit = tk.Button(self.root, text = 'Quit', padx = 60, command = self.root.quit)
self.button_forward = tk.Button(self.root, text = '>>', command = self.forward)
self.button_backward = tk.Button(self.root, text = '<<', command = self.backward)
self.button_quit.grid(row = 1, column = 1)
self.button_forward.grid(row = 1, column = 2)
self.button_backward.grid(row = 1, column = 0)
self.images_paths = get_files_from_folder(images_folder_path, ['.jpg', '.png'])
self.tk_images = []
print(get_files_from_folder)
for image_path in self.images_paths:
self.tk_images.append(ImageTk.PhotoImage(Image.open(image_path)))
self.current_image = tk.Label(image = self.tk_images[0])
self.current_image.grid(column = 0, row = 0, columnspan = 3)
self.root.mainloop()
And for some reason, here when I'm using the tk.DISABLED, it just won't disable it
def backward(self):
if self.current_index == 0:
self.button_backward = self.button_backward = tk.Button(self.root, text = '<<', command = self.backward, state = tk.DISABLED)
self.current_image.grid_forget()
self.current_index -= 1
self.current_image = tk.Label(image = self.tk_images[self.current_index])
self.current_image.grid(column = 0, row = 0, columnspan = 3)
same for the forward:
def forward(self):
self.current_image.grid_forget()
if self.current_index == len(self.tk_images)-1:
self.button_forward = self.button_forward = tk.Button(self.root, text = '>>', command = self.forward, state = tk.DISABLED)
else:
self.button_forward.state = tk.ACTIVE
self.current_index += 1
self.current_image = tk.Label(image = self.tk_images[self.current_index])
self.current_image.grid(column = 0, row = 0, columnspan = 3)
There's at least a couple of things wrong with your current code regarding the forward and backward Buttons commands. As for disabling the Buttons, that can be done by calling their config() method — not by creating a new one or assigning a new value to the existing ones state attribute (i.e. self.button_forward.state = tk.ACTIVE).
Similarly it's not a good practice to continually create new tk.Label widgets every time one of the buttons is pressed and the image on it changes. It's better to change the image= option of the existing Label. Doing this also often simplifies what needs to be done.
In addition your logic for clamping the self.current_index was flawed and would allow it to get out of range and IndexErrors to occur. This is more complicated than I anticipated, but I think I figured out a good way to handle it, namely by putting all the logic a private method _change_current_index() and calling it from the forward and backward callback functions.
Lastly, user #acw1668 commented that it loading all images into memory at program start might be two slow. To avoid that, I replaced the list of all the loaded images you had (self.tk_images) with calls to a another new private method named _open_image() which caches its results by having the functools.lru_cache decorator applied to it. This makes the method remember what values it has already returned for a limited number of indices thereby restricting the number of them in memory at any one time.
Note I also reformatted the code to better conform to the PEP 8 - Style Guide for Python Code guidelines to make it more readable.
from functools import lru_cache
import tkinter as tk
import os
from PIL import Image, ImageTk
class UI:
IMG_CACHE_SIZE = 10
def __init__(self, icon_path, images_folder_path):
self.current_index = 0
self.root = tk.Tk()
self.root.title('Images')
# self.root.iconbitmap(icon_path)
self.button_quit = tk.Button(self.root, text='Quit', padx=60, command=self.root.quit)
self.button_forward = tk.Button(self.root, text='>>', command=self.forward)
self.button_backward = tk.Button(self.root, text='<<', command=self.backward)
self.button_quit.grid(row=1, column=1)
self.button_forward.grid(row=1, column=2)
self.button_backward.grid(row=1, column=0)
self.image_paths = get_files_from_folder(images_folder_path, ['.jpg', '.png'])
self.current_image = tk.Label(image=self._open_image(0))
self.current_image.grid(column=0, row=0, columnspan=3)
self._change_current_index() # Initializes fwd and bkd button states.
self.root.mainloop()
#lru_cache(maxsize=IMG_CACHE_SIZE)
def _open_image(self, i):
image_path = self.image_paths[i]
return ImageTk.PhotoImage(Image.open(image_path))
def backward(self):
self._change_current_index(-1)
def forward(self):
self._change_current_index(1)
def _change_current_index(self, delta=0):
self.current_index += delta
# Update state of forward and backward buttons based on new index value.
bkd_state = (tk.DISABLED if self.current_index == 0 else tk.ACTIVE)
self.button_backward.config(state=bkd_state)
fwd_state = (tk.DISABLED if self.current_index == len(self.image_paths)-1 else tk.ACTIVE)
self.button_forward.config(state=fwd_state)
# Update image on Label.
self.current_image.config(image=self._open_image(self.current_index))
def get_files_from_folder(path, allowed_extensions):
found_paths = []
for (dir_path, _, file_names) in os.walk(path):
for file_name in file_names:
for allowed_extension in allowed_extensions:
if file_name.lower().endswith(allowed_extension.lower()):
found_paths.append(os.path.join(dir_path, file_name))
break
return found_paths
if __name__ == '__main__':
UI('.', './images_test')

Tkinter drawing on canvas is delayed

I am new to tkinter and trying to make a basic drawing app. However, when I move my cursor around, it will sometimes suddenly stop drawing, and then only show the finished line several seconds later. This is shown here.
https://www.youtube.com/edit?video_referrer=watch&video_id=_g8n55V6qPQ
Is this lag? My laptop runs fine otherwise, and it can even 'lag' on the first go (i.e. before there are any other objects on the canvas). If it is just lag, what sort of workarounds do I have in making my python drawing app?
This is my code:
from tkinter import *
root = Tk()
root.title("Note Taking")
can_width = 800
can_height = 800
canvas = Canvas(root, width=can_width, height=can_height, bg='white')
canvas.pack(padx=20, pady=20)
class g():
points = []
user_line = None
drawing = False
t = 0
def leftClick(event):
g.points = []
g.user_line = None
g.drawing = True
g.points.append(event.x)
g.points.append(event.y)
def leftMove(event):
# Print out an increasing number: t, so I can see it in the output
print(g.t)
g.t+=1
if g.drawing:
g.points.append(event.x)
g.points.append(event.y)
if g.user_line == None:
g.user_line = canvas.create_line(g.points, width=4, smooth=1)
else:
canvas.coords(g.user_line, g.points)
def leftRelease(event):
g.points = []
g.user_line = None
canvas.bind('<Button-1>', leftClick)
canvas.bind('<B1-Motion>', leftMove)
canvas.bind('<ButtonRelease-1>', leftRelease)
root.mainloop()

Image viewer/ images for buttons

I'm trying to implement simple image viewer, where you can choose from 2 pictures. I have one menubutton which offers these pictures. After choosing one of the images, the program creates 3 or 5 buttons. I would like to append to each of these buttons different images, so the first button would have one image and the second button would have another image and so on. I've created a function with for loop to create these buttons, but can't move on from that point. I can append one image to all of them, but can't do that one by one with different images.
Thanks for help
try:
import Tkinter as tk
except ImportError:
import tkinter as tk
from functools import partial
from PIL import Image, ImageTk
class Halabala():
def __init__(self):
self.master = tk.Tk()
self.master.geometry("1100x700")
self.lists_labels = []
self.rbutton = tk.Menubutton(self.master, text = "Choose picture")
self.picks2 = tk.Menu(self.rbutton)
self.rbutton.config(menu=self.picks2)
self.picks2.add_command(label = "Spider", command = partial(self.create_labels,3))
self.picks2.add_command(label = "Sugar", command = partial(self.create_labels,5))
self.rbutton.config(width = 30, bg = "white", bd = 5, relief = tk.RAISED)
self.rbutton.place(x = 900, y = 30)
self.master.mainloop()
def create_labels(self, num):
self.picture = Image.open("blue.gif")
self.picture.thumbnail((130,130))
self.tkim = ImageTk.PhotoImage(self.picture)
for label in self.lists_labels:
label.destroy()
self.lists_labels=[]
for i in range(num):
but = tk.Button(self.master, image = self.tkim)
but.grid(row = i + 1, column = 0)
self.lists_labels.append(but)
myapp = Halabala()
This is the code relevant for your question:
class Halabala():
def __init__(self):
.............
self.pictures = ["pavuk1", "pavuk2", "pavuk3"]
self.lists_labels = []
self.lists_images = []
self.init_image_list()
............
def init_image_list(self):
for i in self.pictures:
picture = Image.open(i)
picture.thumbnail((130, 130))
self.lists_images.append(ImageTk.PhotoImage(picture))
def create_labels(self, num):
for label in self.lists_labels:
label.destroy()
self.lists_labels=[]
for i in range(num):
but = tk.Button(self.master, image = self.lists_images[i])
but.grid(row = i + 1, column = 0)
self.lists_labels.append(but)

Categories

Resources