Remove tk dep, use date for session option

This commit is contained in:
ABelliqueux 2024-09-09 14:19:06 +02:00
parent 2582da088b
commit 0e49b446c6
6 changed files with 147 additions and 42 deletions

BIN
Tuffy_Bold.ttf Normal file

Binary file not shown.

View File

@ -1,5 +1,6 @@
[DEFAULT] [DEFAULT]
#camera_type = 2 #camera_type = 2
use_date_for_folder = false
file_extension = 'png' file_extension = 'png'
projects_folder = 'testcv/' projects_folder = 'testcv/'
onion_skin_onstartup = true onion_skin_onstartup = true

View File

@ -2,6 +2,8 @@ import cv2
import gettext import gettext
from itertools import count from itertools import count
import os import os
# Needed for utf-8 text
from PIL import ImageFont, ImageDraw, Image
from send2trash import send2trash from send2trash import send2trash
import signal import signal
import sys import sys
@ -9,14 +11,14 @@ import subprocess
import time import time
import tomllib import tomllib
# TODO : remove tkinter dep # TODO : remove tkinter dep
import tkinter as tk # ~ import tkinter as tk
from tkinter import filedialog, messagebox # ~ from tkinter import messagebox
import numpy as np import numpy as np
running_from_folder = os.path.realpath(__file__) running_from_folder = os.path.realpath(__file__)
alphabet = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'] alphabet = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
next_letter = 'A'
# l10n # l10n
LOCALE = os.getenv('LANG', 'en_EN') LOCALE = os.getenv('LANG', 'en_EN')
@ -30,7 +32,7 @@ project_settings_defaults = {
'file_extension':'JPG', 'file_extension':'JPG',
'trigger_mode': 'key', 'trigger_mode': 'key',
'projects_folder': '', 'projects_folder': '',
# ~ 'project_letter': 'A' 'use_date_for_folder': False,
'onion_skin_onstartup' : False, 'onion_skin_onstartup' : False,
'onionskin_alpha_default' : 0.4, 'onionskin_alpha_default' : 0.4,
'onionskin_fx' : False, 'onionskin_fx' : False,
@ -61,20 +63,85 @@ for location in config_locations:
config_found_msg = _("Found configuration file in {}").format(os.path.expanduser(location)) config_found_msg = _("Found configuration file in {}").format(os.path.expanduser(location))
print(config_found_msg) print(config_found_msg)
# Create blank image with 0s def generate_text_image(text:str, screen_w, screen_h, bullets=False):
blank_image = np.zeros((project_settings['screen_h'], project_settings['screen_w'],3), np.uint8) text_image = Image.new('RGB',
# Set all pixels to light grey (screen_w, screen_h),
blank_image[:,0:] = 200 (0,0,0)
font = cv2.FONT_HERSHEY_SIMPLEX )
cv2.putText(blank_image,_("Pas d'image"),(int(project_settings['screen_w']/3),int(project_settings['screen_h']/2)), font, 4, (100, 100, 100), 8, cv2.LINE_AA) # ~ text_image = np.zeros((screen_h,screen_w,3),np.uint8)
# ~ text_image_pil = Image.fromarray(text_image)
text_image_draw = ImageDraw.Draw(text_image)
if text is not None:
font = ImageFont.truetype("Tuffy_Bold.ttf", (screen_w/32))
font_len = font.getlength(text.split('\n')[0])
text_image_draw.multiline_text((screen_w/2 - font_len/2, screen_h/2 ),
text,
fill=(255, 255, 255),
font=font,
align='center',
spacing=20
)
if bullets:
dot_radius = screen_w/24
x_unit = (screen_w/32)
y_unit = (screen_h/32)
green_dot = (x_unit*14, y_unit*24)
red_dot = (green_dot[0]+x_unit*4, green_dot[1])
# Green dot
text_image_draw.circle(green_dot,
dot_radius,
fill=(0,255,0),
outline=None,
width=1
)
text_image_draw.text(green_dot,
_("Yes"),
fill=(0, 0, 0),
font=font,
anchor="mm",
spacing=20
)
# Red dot
text_image_draw.circle(red_dot,
dot_radius,
fill=(0,0,255),
outline=None,
width=1
)
text_image_draw.text(red_dot,
_("No"),
fill=(0, 0, 0),
font=font,
anchor="mm",
spacing=20
)
text_image = np.array(text_image)
return text_image
def find_letter_after(letter:str):
if letter in alphabet and alphabet.index(letter) < len(alphabet) - 1: def askyesno(text:str):
return alphabet[alphabet.index(letter) + 1] blank = generate_text_image(text, project_settings['screen_w'], project_settings['screen_h'], bullets=True)
cv2.imshow("StopiCV", blank)
# Wait for input to continue
answer = cv2.waitKey(0)
# Space pressed == yes
if answer%256 == 32 or answer%256 == 48 :
return True
# Any other key == no
else: else:
return False return False
def find_letter_after(letter:str, date=False):
if letter in alphabet and alphabet.index(letter) < len(alphabet) - 1 and not date:
letter = alphabet[alphabet.index(letter) + 1]
else:
# Create folder with date
year,mon,day,hour,minute,sec,wd,yd,dst = time.localtime()
letter = '{}-{}-{}_{}-{}-{}'.format(year,mon,day,hour,minute,sec)
return letter
def get_projects_folder(): def get_projects_folder():
if len(projects_folder): if len(projects_folder):
project_folder = projects_folder project_folder = projects_folder
@ -99,28 +166,34 @@ def get_projects_folder():
def get_session_folder(): def get_session_folder():
global next_letter
project_folder = get_projects_folder() project_folder = get_projects_folder()
if project_folder: if project_folder:
sessions_list = [] sessions_list = []
dir_list = os.listdir(project_folder) dir_list = os.listdir(project_folder)
# Filter folders with name only one char long # Filter folders with name only one char long
for dir in dir_list: for folder in dir_list:
if len(dir) == 1 and dir in alphabet: if len(folder) == 1 and folder in alphabet:
sessions_list.append(dir) sessions_list.append(folder)
# If folders exist, find last folder in alphabetical order # If folders exist, find last folder in alphabetical order
if len(sessions_list): if len(sessions_list):
sessions_list.sort() sessions_list.sort()
last_letter = sessions_list[-1] last_letter = sessions_list[-1]
# By default, find next letter for a new session # By default, find next letter for a new session
next_letter = find_letter_after(last_letter) next_letter = find_letter_after(last_letter, project_settings['use_date_for_folder'])
if next_letter is False: if next_letter is False:
return False return False
# A previous session folder was found; ask the user if they wish to resume session # A previous session folder was found; ask the user if they wish to resume session
resume_session = tk.messagebox.askyesno(_("Resume session?"), _("A previous session was found in {}, resume shooting ?").format(os.path.join(project_folder, last_letter))) if not project_settings['use_date_for_folder']:
resume_session = askyesno(_("A previous session was found in {},\n resume shooting ?").format(os.path.join(project_folder, last_letter)))
# ~ resume_session = tk.messagebox.askyesno(_("Resume session?"), _("A previous session was found in {}, resume shooting ?").format(os.path.join(project_folder, last_letter)))
if resume_session: if resume_session:
next_letter = last_letter next_letter = last_letter
else: else:
if not project_settings['use_date_for_folder']:
next_letter = 'A' next_letter = 'A'
else:
next_letter = find_letter_after('A', project_settings['use_date_for_folder'])
if os.path.exists(os.path.join(project_folder, next_letter)) is False: if os.path.exists(os.path.join(project_folder, next_letter)) is False:
os.mkdir(os.path.join(project_folder, next_letter)) os.mkdir(os.path.join(project_folder, next_letter))
print(_("Using {} as session folder.").format(os.path.join(project_folder, next_letter))) print(_("Using {} as session folder.").format(os.path.join(project_folder, next_letter)))
@ -346,10 +419,10 @@ def main(args):
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cam.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
cv2.namedWindow("StopiCV", cv2.WINDOW_GUI_NORMAL) # ~ cv2.namedWindow("StopiCV", cv2.WINDOW_GUI_NORMAL)
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_OPENGL, cv2.WINDOW_OPENGL) # ~ cv2.setWindowProperty("StopiCV", cv2.WND_PROP_OPENGL, cv2.WINDOW_OPENGL)
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) # ~ cv2.setWindowProperty("StopiCV", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_ASPECT_RATIO, cv2.WINDOW_KEEPRATIO) # ~ cv2.setWindowProperty("StopiCV", cv2.WND_PROP_ASPECT_RATIO, cv2.WINDOW_KEEPRATIO)
frame = get_onionskin_frame(savepath, index) frame = get_onionskin_frame(savepath, index)
while True: while True:
@ -403,20 +476,16 @@ def main(args):
# Key left # Key left
elif (k%256 == 81) or (k%256 == 52): elif (k%256 == 81) or (k%256 == 52):
# Displau previous frame # Displau previous frame
print("Left")
if len(img_list): if len(img_list):
if playback: if playback:
playback = False playback = False
index, frame = previous_frame(index) index, frame = previous_frame(index)
print("L: {}".format(index))
elif (k%256 == 83) or (k%256 == 54): elif (k%256 == 83) or (k%256 == 54):
# Displau next frame # Displau next frame
print("Right")
if len(img_list): if len(img_list):
if playback: if playback:
playback = False playback = False
index, frame = next_frame(index) index, frame = next_frame(index)
print("R: {}".format(index))
# Key e / keypad * # Key e / keypad *
elif (k%256 == 101) or (k%256 == 42): elif (k%256 == 101) or (k%256 == 42):
print("Export") print("Export")
@ -444,8 +513,8 @@ def main(args):
print(_("Window was closed, exiting...")) print(_("Window was closed, exiting..."))
break break
# Take pic # Take pic
# SPACE or numpad 0 pressed
elif (k%256 == 32) or (k%256 == 48): elif (k%256 == 32) or (k%256 == 48):
# SPACE pressed
img_name = return_next_frame_number(get_last_frame(savepath)) img_name = return_next_frame_number(get_last_frame(savepath))
img_path = os.path.join(savepath, img_name) img_path = os.path.join(savepath, img_name)
cv2.imwrite(img_path, og_frame) cv2.imwrite(img_path, og_frame)
@ -455,7 +524,6 @@ def main(args):
else: else:
index += 1 index += 1
frame = get_onionskin_frame(savepath, index) frame = get_onionskin_frame(savepath, index)
# TODO : go back to last frame ?
# REMOVE : Debug print keycode # REMOVE : Debug print keycode
elif k==-1: # normally -1 returned,so don't print it elif k==-1: # normally -1 returned,so don't print it
continue continue
@ -463,23 +531,36 @@ def main(args):
print(k) # else print its value print(k) # else print its value
if 'ffmpeg_process' in locals(): if 'ffmpeg_process' in locals():
if ffmpeg_process.poll() is None: if ffmpeg_process.poll() is None:
print("Ffmpeg is still running. Waiting for task to complete.") print(_("Ffmpeg is still running.\n Waiting for task to complete."))
msg = generate_text_image(_("Ffmpeg is still running.\n Waiting for task to complete."),
project_settings['screen_w'], project_settings['screen_h']
)
cv2.imshow("StopiCV", msg)
# Force window refresh
cv2.pollKey()
try: try:
ffmpeg_process.wait(timeout=1) ffmpeg_process.wait(timeout=20)
except: except:
print("Terminating running process...") print(_("Terminating running process..."))
ffmpeg_process.terminate() ffmpeg_process.terminate()
cam.release() cam.release()
cv2.destroyAllWindows() cv2.destroyAllWindows()
cv2.namedWindow("StopiCV", cv2.WINDOW_GUI_NORMAL)
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_OPENGL, cv2.WINDOW_OPENGL)
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_ASPECT_RATIO, cv2.WINDOW_KEEPRATIO)
ctrlc_pressed = False ctrlc_pressed = False
projects_folder = project_settings['projects_folder'] projects_folder = project_settings['projects_folder']
next_letter = 'Z'
img_list = [] img_list = []
savepath = get_session_folder() savepath = get_session_folder()
onionskin = project_settings['onion_skin_onstartup'] onionskin = project_settings['onion_skin_onstartup']
playback = False playback = False
playhead = 0 playhead = 0
loop_playback = True loop_playback = True
blank_image = generate_text_image(_("No images yet! Start shooting..."), project_settings['screen_w'], project_settings['screen_h'])
if len(savepath): if len(savepath):
project_letter = savepath.split(os.sep)[-1] project_letter = savepath.split(os.sep)[-1]

Binary file not shown.

View File

@ -6,14 +6,20 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-02-18 11:30+0100\n" "POT-Creation-Date: 2024-02-18 11:30+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2024-09-09 12:28+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: arthus <arthus@ateliers>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
msgid "Yes"
msgstr "Oui"
msgid "No"
msgstr "Non"
#: ../main_c.py:96 #: ../main_c.py:96
msgid "No configuration file found, using defaults." msgid "No configuration file found, using defaults."
msgstr "Aucun fichier de configuration trouvé, utilisation des valeurs par défaut." msgstr "Aucun fichier de configuration trouvé, utilisation des valeurs par défaut."
@ -37,8 +43,8 @@ msgid ""
msgstr "\nCaméra introuvable ou occupée." msgstr "\nCaméra introuvable ou occupée."
#: ../main_c.py:256 #: ../main_c.py:256
msgid "A previous session was found in {}, resume shooting ?" msgid "A previous session was found in {},\n resume shooting ?"
msgstr "Une session précédente à été trouvée dans {}, reprendre la session ?" msgstr "Une session précédente à été trouvée dans {},\n reprendre la session ?"
#: ../main_c.py:256 #: ../main_c.py:256
msgid "Resume session?" msgid "Resume session?"
@ -86,3 +92,9 @@ msgstr "Exportation dans {}"
msgid "Mp4 files" msgid "Mp4 files"
msgstr "Fichier Mp4" msgstr "Fichier Mp4"
msgid "Ffmpeg is still running.\n Waiting for task to complete."
msgstr "Ffmpeg est toujours en cours d'éxécution.\n Attente de la fin du processus.'"
msgid "Terminating running process..."
msgstr "Terminaison des processus en cours..."

View File

@ -6,8 +6,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-02-19 18:47+0100\n" "POT-Creation-Date: 2024-02-19 18:47+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2024-09-09 12:28+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: arthus <arthus@ateliers>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -15,6 +15,12 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
msgid "Yes"
msgstr ""
msgid "No"
msgstr ""
#: ../main_c.py:96 #: ../main_c.py:96
msgid "No configuration file found, using defaults." msgid "No configuration file found, using defaults."
msgstr "" msgstr ""
@ -38,7 +44,7 @@ msgid ""
msgstr "" msgstr ""
#: ../main_c.py:281 #: ../main_c.py:281
msgid "A previous session was found in {}, resume shooting ?" msgid "A previous session was found in {},\n resume shooting ?"
msgstr "" msgstr ""
#: ../main_c.py:281 #: ../main_c.py:281
@ -85,3 +91,8 @@ msgstr ""
msgid "Exporting to {}" msgid "Exporting to {}"
msgstr "" msgstr ""
msgid "Ffmpeg is still running.\n Waiting for task to complete."
msgstr ""
msgid "Terminating running process..."
msgstr ""