i'm trying to make a photo slideshow program. it isn't working as i want to go to the next photo in the list using the self.counter variable, but the value of self.counter is going back to 0 as it forgets the fact that i changed the value. i didn't think i could use configure as then the buttons to go to the next image wouldn't work either.
i hope you can help :) much appreciated.
from tkinter import *
class SlideShowGUI:
def __init__(self, parent, counter = 0):
self.counter = counter
self.images_list = ["smiley.png", "carrot.png", "bunny.jpg"]
self.photo = PhotoImage(file = self.images_list[self.counter])
self.b1 = Button(parent, text = "", image = self.photo, bg = "white")
self.b1.grid(row = 0, column = 0)
back = Button(parent, width = 2, anchor = W, text = "<", relief = RIDGE, command = self.previous_image)
back.grid(row = 1, column = 0)
forward = Button(parent, width = 2, anchor = E, text = ">", relief = RIDGE, command = self.next_image)
forward.grid(row = 1, column = 1)
def previous_image(self):
self.counter -= 1
self.photo.configure(file = self.images_list[self.counter])
def next_image(self):
self.counter += 1
self.photo.configure(file = self.images_list[self.counter])
#main routine
if __name__ == "__main__":
root = Tk()
root.title("hello")
slides = SlideShowGUI(root)
root.mainloop()
sorry it will not work without getting images !
error message if i click next button two times
use this instead:
def previous_image(self):
self.counter -= 1
self.photo.configure(file = images_list[self.counter])
def next_image(self):
self.counter += 1
self.photo.configure(file = images_list[self.counter])
except You have to watch out for List Out Of Index errors
Also why are You using global images_list? There seems to be no point. If You wanted to reuse that list in the class You could have just named it self.images_list = [your, items].
The error You are getting: read this
Related
I'm trying to make a memory game for fun and as a learning experience, and I've run into the issue where even with something like time.sleep(.5) I still can't get buttons to update correctly with a delay. In fact the second button seems to update to hidden as it's about to show the proper image. I'm assuming the issue lies somewhere in the buttonClicked() function.
I'm trying to figure out how I can make it show one button, then the second, then wait half a second and hide both. And if someone understands why this is happening or where I could look into the issue and read up on my own, that would be helpful.
Thanks.
from re import A
import time
import tkinter as tk
from tkinter import *
from typing_extensions import Self
from PIL import Image, ImageTk
import glob
import os, os.path
import numpy as np
from sqlalchemy import null
#resize images and button
imgButtonWidth = 100
imgButtonHeight = 100
imgButtonSize = (imgButtonWidth,imgButtonHeight)
#Set the height and width of the game by number of items.
width = 6
height = 6
#buttons = [[Button]*width]*height
#Total number of items 36 (0-35)
count = width*height-1
buttonList = []
#Will be a 2d array of [button, id]
answersList = []
clickedCount = 0
imgs = []
hiddenImg = null
# Create frame, set default size of frame and background color.
root = Tk()
root.title('Memory Game')
root.geometry(str(imgButtonWidth * (width+1)) + "x" + str(imgButtonHeight * (height+1)))
root.config(bg='darkblue')
frame = Frame(root, bg='darkblue')
# Fetch images from location and create a list of Image objects, then return.
def getImages():
imgs = []
path = "/home/paul/Programming/Python/MyMiniProjects/Mid/MemoryGame/"
valid_images = [".jpg",".gif",".png",".tga"]
for f in os.listdir(path):
ext = os.path.splitext(f)[1]
if ext.lower() not in valid_images:
continue
imgs.append([Image.open(os.path.join(path,f)).resize(imgButtonSize), f])
return imgs + imgs
#Shuffle images for the game
imgs = getImages()
random.shuffle(imgs)
#Simple image to cover the tiles
hiddenImg = ImageTk.PhotoImage(Image.new('RGB', (imgButtonWidth, imgButtonHeight), (0,0,105)))
#Disable buttons after a match
def disable():
global clickedCount, answersList
clickedCount = 0
for a in answersList:
a[0]["state"] = "disabled"
a[0]["bg"] = "green"
answersList = []
#Hide buttons again
def hide():
global clickedCount, answersList
clickedCount = 0
for a in answersList:
#a[0].config(image = hiddenImg)
a[0]["image"] = hiddenImg
a[0]["state"] = "normal"
a[0]["bg"] = "white"
answersList = []
def wrong():
for a in answersList:
a[0]["bg"] = "red"
def buttonClicked(picture, id, button):
global clickedCount, answersList
print(clickedCount, len(answersList))
#print(button.image, "1", hiddenImg, picture)
if button.image is hiddenImg and clickedCount < 2:
button["image"] = picture
button["state"] = "disabled"
clickedCount += 1
answersList.append([button, id])
if len(answersList) == 2:
#Check id but make sure it's not the same button pressed twice
if answersList[0][1] is answersList[1][1]:#and answersList[0][0] is not answersList[1][0]:
disable()
else:
wrong()
hide()
#Create the actual buttons with their respective image
for h in range(height): #print(buttons[w][::],"\n")
newList = []
for w in range(width):
tempImage = imgs.pop(count)
picture = ImageTk.PhotoImage(tempImage[0])
id = tempImage[1]
button = Button(frame, image=hiddenImg, state=NORMAL, height=imgButtonHeight, width=imgButtonWidth)
#Need to split this up because of how python handles closures
button["command"] = lambda pic_temp=picture, id_temp=id, button_temp = button: buttonClicked(pic_temp, id_temp, button_temp)
button.image = hiddenImg
#buttons[w][h].name = str(w + h)
#buttons[w][h].grid(row=w, column=h, ipadx=random.randint(0,40), ipady=random.randint(0,40), padx=random.randint(0,5), pady=random.randint(0,5))
button.grid(row=h, column=w, padx=1, pady=1)
#Button(frame, image=picture).grid(row=w, column=h, ipadx=random.randint(0,40), ipady=random.randint(0,40), padx=random.randint(0,5), pady=random.randint(0,5))
count -= 1
# buttonList.append(buttons[h][w])
newList.append(button)
buttonList.append(newList)
# for y in range(height):
# for x in range(width):
# print(ButtonList[y][x])
# print("")
frame.pack(expand=True)
root.mainloop()```
I have a GUI where the user can click a button named "next set" that allows them to move onto the next task. I wanted to add a timer that starts as soon as they start the application and run the timer until they press the button "next set". When clicked, I want the time elapsed to print and the timer to restart until they press "next set" button again. I would like the timer to start automatically when the code runs. Currently, the "next set" button has two actions, one is to retrieve the next set of images and the other action I am trying to include is to reset the timer and print time elapsed. I also only included part of the code that felt relevant because it is long.
import time
import tkinter as tk
import csv
from pathlib import Path
import PIL.Image
import PIL.ImageDraw
import PIL.ImageTk
MAX_HEIGHT = 500
IMAGES_PATH = Path("Images")
CSV_LABELS_KEY = "New Labels"
CSV_FILE_NAME_KEY = "FolderNum_SeriesNum"
CSV_BOUNDING_BOX_KEY = "correct_flip_bbox"
counter = 0
timer_id = None
class App(tk.Frame):
def __init__(self, master=None):
super().__init__(master) # python3 style
self.config_paths = ["config 1.yaml", "config 2.yaml", "config 3.yaml"]
self.config_index = 0
self.clickStatus = tk.StringVar()
self.loadedImages = dict()
self.loadedBoxes = dict() # this dictionary will keep track of all the boxes drawn on the images
self.master.title('Slideshow')
frame = tk.Frame(self)
tk.Button(frame, text=" Next set ", command=lambda:[self.get_next_image_set(), self.reset()]).pack(side=tk.RIGHT)
tk.Button(frame, text=" Exit ", command=self.destroy).pack(side=tk.RIGHT)
frame.pack(side=tk.TOP, fill=tk.BOTH)
self.canvas = tk.Canvas(self)
self.canvas.pack()
self._load_dataset()
self.reset()
self.start_timer = None
t = time()
t.start()
def start_timer(self, evt=None):
if self._start_timer is not None:
self._start_timer = time.perf_counter()
# global counter
# counter += 1
# label.config(text=str(counter))
# label.after(1000, count)
def reset(self):
if self._start_timer is None:
elapsed_time = time.perf_counter() - self._start_timer
self._start_timer = None
print('Time elapsed (hh:mm:ss.ms) {}'.format(elapsed_time))
def _load_dataset(self):
try:
config_path = self.config_paths[self.config_index]
self.config_index += 1
except IndexError:
return
image_data = loadData(config_path)
# drawing the image on the label
self.image_data = image_data
self.currentIndex = 0
# start from 0th image
self._load_image()
def _load_image(self):
imgName = self.image_data[self.currentIndex]['image_file']
if imgName not in self.loadedImages:
self.im = PIL.Image.open(self.image_data[self.currentIndex]['image_file'])
ratio = MAX_HEIGHT / self.im.height
# ratio divided by existing height -> to get constant amount
height, width = int(self.im.height * ratio), int(self.im.width * ratio)
# calculate the new h and w and then resize next
self.canvas.config(width=width, height=height)
self.im = self.im.resize((width, height))
if self.im.mode == "1":
self.img = PIL.ImageTk.BitmapImage(self.im, foreground="white")
else:
self.img = PIL.ImageTk.PhotoImage(self.im)
imgData = self.loadedImages.setdefault(self.image_data[self.currentIndex]['image_file'], dict())
imgData['image'] = self.img
imgData['shapes'] = self.image_data[self.currentIndex]['shapes']
# for next and previous so it loads the same image adn don't do calculations again
self.img = self.loadedImages[self.image_data[self.currentIndex]['image_file']]['image']
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.img)
self.show_drag_box()
def loadData(fname):
with open(fname, mode='r') as f:
return yaml.load(f.read(), Loader=yaml.SafeLoader)
if __name__ == "__main__":
data = loadData('config 1.yaml')
app = App(data)
app.pack() # goes here
app.mainloop()
I have used datetime instead of time, as subtracting two datetime objects will give an output with hours and minutes included, whereas subtracting two time objects only gives seconds. However, both will work, you may just need to do more reformatting using time.
Read the current time when the application starts and store it. Each time you press the button, subtract your stored time from the current time which gives you your time elapsed. Then simply store your new current time until the next button press. The code below demonstrates this.
import tkinter as tk
import datetime as dt
class TimeButton(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
# Start timer
self.current_time = dt.datetime.today()
# Button
self.next_set = tk.Button(self, text='Next Set', command = self.clicked)
self.next_set.pack()
def clicked(self):
now = dt.datetime.today()
time_elapsed = now - self.current_time
print(time_elapsed)
self.current_time = now
if __name__ == "__main__":
window = tk.Tk()
button = TimeButton(window)
button.pack()
window.mainloop()
I've only recently dipped my toes into python (only ever coded in java and JS) and thought I would try my hand at making a simple number pad GUI. I'm still getting used to the indentation format.
I've applied proper indenting and fixed all typos and syntax errors but still receive the following error
"Traceback (most recent call last):
File "/home/pi/Desktop/Lab Work/Lab 1/Number Pad test GUI.py", line 4, in
class BaseWindow(tkinter.Tk):
File "/home/pi/Desktop/Lab Work/Lab 1/Number Pad test GUI.py", line 8, in BaseWindow
self.minsize (x,y)
NameError: name 'self' is not defined"
I have updated the code below to my latest version also
import tkinter, tkinter.ttk as ttk
import random
class BaseWindow(tkinter.Tk):
def _Change (self):
x,y = self.winfo_width(), self.winfo_height()
self.minsize (x, y); self.maxsize(x, y)
#This locks window size when called
def FgridFormatButtons (self, ButtonList, NewLineAmount = 3):
self.Row = 0
self.Col = 0
for Button in ButtonList:
Button.grid(row = self.Row, column = self.Col)
self.Col += 1
if self.Col == NewLineAmount:
self.Row += 1
self.Col = 0
continue
class Window (BaseWindow):
def __init__(self, **args):
super(Window, self).__init__()
#Main method code
self.EntryFrame = ttk.Frame(self)
self.PadFrame = ttk.Frame(self)
self.EntryFrame.pack(padx = 5, pady = 5)
self.PadFrame.pack(padx = 5, pady = 5)
self.AllButtons = []
self.CanWrite = true
self.Cod = args.get("Code") or random.randrange(9999)
self.Timer = args.get("Timer") or 2000
print ("debug %d"% self.Code)
for x in range (1,10):
self.AllButtons.append(ttk.Button(self.PadFrame, width = 4, text = x, command = lambda y = x: self.Update(x)))
self.bind(str(x), lambda CatchEvent, y = x: self.Update(y))
self.FGridFormatButtons(self.AllButtons)
self.ZeroButton = ttk.Button (self.PadFrame, width = 4, text = 0, command = lambda: self.Update(0))
self.SubmitButton = ttk.Button(self.PadFrame, width = 4, text = "Ent", command = self.CheckCode)
self.ClearButton = ttk.Button(self.PadFrame, width = 4, text = "C", command = lambda: self.Update(-1))
self.ClearButton.grid(row = self.Row, column = 0)
self.ZeroButton.grid(row = self.Row, column = 1)
self.SubmitButton.grid(row = self.Row, column = 2)
self.bind ("0", lambda CatchEvent: self.Update(0))
self.bind("<return>", lambda CatchEvent: self.CheckCode())
self.KeyEnter = ttk.Entry(self,EntryFrame, state ="disabled")
self.KeyEnter.pack
#--
self.after (5, self._Change)
#This will wait 5 miliseconds and then lock the window
#If your computer takes longer than 5 miliseconds to load then kill yourself. Or alternatively, change the last value to match how long it takes
def Update (self, x):
if self.CanWrite:
self.KeyEnter["state"] = "normal"
if x == 1:
self.KeyEnter.delete(0, tkinter.END)
else:
self.KeyEnter.insert(tkinter.END, x)
self.KeyEnter["state"] = 'disabled'
def CheckCode(self):
Key = self.KeyEnter.get()
self.Update(-1)
if Key == str(self.Code):
self.Update("Correct Code!")
self.after(self.Timer, self.destroy)
else:
self.Update("Incorrect code")
self.ChangeWritePerms()
self.after(self.Timer, self.ChangeWritePerms)
def ChangeWritePerms(self):
if self.CanWrite:
self.CanWrite = False
else:
self.CanWrite = True
self.Update(-1)
window().mainloop()
class BaseWindow(tkinter.Tk):
def _change (self):
x,y = self.winfo_width(), self.winfo_height()
self.minimize (x,y)
#This locks window size when called
def FgridFormatButtons (self, ButtonList, NewLineAmount = 3):
self.Row = 0
self.Col = 0
for Button in ButtonList:
Button.grid(row = self.Row, column = self.Col)
self.Col += 1
if self.Col == NewLineAmount:
self.Row += 1
self.Col = 0
continue
class Window (BaseWindow):
def __init__(self, **args):
super(Window, self).__init__()
#Main method code
self.EntryFrame = ttk.Frame(self)
self.PadFrame = ttk.Frame(self)
self.EntryFrame.pack(padx = 5, pady = 5)
self.PadFrame.pack(padx = 5, pady = 5)
self.AllButtons = []
self.CanWrite = true
self.Cod = args.get("Code") or random.randrange(9999)
self.Timer = args.get("Timer") or 2000
print ("debug %d"% self.Code)
for x in range (1,10):
self.AllButtons.append(ttk.Button(self.PadFrame, width = 4, text = x, command = lambda y = x: self.Update(x)))
self.bind(str(x), lambda CatchEvent, y = x: self.Update(y))
self.FGridFormatButtons(self.AllButtons)
self.ZeroButton = ttk.Button (self.PadFrame, width = 4, text = 0, command = lambda: self.Update(0))
self.SubmitButton = ttk.Button(self.PadFrame, width = 4, text = "Ent", command = self.CheckCode)
self.ClearButton = ttk.Button(self.PadFrame, width = 4, text = "C", command = lambda: self.Update(-1))
self.ClearButton.grid(row = self.Row, column = 0)
self.ZeroButton.grid(row = self.Row, column = 1)
self.SubmitButton.grid(row = self.Row, column = 2)
self.bind ("0", lambda CatchEvent: self.Update(0))
self.bind("<return>", lambda CatchEvent: self.CheckCode())
self.KeyEnter = ttk.Entry(self,EntryFrame, state ="disabled")
self.KeyEnter.pack
#--
self.after (5, self._Change)
#This will wait 5 miliseconds and then lock the window
#If your computer takes longer than 5 miliseconds to load then kill yourself. Or alternatively, change the last value to match how long it takes
def Update (self, x):
if self.CanWrite:
self.KeyEnter["state"] = "normal"
if x == 1:
self.KeyEnter.delete(0, tkinter.END)
else:
self.KeyEnter.insert(tkinter.END, x)
self.KeyEnter["state"] = 'disabled'
def CheckCode(self):
Key = self.KeyEnter.get()
self.Update(-1)
if Key == str(self.Code):
self.Update("Correct Code!")
self.after(self.Timer, self.destroy)
else:
self.Update("Incorrect code")
self.ChangeWritePerms()
self.after(self.Timer, self.ChangeWritePerms)
def ChangeWritePerms(self):
if self.CanWrite:
self.CanWrite = False
else:
self.CanWrite = True
self.Update(-1)
window().mainloop()
It seems like you change your question.
Try to change tkinter.tk into tkinter.Tk with big T.
This error shows that you forgot to put comma before lambda function.
"line 60 self.bind("" lambda CatchEvent: self.CheckCode()) ^SyntaxError: invalid syntax"
Have you tried to add comma ?
Check line 5 and 110 you forget space after def and colon at the end function argument.
Check line 86 too, you defined key as Key in line 82, so you have to call it Key with big K in line 86.
Check line 88, it shows self.;after. Please delete the semicolon to avoid syntax error too.
What is the text editor you are using ? If you use vscode, it will show the error with red underline.
include "self.minsize (x, y); self.maxsize(x, y)" inside the function
any statement inside the class must be inside a function
Ex: import tkinter, tkinter.ttk as ttk
import random
class BaseWindow(tkinter.Tk):
def _Change (self):
x,y = self.winfo_width(), self.winfo_height()
self.minsize (x, y); self.maxsize(x, y)
I wrote a Tkinter GUI that is supposed to show display 3D images by looping through the 2D slices of each 3D image. I read up on how to implement such an image loop, and it was recommended to use the after() function with recursion. This is, in principle, my implementation of that:
def load_image(self):
self.stack = read_3D_image(path)
slice = self.stack[self.slice_no]
im = Image.fromarray(slice)
self.photo = ImageTk.PhotoImage(image=im)
self.canvas.create_image(0, 0, image=self.photo, anchor=tk.NW)
if self.forward is True:
self.slice_no += 1
if self.slice_no == 21:
self.forward = False
if self.forward is False:
self.slice_no -= 1
if self.slice_no == 0:
self.forward = True
root.after(10, self.load_image)
This works well for some time, but after a couple of minutes, the loop notably slows down. I guess that is because of the high number of iterations. Is there a way to fix this?
Edit: I noticed this: when the program runs, the image loop will slow down to about half the original frequency after about 10 minutes. When I run a second instance, its loop runs equally slow. Then when I close the first instance, the second instance loop immediately runs faster. I launch from Eclipse.
Updated full code
import glob
import os.path
import tkinter as tk
from PIL import Image, ImageTk
import numpy as np
import helpers
class App():
def __init__(self, master):
super().__init__()
self.frame = tk.Frame(master)
master.bind("<KP_1>", lambda e: self.is_cell())
master.bind("<KP_2>", lambda e: self.is_not_cell())
self.frame.pack()
self.goto_button = tk.Button(
self.frame, text="Go to:", command=self.goto)
self.goto_button.pack(side=tk.TOP)
self.goto_entry = tk.Entry(self.frame, width=5)
self.goto_entry.pack(side=tk.TOP)
self.img_side_length = 100
self.canvas = tk.Canvas(
master=self.frame, width=self.img_side_length, height=self.img_side_length)
self.canvas.pack()
self.img_label = tk.Label(self.frame, text="Bla")
self.img_label.pack(side=tk.TOP)
self.no_cell_button = tk.Button(
self.frame, text="2: Not cell!", command=self.is_not_cell)
self.no_cell_button.pack(side=tk.RIGHT)
self.cell_button = tk.Button(
self.frame, text="1: Cell!", command=self.is_cell)
self.cell_button.pack(side=tk.RIGHT)
self.img_path = "/storage/images/"
self.img_list = glob.glob(os.path.join(self.img_path, "*"))
self.img_list.sort()
self.slice_no = 0
self.img_no = 0
self.forward = True
self.no_of_imgs = len(self.img_list)
self.stack = []
self.image_id = self.canvas.create_image(0, 0, anchor=tk.NW)
self.stack = helpers.read_image_sequence(self.img_list[self.img_no])
self.classifications = np.zeros(self.no_of_imgs)
self.out_path = "/dev/null"
self.loop_image()
def loop_image(self):
data = self.stack[self.slice_no]
im = Image.fromarray(data)
im = im.resize((self.img_side_length, self.img_side_length))
self.photo = ImageTk.PhotoImage(image=im)
self.canvas.itemconfigure(self.image_id, image=self.photo)
if self.forward is True:
self.slice_no += 1
if self.slice_no == 21:
self.forward = False
if self.forward is False:
self.slice_no -= 1
if self.slice_no == 0:
self.forward = True
root.after(10, self.loop_image)
def next_image(self):
self.img_no += 1
self.stack = helpers.read_image_sequence(self.img_list[self.img_no])
self.img_label['text'] = self.img_list[self.img_no].split("/")[-1]
def previous_image(self):
self.img_no -= 1
self.stack = helpers.read_image_sequence(self.img_list[self.img_no])
self.img_label['text'] = self.img_list[self.img_no].split("/")[-1]
def is_cell(self):
self.classifications[self.img_no] = 1
with open(self.out_path, "a") as f:
f.write(str(self.img_no) + "," + str(1) + "\n")
print(self.classifications)
self.next_image()
def is_not_cell(self):
self.classifications[self.img_no] = 2
with open(self.out_path, "a") as f:
f.write(str(self.img_no) + "," + str(2) + "\n")
print(self.classifications)
self.next_image()
def goto(self):
self.img_no = int(self.goto_entry.get())
root = tk.Tk()
app = App(root)
root.mainloop()
You are creating 100 images per second and are stacking them on top of each other. After 10 minutes, that's 60,000 images stacked on top of each other. The canvas has performance issues when it has tens of thousands of items on it, even if all but one is invisible.
Instead of creating more and more images on the canvas, just modify the existing image:
def __init__(self):
...
self.canvas = tk.Canvas(...)
...
# create the image object which will display the image
self.image_id = self.canvas.create_image(0, 0, anchor=tk.NW)
def load_image(self):
...
self.photo = ImageTk.PhotoImage(image=im)
# reconfigure the canvas item to show the new image
canvas.itemconfigure(self.image_id, image=self.photo)
...
I am writing a program for a school project. It is a program to book tickets at a cinema theater. I have used tkinter to create a gui for users to select their desired seats. However, I also need to count the number of seats selected, to print the total cost of tickets. But I am stuck here and I am not sure how to proceed(I have just learned classes and do not fully understand them). So far users can select and deselect the buttons. Here is the code that is used.
class buttonc:
def __init__(self,master,x,ro):
self.x = x
self.ro = ro
self.count = 0
self.button = []
self.button += [Button(root,text = self.x,background = "white",width= 2,height = 1,command = lambda:self.color())]
self.click = 0
def clicks(self,numm):
self.click += numm
def color(self):
if self.count == 1:
self.button[0].configure(background = "white")
self.count = 0
self.clicks(-1)
elif self.count == 0:
self.button[0].configure(background = "green")
self.count = 1
self.clicks(1)
def counter(self):
return self.click
def pos(self):
self.button[0].grid(column = self.x+30,row = self.ro, padx = 14, pady = 14)
fo = open('tickets.txt','w')
for i in range(9):
for k in range(25):
b = buttonc(root,k+1,i+1)
b.pos()
fincount = str(b.counter())
root.mainloop()
fo.write(fincount)
fo.close()
As you can see i have used a counter to write the value into a text file, but this value never gets updated. Any help would be appreciated, and thank you in advance.
Are you looking into the file "tickets.txt" and see a 0? If so, it's not really surprising. You extract b.counter() before you enter the main loop.
Rearrange your code like:
buttons = []
for i in range(9):
for k in range(25):
b = buttonc(root,k+1,i+1)
b.pos()
buttons.append(b)
root.mainloop()
fo = open('tickets.txt','w')
for b in buttons:
fo.write(str(b.counter() + "\n")
fo.close()