♻️ Refactor classes to avoid big ones

Fix #20
This commit is contained in:
Clement Desmidt 2023-05-24 14:14:07 +02:00
parent 0bd8f97f14
commit f4c44756f6
7 changed files with 211 additions and 163 deletions

159
image.py
View File

@ -1,159 +0,0 @@
import os
import random
from tkinter import *
from pygame import mixer
from PIL import ImageTk, Image, ImageOps
from util import Util
class ImageWindow:
def __init__(self, app):
self.app = app
self.window = Toplevel(app.root)
self.window.title("Image")
self.window.geometry("1280x1024")
self.option = {
"bw": False,
"mirror": False,
"always_on_top": False,
}
mixer.init()
self.countdown_sound = mixer.Sound('assets/sounds/countdown.mp3')
self.images = []
self.current_image = None
self.image_label = None
toolbar = Frame(self.window, bd=1, relief=RAISED)
toolbar.pack(side=BOTTOM, fill=X)
self.timer = 0
self.timer_label = None
self.timer_check = None
self.timer_check = None
next_button = Button(toolbar, relief=FLAT, compound=LEFT, text="next ➡️", command=self.next_image)
next_button.pack(side=LEFT, padx=0, pady=0)
self.bw_button = Button(toolbar, relief=FLAT, compound=LEFT, text="black&white",
command=self.toggle_black_white)
self.bw_button.pack(side=LEFT, padx=0, pady=0)
self.mirror_button = Button(toolbar, relief=FLAT, compound=LEFT, command=self.toggle_mirror, text="mirror")
self.mirror_button.pack(side=LEFT, padx=0, pady=0)
self.open_folder_button = Button(toolbar, relief=FLAT, compound=LEFT, command=self.open_folder
, text="open folder")
self.open_folder_button.pack(side=LEFT, padx=0, pady=0)
self.always_on_top_button = Button(toolbar, relief=FLAT, compound=LEFT, command=self.toggle_always_on_top
, text="always on top")
self.always_on_top_button.pack(side=LEFT, padx=0, pady=0)
self.always_on_top_check = None
self.timer_button = Button(toolbar, relief=FLAT, compound=LEFT, command=self.toggle_timer, text="timer")
self.timer_button.pack(side=LEFT, padx=0, pady=0)
self.image_label = Label(self.window)
self.image_label.pack(side=TOP, fill=BOTH, expand=1)
self.timer_label = Label(toolbar, text=self.timer)
self.timer_label.pack(side=RIGHT, ipadx=20)
def update_timer(self, current):
current -= 1
if current == 10:
self.play_countdown()
if current > 0:
self.timer_label.configure(text=Util.format_seconds(current))
self.timer_check = self.window.after(1000, self.update_timer, current)
else:
self.window.after_cancel(self.timer_check)
if len(self.images) == 0:
self.window.destroy()
self.window.update()
return
self.next_image()
def next_image(self):
if self.timer_check is not None:
self.window.after_cancel(self.timer_check)
self.display_new_image()
self.display_new_timer()
def toggle_black_white(self):
self.option["bw"] = not self.option["bw"]
self.bw_button.config(bg="blue" if self.option["bw"] else "grey85")
def toggle_mirror(self):
self.option["mirror"] = not self.option["mirror"]
self.mirror_button.config(bg="blue" if self.option["mirror"] else "grey85")
def toggle_timer(self):
try:
self.timer_label.pack_info()
self.timer_label.pack_forget()
except TclError:
self.timer_label.pack(side=RIGHT, ipadx=20)
def open_folder(self):
os.startfile(self.app.selected_folder)
def toggle_always_on_top(self):
self.option["always_on_top"] = not self.option["always_on_top"]
if self.option["always_on_top"]:
self.always_on_top_button.config(bg="blue")
self.stay_on_top()
else:
self.always_on_top_button.config(bg="grey85")
self.window.after_cancel(self.always_on_top_check)
def stay_on_top(self):
# self.window.lift()
self.window.attributes('-topmost', True)
self.always_on_top_check = self.window.after(2000, self.stay_on_top)
def lets_draw(self, images, timer):
random.shuffle(images)
self.images = images
self.timer = timer
self.next_image()
def display_new_timer(self):
self.timer_label.configure(text=self.timer)
self.timer_check = self.window.after(1000, self.update_timer, self.timer)
def display_new_image(self):
image_path = self.images[0]
self.images.pop(0)
self.current_image = Image.open(image_path)
self.resize_image()
if self.option["bw"]:
self.current_image = self.current_image.convert("L")
if self.option["mirror"]:
self.current_image = ImageOps.mirror(self.current_image)
image_to_display = ImageTk.PhotoImage(self.current_image)
self.image_label.configure(image=image_to_display)
self.image_label.bind('<Configure>', lambda event: self.resize_image())
self.image_label.image = image_to_display
def resize_image(self):
ma = self.window.winfo_toplevel()
w, h = ma.winfo_width(), ma.winfo_height()
# if self.current_image.size[0] > w or self.current_image.size[1] > h:
if w < 21 or h < 21: # too small
w = 1280
h = 1024
self.current_image.thumbnail((w - 20, h - 20), Image.ANTIALIAS)
print("resized: win %s >= img %s", (w, h), self.current_image.size)
image_to_display = ImageTk.PhotoImage(self.current_image)
self.image_label.configure(image=image_to_display)
self.image_label.image = image_to_display
def play_countdown(self):
self.countdown_sound.play()

View File

@ -1,5 +1,5 @@
from tkinter import * from tkinter import *
from app import App from src.window.app import App
def main(): def main():

50
src/element/image.py Normal file
View File

@ -0,0 +1,50 @@
import copy
from tkinter import *
from PIL import ImageTk, Image, ImageOps
class ImagePlaceholder:
def __init__(self, image_window, images):
self.current_image = None
self.current_original_image = None
self.image_window = image_window
self.images = images.copy()
self.image_label = Label(self.image_window.window)
self.image_label.bind('<Configure>', lambda event: self.resize_image(True))
self.image_label.pack(side=TOP, fill=BOTH, expand=1)
def display_new_image(self):
image_path = self.images[0]
self.images.pop(0)
self.current_original_image = Image.open(image_path)
self.current_image = copy.deepcopy(self.current_original_image)
self.apply_options()
def resize_image(self, reload=False):
ma = self.image_window.window.winfo_toplevel()
w, h = ma.winfo_width(), ma.winfo_height()
if w < 21 or h < 21:
w = 1280
h = 1024
self.current_image.thumbnail((w - 20, h - 20), Image.ANTIALIAS)
if reload:
self.load_widget()
def apply_options(self):
self.current_image = copy.deepcopy(self.current_original_image)
if self.image_window.option["bw"]:
self.current_image = self.current_image.convert("L")
if self.image_window.option["mirror"]:
self.current_image = ImageOps.mirror(self.current_image)
self.resize_image(True)
def load_widget(self):
image_to_display = ImageTk.PhotoImage(self.current_image)
self.image_label.configure(image=image_to_display)
self.image_label.image = image_to_display

107
src/element/toolbar.py Normal file
View File

@ -0,0 +1,107 @@
import copy
import os
import subprocess
import sys
from tkinter import *
from src.util import Util
class Toolbar:
def __init__(self, image_window, timer):
self.current_image = None
self.current_original_image = None
self.image_window = image_window
self.timer = timer
self.timer_label = None
self.timer_check = None
toolbar = Frame(self.image_window.window, bd=1, relief=RAISED)
toolbar.pack(side=BOTTOM, fill=X)
next_button = Button(toolbar, relief=FLAT, compound=LEFT, text="next ➡️", command=self.image_window.next_image)
next_button.pack(side=LEFT, padx=0, pady=0)
self.bw_button = Button(toolbar, relief=FLAT, compound=LEFT, text="black&white",
command=self.toggle_black_white)
self.bw_button.pack(side=LEFT, padx=0, pady=0)
self.mirror_button = Button(toolbar, relief=FLAT, compound=LEFT, command=self.toggle_mirror,
text="mirror")
self.mirror_button.pack(side=LEFT, padx=0, pady=0)
self.open_folder_button = Button(toolbar, relief=FLAT, compound=LEFT, command=self.open_folder
, text="open folder")
self.open_folder_button.pack(side=LEFT, padx=0, pady=0)
self.always_on_top_button = Button(toolbar, relief=FLAT, compound=LEFT,
command=self.toggle_always_on_top
, text="always on top")
self.always_on_top_button.pack(side=LEFT, padx=0, pady=0)
self.always_on_top_check = None
self.timer_button = Button(toolbar, relief=FLAT, compound=LEFT, command=self.toggle_timer,
text="timer")
self.timer_button.pack(side=LEFT, padx=0, pady=0)
self.timer_label = Label(toolbar, text=self.timer)
self.timer_label.pack(side=RIGHT, ipadx=20)
def display_new_timer(self):
self.timer_label.configure(text=Util.format_seconds(self.timer))
self.timer_check = self.image_window.window.after(1000, self.update_timer, self.timer)
def update_timer(self, current):
current -= 1
if current == 10:
self.image_window.play_countdown()
if current > 0:
self.timer_label.configure(text=Util.format_seconds(current))
self.timer_check = self.image_window.window.after(1000, self.update_timer, current)
else:
self.image_window.window.after_cancel(self.timer_check)
if len(self.image_window.images) == 0:
self.image_window.window.destroy()
self.image_window.window.update()
return
self.image_window.next_image()
def toggle_black_white(self):
self.image_window.option["bw"] = not self.image_window.option["bw"]
self.bw_button.config(bg="blue" if self.image_window.option["bw"] else "grey85")
self.image_window.image.apply_options()
def toggle_mirror(self):
self.image_window.option["mirror"] = not self.image_window.option["mirror"]
self.mirror_button.config(bg="blue" if self.image_window.option["mirror"] else "grey85")
self.image_window.image.apply_options()
def toggle_timer(self):
try:
self.timer_label.pack_info()
self.timer_label.pack_forget()
except TclError:
self.timer_label.pack(side=RIGHT, ipadx=20)
def open_folder(self):
if sys.platform == "win32":
os.startfile(self.image_window.app.selected_folder)
else:
opener = "open" if sys.platform == "darwin" else "xdg-open"
subprocess.call([opener, self.image_window.app.selected_folder])
def toggle_always_on_top(self):
self.image_window.option["always_on_top"] = not self.image_window.option["always_on_top"]
if self.image_window.option["always_on_top"]:
self.always_on_top_button.config(bg="blue")
self.stay_on_top()
else:
self.always_on_top_button.config(bg="grey85")
self.image_window.window.after_cancel(self.always_on_top_check)
def stay_on_top(self):
# self.window.lift()
self.image_window.window.attributes('-topmost', True)
self.always_on_top_check = self.image_window.window.after(2000, self.stay_on_top)

View File

@ -1,9 +1,9 @@
from tkinter import * from tkinter import *
from tkinter import filedialog from tkinter import filedialog
from pathlib import Path from pathlib import Path
from util import Util from src.util import Util
from functools import partial from functools import partial
from image import ImageWindow from src.window.image import ImageWindow
class App: class App:
@ -36,7 +36,7 @@ class App:
t = i t = i
new_button = Button(self.button_frame, text=Util.format_seconds(t), new_button = Button(self.button_frame, text=Util.format_seconds(t),
command=partial(self.set_timer_in_seconds, t)) command=partial(self.set_timer_in_seconds, t))
# new_button.pack(side="left")
self.buttons.append(new_button) self.buttons.append(new_button)
self.button_frame.columnconfigure(i, weight=1) self.button_frame.columnconfigure(i, weight=1)
new_button.grid(row=0, column=i, sticky=W + E) new_button.grid(row=0, column=i, sticky=W + E)

50
src/window/image.py Normal file
View File

@ -0,0 +1,50 @@
import random
from tkinter import *
from pygame import mixer
from src.element.image import ImagePlaceholder
from src.element.toolbar import Toolbar
class ImageWindow:
def __init__(self, app):
self.current_original_image = None
self.app = app
self.window = Toplevel(app.root)
self.window.title("Image")
self.window.geometry("1280x1024")
self.option = {
"bw": False,
"mirror": False,
"always_on_top": False,
}
mixer.init()
self.countdown_sound = mixer.Sound('assets/sounds/countdown.mp3')
self.images = []
self.current_image = None
self.image_label = None
toolbar = Frame(self.window, bd=1, relief=RAISED)
toolbar.pack(side=BOTTOM, fill=X)
self.timer = 0
self.toolbar = Toolbar(self, self.timer)
self.image = ImagePlaceholder(self, self.images)
def next_image(self):
if self.toolbar.timer_check is not None:
self.window.after_cancel(self.toolbar.timer_check)
self.image.display_new_image()
self.toolbar.display_new_timer()
def lets_draw(self, images, timer):
random.shuffle(images)
# self.images = images
self.image.images = images
self.toolbar.timer = timer
# self.timer = timer
self.next_image()
def play_countdown(self):
self.countdown_sound.play()