1 Commits

Author SHA1 Message Date
d99478935d 🚧 Add icons in toolbar
For #17
2023-06-15 12:12:52 +02:00
14 changed files with 34 additions and 228 deletions

View File

@@ -1,19 +0,0 @@
# Drawing Training
Open Source alternative to [GestureDrawing!](https://cubebrush.co/advanches/products/d9q6yq/gesturedrawing)
## Authors
- [@shikiryu](https://git.shikiryu.com/Shikiryu)
## Installation
Install my-project with python
```bash
sudo apt install cmake build-essential tcl-dev tk-dev python3-tk
pip install -r requirements.txt
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 543 B

View File

@@ -1,6 +1,5 @@
import os import os
import sys import sys
from tkinter import PhotoImage
import customtkinter import customtkinter
@@ -16,9 +15,7 @@ def main():
root = customtkinter.CTk() root = customtkinter.CTk()
root.title("Drawing Training!") root.title("Drawing Training!")
root.geometry("300x300") root.geometry("300x600")
icon = PhotoImage(file="assets/icons/logo-dt.png")
root.iconphoto(True, icon)
App(root) App(root)

View File

@@ -1,20 +1,12 @@
.PHONY: build dist .PHONY: build dist
.ONESHELL: TOTAL_LOCATION := $(pip show customtkinter | grep Location)
VENV_DIR=.venv LOCATION=!TOTAL_LOCATION:~0,10!
ACTIVATE_VENV:=. $(VENV_DIR)/bin/activate
LOCATION:= $(shell pip show customtkinter | grep Location | cut -c 11-)
install:
python3 -m venv "$(VENV_DIR)"
$(ACTIVATE_VENV)
pip install --upgrade --requirement requirements.txt
build_linux: build_linux:
rm -rf build dist drawingtraining.spec rm -rf build dist drawingtraining.spec
$(ACTIVATE_VENV)
pyinstaller main.py --onefile -w --hidden-import="PIL._tkinter_finder" -n drawingtraining --add-data "assets:assets" pyinstaller main.py --onefile -w --hidden-import="PIL._tkinter_finder" -n drawingtraining --add-data "assets:assets"
build_windows: build_windows:
rm -rf build dist drawingtraining.spec rm -rf build dist drawingtraining.spec
pyinstaller main.py --onefile -w --hidden-import="PIL._tkinter_finder" -n drawingtraining --add-data "assets;assets" --add-data "$(LOCATION)\customtkinter;customtkinter" --icon="assets/icons/logo-dt.png" pyinstaller main.py --onefile -w --hidden-import="PIL._tkinter_finder" -n drawingtraining --add-data "assets;assets" --add-data "${LOCATION}\customtkinter;customtkinter\"

View File

@@ -1,8 +1,6 @@
pillow~=10.0.1 pillow~=9.5.0
pyinstaller==5.13.1 pyinstaller==5.12.0
pygame~=2.4.0 pygame~=2.4.0
customtkinter~=5.1.3 customtkinter~=5.1.3
configparser~=5.3.0 configparser~=5.3.0
CTkToolTip~=0.4 CTkToolTip~=0.4
requests~=2.32.3
CTkMenuBar~=0.8

View File

@@ -1,7 +1,5 @@
import copy import copy
from tkinter import * from tkinter import *
import PIL
from PIL import ImageTk, Image, ImageOps from PIL import ImageTk, Image, ImageOps
@@ -10,7 +8,6 @@ class ImagePlaceholder:
self.is_break = False self.is_break = False
self.current_image = None self.current_image = None
self.current_original_image = None self.current_original_image = None
self.paused_original_image = None
self.image_window = image_window self.image_window = image_window
self.images = images.copy() self.images = images.copy()
@@ -19,9 +16,6 @@ class ImagePlaceholder:
self.image_label.pack(side=TOP, fill=BOTH, expand=1) self.image_label.pack(side=TOP, fill=BOTH, expand=1)
def display_new_image(self): def display_new_image(self):
if len(self.images) == 0:
self.image_window.on_closing()
return
image_path = self.images.pop(0) image_path = self.images.pop(0)
if image_path == "break": if image_path == "break":
self.is_break = True self.is_break = True
@@ -41,7 +35,7 @@ class ImagePlaceholder:
if w < 21 or h < 21: if w < 21 or h < 21:
w = 1280 w = 1280
h = 1024 h = 1024
self.current_image.thumbnail((w - 20, h - 20), PIL.Image.Resampling.LANCZOS) self.current_image.thumbnail((w - 20, h - 20), Image.ANTIALIAS)
if reload: if reload:
self.load_widget() self.load_widget()
@@ -62,16 +56,3 @@ class ImagePlaceholder:
self.image_label.configure(bg="#e8d4bc" if self.is_break else "#FFFFFF") self.image_label.configure(bg="#e8d4bc" if self.is_break else "#FFFFFF")
self.image_label.configure(image=image_to_display) self.image_label.configure(image=image_to_display)
self.image_label.image = image_to_display self.image_label.image = image_to_display
def pause(self, pause=True, cheat=False):
if pause:
self.paused_original_image = self.current_original_image
if not cheat:
image_path = 'assets/images/break.jpg'
self.current_original_image = Image.open(image_path)
else:
self.current_original_image = self.paused_original_image
self.paused_original_image = None
self.current_image = copy.deepcopy(self.current_original_image)
self.apply_options()

View File

@@ -1,21 +1,18 @@
import copy
import os import os
import subprocess import subprocess
import sys import sys
from tkinter import * from tkinter import *
from tkinter.ttk import Progressbar, Style
from tkinter.messagebox import *
import customtkinter
from CTkToolTip import * from CTkToolTip import *
from PIL import ImageTk, Image from PIL import ImageTk, Image, ImageOps
import customtkinter
from src.util import Util from src.util import Util
class Toolbar: class Toolbar:
def __init__(self, image_window, timer): def __init__(self, image_window, timer):
self.timers = [] self.timers = []
self.is_paused = False
self.current_image = None self.current_image = None
self.current_original_image = None self.current_original_image = None
self.image_window = image_window self.image_window = image_window
@@ -29,67 +26,52 @@ class Toolbar:
next_button = customtkinter.CTkButton(toolbar, image=ImageTk.PhotoImage(Image.open("assets/icons/next.png")), next_button = customtkinter.CTkButton(toolbar, image=ImageTk.PhotoImage(Image.open("assets/icons/next.png")),
text="", command=self.image_window.next_image) text="", command=self.image_window.next_image)
next_button.pack(side=LEFT, padx=0, pady=0) next_button.pack(side=LEFT, padx=0, pady=0)
CTkToolTip(next_button, delay=0.1, message="Next", y_offset=-40, x_offset=-40) CTkToolTip(next_button, delay=0.5, message="Next")
pause_button = customtkinter.CTkButton(toolbar, image=ImageTk.PhotoImage(Image.open("assets/icons/pause.png")),
text="", command=self.pause)
pause_button.pack(side=LEFT, padx=0, pady=0)
CTkToolTip(pause_button, delay=0.1, message="Next", y_offset=-40, x_offset=-40)
self.bw_button = customtkinter.CTkButton(toolbar, image=ImageTk.PhotoImage(Image.open("assets/icons/black-and" self.bw_button = customtkinter.CTkButton(toolbar, image=ImageTk.PhotoImage(Image.open("assets/icons/black-and"
"-white.png")), "-white.png")),
text="", command=self.toggle_black_white) text="", command=self.toggle_black_white)
self.bw_button.pack(side=LEFT, padx=0, pady=0) self.bw_button.pack(side=LEFT, padx=0, pady=0)
CTkToolTip(self.bw_button, delay=0.1, message="Black and white image", y_offset=-40, x_offset=-40) CTkToolTip(self.bw_button, delay=0.5, message="Black and white image")
self.mirror_button = customtkinter.CTkButton(toolbar, command=self.toggle_mirror, text="", self.mirror_button = customtkinter.CTkButton(toolbar, command=self.toggle_mirror, text="",
image=ImageTk.PhotoImage(Image.open("assets/icons/flip.png"))) image=ImageTk.PhotoImage(Image.open("assets/icons/flip.png")))
self.mirror_button.pack(side=LEFT, padx=0, pady=0) self.mirror_button.pack(side=LEFT, padx=0, pady=0)
CTkToolTip(self.mirror_button, delay=0.1, message="Mirrored image", y_offset=-40, x_offset=-40) CTkToolTip(self.mirror_button, delay=0.5, message="Mirrored image")
open_folder_button = customtkinter.CTkButton(toolbar, command=self.open_folder, text="", open_folder_button = customtkinter.CTkButton(toolbar, command=self.open_folder, text="",
image=ImageTk.PhotoImage(Image.open("assets/icons/folder.png"))) image=ImageTk.PhotoImage(Image.open("assets/icons/folder.png")))
open_folder_button.pack(side=LEFT, padx=0, pady=0) open_folder_button.pack(side=LEFT, padx=0, pady=0)
CTkToolTip(open_folder_button, delay=0.1, message="Open folder", y_offset=-40, x_offset=-40) CTkToolTip(open_folder_button, delay=0.5, message="Open folder")
self.always_on_top_button = customtkinter.CTkButton(toolbar, command=self.toggle_always_on_top, text="", self.always_on_top_button = customtkinter.CTkButton(toolbar, command=self.toggle_always_on_top, text="",
image=ImageTk.PhotoImage( image=ImageTk.PhotoImage(
Image.open("assets/icons/copy.png"))) Image.open("assets/icons/copy.png")))
self.always_on_top_button.pack(side=LEFT, padx=0, pady=0) self.always_on_top_button.pack(side=LEFT, padx=0, pady=0)
CTkToolTip(self.always_on_top_button, delay=0.1, message="Always on top", y_offset=-40, x_offset=-40) CTkToolTip(self.always_on_top_button, delay=0.5, message="Always on top")
self.fullscreen_button = customtkinter.CTkButton(toolbar, command=self.toggle_fullscreen, text="", self.fullscreen_button = customtkinter.CTkButton(toolbar, command=self.toggle_fullscreen, text="",
image=ImageTk.PhotoImage( image=ImageTk.PhotoImage(
Image.open("assets/icons/maximize.png"))) Image.open("assets/icons/maximize.png")))
self.fullscreen_button.pack(side=LEFT, padx=0, pady=0) self.fullscreen_button.pack(side=LEFT, padx=0, pady=0)
CTkToolTip(self.fullscreen_button, delay=0.1, message="Fullscreen", y_offset=-40, x_offset=-40) CTkToolTip(self.fullscreen_button, delay=0.5, message="Fullscreen")
timer_button = customtkinter.CTkButton(toolbar, command=self.toggle_timer, text="", timer_button = customtkinter.CTkButton(toolbar, command=self.toggle_timer, text="",
image=ImageTk.PhotoImage(Image.open("assets/icons/sand-clock.png"))) image=ImageTk.PhotoImage(Image.open("assets/icons/timer.png")))
timer_button.pack(side=LEFT, padx=0, pady=0) timer_button.pack(side=LEFT, padx=0, pady=0)
CTkToolTip(timer_button, delay=0.1, message="Toggle timer", y_offset=-40, x_offset=-40) CTkToolTip(timer_button, delay=0.5, message="Toggle timer")
self.timer_label = customtkinter.CTkLabel(toolbar, text=str(self.timer), self.timer_label = customtkinter.CTkLabel(toolbar, text=str(self.timer),
text_color=Util.get_default_button_color()) text_color=Util.get_default_button_color())
self.timer_label.pack(side=RIGHT, ipadx=20) self.timer_label.pack(side=RIGHT, ipadx=20)
style = Style()
style.theme_use('default')
style.configure("black.Horizontal.TProgressbar", background='black')
self.progressbar = Progressbar(toolbar, length=280)
self.progressbar.pack(side=RIGHT, ipadx=20)
def display_new_timer(self): def display_new_timer(self):
if len(self.timers) == 0:
return
self.timer = self.timers.pop(0) self.timer = self.timers.pop(0)
self.timer_label.configure(text=Util.format_seconds(self.timer)) self.timer_label.configure(text=Util.format_seconds(self.timer))
self.timer_check = self.image_window.window.after(1000, self.update_timer, self.timer) self.timer_check = self.image_window.window.after(1000, self.update_timer, self.timer)
def update_timer(self, current): def update_timer(self, current):
current -= 1 current -= 1
self.progressbar['value'] = 100 - (current * 100 / self.timer)
if current == 10: if current == 10:
self.image_window.play_countdown() self.image_window.play_countdown()
if current > 0: if current > 0:
@@ -121,11 +103,8 @@ class Toolbar:
try: try:
self.timer_label.pack_info() self.timer_label.pack_info()
self.timer_label.pack_forget() self.timer_label.pack_forget()
self.progressbar.pack_info()
self.progressbar.pack_forget()
except TclError: except TclError:
self.timer_label.pack(side=RIGHT, ipadx=20) self.timer_label.pack(side=RIGHT, ipadx=20)
self.progressbar.pack(side=RIGHT, ipadx=20)
def open_folder(self): def open_folder(self):
if sys.platform == "win32": if sys.platform == "win32":
@@ -154,23 +133,3 @@ class Toolbar:
"fullscreen"] else Util.get_default_button_color()) "fullscreen"] else Util.get_default_button_color())
self.image_window.window.attributes("-fullscreen", self.image_window.option["fullscreen"]) self.image_window.window.attributes("-fullscreen", self.image_window.option["fullscreen"])
def pause(self):
cheating = False
if not self.is_paused:
cheating = askyesno("Pause", "Laisser l'image affichée ?")
self.is_paused = True
else:
self.is_paused = False
# timer is paused whatever happened
if self.timer_check is not None:
self.image_window.window.after_cancel(self.timer_check)
self.timer_check = None
else:
self.timer_check = self.image_window.window.after(
1000, self.update_timer, int((100-self.progressbar['value'])*self.timer/100)
)
self.image_window.image.pause(pause=self.is_paused, cheat=cheating)

View File

@@ -1,41 +0,0 @@
from datetime import datetime
import os
import io
import tempfile
import requests
from PIL import Image
class Generator(object):
def __init__(self, generator=None, folder=None):
self.selected_folder = folder
if generator == 'poses':
self.generate_poses()
def generate_poses(self):
dt = datetime.now()
ts = int(datetime.timestamp(dt))
url = 'https://quickposes.com/ajax/getPosesForSlider/Chiaroscuro?_=' + str(ts)
resp_json = requests.get(url=url)
if resp_json.status_code == 200:
data = resp_json.json()
c = 0
for src in data:
if os.path.exists(os.path.join(self.selected_folder, src['src'])):
continue
if c >= 3:
break
buffer = tempfile.SpooledTemporaryFile(max_size=1e9)
r = requests.get('https://quickposes.com/assets/poses/' + src['src'], stream=True)
if r.status_code == 200:
downloaded = 0
for chunk in r.iter_content(chunk_size=1024):
downloaded += len(chunk)
buffer.write(chunk)
buffer.seek(0)
i = Image.open(io.BytesIO(buffer.read()))
i.save(os.path.join(self.selected_folder, src['src']), quality=85)
buffer.close()
c += 1

View File

@@ -47,4 +47,4 @@ class Util:
@staticmethod @staticmethod
def get_default_active_button_color(): def get_default_active_button_color():
return "green", "green" return "gray29", "gray29"

View File

@@ -1,30 +0,0 @@
import webbrowser
import customtkinter
from customtkinter import CTkToplevel
class AboutWindow:
def __init__(self, app):
self.app = app
self.window = CTkToplevel(app.root)
self.window.title("À propos")
self.window.geometry("600x300")
self.window.protocol("WM_DELETE_WINDOW", self.on_closing)
link_gesture_drawing = customtkinter.CTkLabel(self.window, text="Open Source alternative to GestureDrawing!")
link_gesture_drawing.pack()
link_gesture_drawing.bind("<Button-1>",
lambda e: self.open_link("https://cubebrush.co/advanches/products/d9q6yq/gesturedrawing"))
link_shikiryu = customtkinter.CTkLabel(self.window, text="by Shikiryu")
link_shikiryu.pack()
link_shikiryu.bind("<Button-1>", lambda e: self.open_link("https://shikiryu.com"))
@staticmethod
def open_link(url):
webbrowser.open_new(url)
def on_closing(self):
self.window.destroy()

View File

@@ -1,15 +1,11 @@
import tkinter
from pathlib import Path from pathlib import Path
from tkinter import * from tkinter import *
from tkinter import filedialog from tkinter import filedialog
import customtkinter import customtkinter
from src.entity.config import Config from src.entity.config import Config
from src.entity.generator import Generator
from src.util import Util from src.util import Util
from src.window.about import AboutWindow
from src.window.image import ImageWindow from src.window.image import ImageWindow
from src.window.session import SessionWindow from src.window.session import SessionWindow
from CTkMenuBar import *
class App: class App:
@@ -25,17 +21,6 @@ class App:
self.selected_folder = "" self.selected_folder = ""
self.found_images = [] self.found_images = []
self.timer = 0 self.timer = 0
menu = CTkMenuBar(master=root)
generate_button = menu.add_cascade("Generate")
help_button = menu.add_cascade("Help")
dropdown = CustomDropdownMenu(widget=generate_button)
dropdown.add_option(option="Poses", command=lambda:self.download_poses('poses'))
about = CustomDropdownMenu(widget=help_button)
about.add_option(option="À propos", command=self.show_help)
self.title = customtkinter.CTkLabel(root, text="Drawing Training") self.title = customtkinter.CTkLabel(root, text="Drawing Training")
self.title.pack() self.title.pack()
@@ -48,8 +33,8 @@ class App:
self.images_len = customtkinter.CTkLabel(root, text="Found : " + str(len(self.found_images))) self.images_len = customtkinter.CTkLabel(root, text="Found : " + str(len(self.found_images)))
self.images_len.pack() self.images_len.pack()
self.button_frame = Frame(root, bg=root.cget("bg")) self.button_frame = Frame(root)
self.button_frame.pack(fill=X, expand=True) self.button_frame.pack(fill=X)
timers = [ timers = [
Util.format_seconds(30), Util.format_seconds(30),
@@ -60,16 +45,14 @@ class App:
Util.format_seconds(600), Util.format_seconds(600),
"Custom" "Custom"
] ]
self.buttons = customtkinter.CTkSegmentedButton(
self.buttons = [] root, values=timers,
for i, timer in enumerate(timers): command=self.set_timer_in_seconds
new_button = customtkinter.CTkButton(self.button_frame, width=10, text=timer, )
command=lambda t=timer: self.set_timer_in_seconds(t)) self.buttons.pack(fill=X)
new_button.pack(side=tkinter.LEFT, fill=X, expand=True)
self.buttons.append(new_button)
self.launch_button = customtkinter.CTkButton(root, text="Let's draw!", command=self.lets_draw, state="disabled") self.launch_button = customtkinter.CTkButton(root, text="Let's draw!", command=self.lets_draw, state="disabled")
self.launch_button.pack(side=tkinter.BOTTOM) self.launch_button.pack(side="bottom")
self.read_config() self.read_config()
@@ -81,9 +64,6 @@ class App:
except Exception: except Exception:
pass pass
def show_help(self):
AboutWindow(self)
def lets_draw(self): def lets_draw(self):
self.image_window = ImageWindow(self) self.image_window = ImageWindow(self)
self.image_window.lets_draw(self.found_images.copy(), self.timer) self.image_window.lets_draw(self.found_images.copy(), self.timer)
@@ -93,18 +73,12 @@ class App:
self.session_window.open() self.session_window.open()
def select_folder(self): def select_folder(self):
new_directory = filedialog.askdirectory() self.selected_folder = filedialog.askdirectory()
if new_directory:
self.selected_folder = new_directory
self.folder_name.configure(text="Folder : " + self.selected_folder) self.folder_name.configure(text="Folder : " + self.selected_folder)
Config.set_config_var("default_folder", self.selected_folder).save() Config.set_config_var("default_folder", self.selected_folder).save()
self.find_images_in_folder() self.find_images_in_folder()
self.check_lets_draw() self.check_lets_draw()
def download_poses(self, generator=None):
Generator(generator=generator, folder=self.selected_folder)
self.find_images_in_folder()
def find_images_in_folder(self): def find_images_in_folder(self):
if Path(self.selected_folder).exists(): if Path(self.selected_folder).exists():
self.found_images = list( self.found_images = list(
@@ -119,11 +93,6 @@ class App:
self.launch_button.configure(state="disabled") self.launch_button.configure(state="disabled")
def set_timer_in_seconds(self, user_data): def set_timer_in_seconds(self, user_data):
# resetting buttons states
for button in self.buttons:
button.configure(fg_color=(Util.get_default_button_color() if user_data != button.cget(
"text") else Util.get_default_active_button_color()))
self.custom = False self.custom = False
if user_data == "Custom": if user_data == "Custom":
return self.custom_session() return self.custom_session()

View File

@@ -45,14 +45,14 @@ class SessionWindow:
text=session.number_of_drawings + " drawings of " + Util.format_seconds(int(session.timer)) + " each", text=session.number_of_drawings + " drawings of " + Util.format_seconds(int(session.timer)) + " each",
command=partial(self.edit_element, i), command=partial(self.edit_element, i),
fg_color=( fg_color=(
Util.get_default_active_button_color() if i == self.selected_element_index else Util.get_default_button_color()) Util.get_default_button_color() if i == self.selected_element_index else Util.get_default_active_button_color())
) )
elif isinstance(session, BreakElement): elif isinstance(session, BreakElement):
new_session = CTkButton(self.left_column, new_session = CTkButton(self.left_column,
text="Break of " + Util.format_seconds(int(session.timer)) + " each", text="Break of " + Util.format_seconds(int(session.timer)) + " each",
command=partial(self.edit_element, i), command=partial(self.edit_element, i),
fg_color=( fg_color=(
Util.get_default_active_button_color() if i == self.selected_element_index else Util.get_default_button_color()) Util.get_default_button_color() if i == self.selected_element_index else Util.get_default_active_button_color())
) )
else: else:
raise Exception("unknown type.") raise Exception("unknown type.")