Skip to content

Instantly share code, notes, and snippets.

@markrmiller
Created April 17, 2023 22:48
Show Gist options
  • Save markrmiller/80f1c2c8e759eb2c34ba6f2b35ed011e to your computer and use it in GitHub Desktop.
Save markrmiller/80f1c2c8e759eb2c34ba6f2b35ed011e to your computer and use it in GitHub Desktop.
import tkinter as tk
import os
import glob
from pathlib import Path
from tkinter import filedialog
from PIL import Image, ImageTk
class ImageManager:
def __init__(self):
self.image_sets = []
self.edit_mode = False
def add_image_set(self, image_set):
self.image_sets.append(image_set)
def remove_image_set(self, index):
del self.image_sets[index]
def add_image_to_set(self, set_index, image_path):
self.image_sets[set_index].images.append(image_path)
def move_image_within_set(self, set_index, old_index, new_index):
self.image_sets[set_index].images.insert(new_index, self.image_sets[set_index].images.pop(old_index))
def get_image_sets(self):
return self.image_sets
class ImageSet:
def __init__(self, images):
self.images = images
self.current_image_index = 0
class ImageManagerApp:
def __init__(self, image_manager):
self.image_manager = image_manager
self.root = tk.Tk()
self.root.title("Image Manager")
self.root.geometry("800x600")
self.images = []
# create a sample image set with a sample image
# image_set = ImageSet("Sample Set")
# image_set.images.append("sample.jpg")
# self.image_manager.add_image_set(image_set)
# self.update_image_set_listbox()
self.edit_mode_button = tk.Button(self.root, text="Edit Mode", command=self.toggle_edit_mode)
self.edit_mode_button.pack()
self.image_set_listbox = tk.Listbox(self.root)
self.image_set_listbox.pack(side=tk.LEFT, fill=tk.Y)
self.image_set_scrollbar = tk.Scrollbar(self.root)
self.image_set_scrollbar.pack(side=tk.LEFT, fill=tk.Y)
self.image_set_listbox.config(yscrollcommand=self.image_set_scrollbar.set)
self.image_set_scrollbar.config(command=self.image_set_listbox.yview)
self.image_set_listbox.bind("<<ListboxSelect>>", self.update_image_listbox)
self.image_listbox = tk.Listbox(self.root)
self.image_listbox.pack(side=tk.LEFT, fill=tk.Y)
self.image_scrollbar = tk.Scrollbar(self.root)
self.image_scrollbar.pack(side=tk.LEFT, fill=tk.Y)
self.image_listbox.config(yscrollcommand=self.image_scrollbar.set)
self.image_scrollbar.config(command=self.image_listbox.yview)
self.image_listbox.bind("<<ListboxSelect>>", self.update_current_image)
self.image_canvas = tk.Canvas(self.root)
self.image_canvas.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
self.image_canvas.bind("<Configure>", self.update_image_grid)
self.next_button = tk.Button(self.root, text="Next Image", command=self.show_next_image)
self.next_button.pack(side=tk.BOTTOM)
self.prev_button = tk.Button(self.root, text="Previous Image", command=self.show_prev_image)
self.prev_button.pack(side=tk.BOTTOM)
self.add_imageset_button = tk.Button(self.root, text="Add ImageSet", command=self.add_image_set)
self.add_imageset_button.pack(side=tk.BOTTOM)
self.add_image_button = tk.Button(self.root, text="Add Image", command=self.add_image_to_set)
self.add_image_button.pack(side=tk.BOTTOM)
self.remove_image_button = tk.Button(self.root, text="Remove Image", command=self.remove_image_from_set)
self.remove_image_button.pack(side=tk.BOTTOM)
self.remove_imageset_button = tk.Button(self.root, text="Remove ImageSet", command=self.remove_image_set)
self.remove_imageset_button.pack(side=tk.BOTTOM)
self.image_listbox.pack_forget()
self.root.bind("<space>", self.show_next_image)
self.update_image_set_listbox()
image_set = ImageSet([])
image_manager.add_image_set(image_set)
self.root.bind("<Configure>", self.update_image_grid)
def on_image_set_listbox_select(self, event):
if self.image_manager.edit_mode:
self.update_image_set_listbox()
def remove_image_set(self):
set_index = self.image_set_listbox.curselection()[0]
self.image_manager.remove_image_set(set_index)
self.update_image_set_listbox()
def remove_image_from_set(self):
if not self.image_manager.edit_mode:
return
if not self.image_set_listbox.curselection() or not self.image_listbox.curselection():
return
set_index = self.image_set_listbox.curselection()[0]
image_index = self.image_listbox.curselection()[0]
self.image_manager.remove_image_from_set(set_index, image_index)
self.update_image_listbox()
def add_image_set(self):
self.image_manager.add_image_set(ImageSet([]))
self.update_image_set_listbox()
def toggle_edit_mode(self):
self.image_manager.edit_mode = not self.image_manager.edit_mode
if self.image_manager.edit_mode:
self.edit_mode_button.config(text="View Mode")
self.remove_imageset_button.config(state=tk.NORMAL)
self.remove_image_button.config(state=tk.NORMAL)
self.image_listbox.pack(side=tk.LEFT, fill=tk.Y)
else:
self.edit_mode_button.config(text="Edit Mode")
self.remove_imageset_button.config(state=tk.DISABLED)
self.remove_image_button.config(state=tk.DISABLED)
self.image_listbox.pack_forget()
self.update_image_set_listbox()
self.update_image_listbox()
def add_image_to_set(self):
if not self.image_manager.edit_mode:
return
if not self.image_set_listbox.curselection():
return
set_index = self.image_set_listbox.curselection()[0]
filetypes = (("JPEG files", "*.jpg"), ("PNG files", "*.png"))
filenames = filedialog.askopenfilenames(filetypes=filetypes)
for filename in filenames:
self.image_manager.add_image_to_set(set_index, filename)
self.update_image_set_listbox()
def update_image_listbox(self, event=None):
if not self.image_manager.edit_mode:
return
set_index = self.image_set_listbox.curselection()[0] if self.image_set_listbox.curselection() else 0
image_set = self.image_manager.get_image_sets()[set_index]
self.image_listbox.delete(0, tk.END)
for j, image_path in enumerate(image_set.images):
self.image_listbox.insert(tk.END, f"{j + 1}. {image_path}")
if self.image_manager.edit_mode:
self.update_image_set_listbox()
def update_image_set_listbox(self):
self.image_set_listbox.delete(0, tk.END)
for i, image_set in enumerate(self.image_manager.get_image_sets()):
self.image_set_listbox.insert(tk.END, f"Image Set {i + 1}")
if self.image_manager.edit_mode:
set_index = self.image_set_listbox.curselection()[0] if self.image_set_listbox.curselection() else 0
image_set = self.image_manager.get_image_sets()[set_index]
self.image_listbox.delete(0, tk.END)
for j, image_path in enumerate(image_set.images):
self.image_listbox.insert(tk.END, f"{j + 1}. {image_path}")
def update_current_image(self, event):
if self.image_manager.edit_mode:
return
selection = event.widget.curselection()
if len(selection) == 0:
return
if len(self.image_manager.get_image_sets()) == 0:
return
self.update_image_grid()
def update_image_grid(self, event=None):
if self.image_manager.edit_mode:
return
self.image_canvas.delete(tk.ALL)
image_sets = self.image_manager.get_image_sets()
num_image_sets = len(image_sets)
# Determine the number of columns and rows based on the current window size
window_width = self.image_canvas.winfo_width()
window_height = self.image_canvas.winfo_height()
cell_size = 200 # You can adjust this value to change the size of the grid cells
cols = window_width // cell_size
rows = (num_image_sets + cols - 1) // cols
if cols > 0:
max_width = window_width // cols
max_height = window_height // rows
for i, image_set in enumerate(image_sets):
if len(image_set.images) > 0:
row = i // cols
col = i % cols
img = Image.open(image_set.images[image_set.current_image_index])
img_width, img_height = img.size
# Calculate new width and height while maintaining aspect ratio
aspect_ratio = float(img_width) / float(img_height)
if img_width > max_width or img_height > max_height:
if (max_width / img_width) < (max_height / img_height):
new_width = max_width
new_height = int(new_width / aspect_ratio)
else:
new_height = max_height
new_width = int(new_height * aspect_ratio)
else:
new_width = img_width
new_height = img_height
img = img.resize((new_width, new_height), Image.ANTIALIAS)
img = ImageTk.PhotoImage(img)
x_offset = (max_width - new_width) // 2
y_offset = (max_height - new_height) // 2
self.image_canvas.create_image(col * max_width + x_offset, row * max_height + y_offset,
anchor=tk.NW, image=img)
self.images.append(img) # Store the image in the list
def show_current_image(self):
image_path = self.image_manager.get_current_image()
img = Image.open(image_path)
img = img.resize((self.image_canvas.winfo_width(), self.image_canvas.winfo_height()), Image.ANTIALIAS)
img = ImageTk.PhotoImage(img)
self.image_canvas.create_image(0, 0, anchor=tk.NW, image=img)
self.image_canvas.image = img
def show_next_image(self, event=None):
for image_set in self.image_manager.get_image_sets():
image_set.current_image_index = (image_set.current_image_index + 1) % len(image_set.images) if len(
image_set.images) > 0 else 0
self.update_image_grid()
def show_prev_image(self):
for image_set in self.image_manager.get_image_sets():
image_set.current_image_index = (image_set.current_image_index - 1) % len(image_set.images) if len(
image_set.images) > 0 else 0
self.update_image_grid()
def load_image_sets():
image_manager = ImageManager()
for image_set_path in glob.glob("image_sets/*.txt"):
with open(image_set_path) as f:
image_paths = f.read().splitlines()
image_set = ImageSet(image_paths)
image_manager.add_image_set(image_set)
return image_manager
def save_image_sets(image_manager):
Path("image_sets").mkdir(parents=True, exist_ok=True)
for i, image_set in enumerate(image_manager.get_image_sets()):
with open(f"image_sets/image_set_{i + 1}.txt", "w") as f:
f.write("\n".join(image_set.images))
def main():
image_manager = load_image_sets()
app = ImageManagerApp(image_manager)
app.root.mainloop()
save_image_sets(image_manager)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment