Add config file, l10n, session resuming
This commit is contained in:
parent
4d24f104d9
commit
a19f713d16
25
config.toml
Normal file
25
config.toml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
file_extension = 'JPG'
|
||||||
|
trigger_mode = 'key'
|
||||||
|
projects_folder = ''
|
||||||
|
# project_letter = A
|
||||||
|
onion_skin_onstartup = false
|
||||||
|
onionskin_alpha_default = 0.4
|
||||||
|
onionskin_fx = false
|
||||||
|
fullscreen_bool = true
|
||||||
|
screen_w = 1920
|
||||||
|
screen_h = 1080
|
||||||
|
framerate = 16
|
||||||
|
vflip = true
|
||||||
|
hflip = false
|
||||||
|
export_options = 'scale=1920:-1,crop=1920:1080:0:102'
|
||||||
|
[CAMERA]
|
||||||
|
# Nikon D40x
|
||||||
|
capturemode = 3 # use IR remote
|
||||||
|
imagesize = 2 # use size S (1936x1296)
|
||||||
|
whitebalance = 1 # Natural light
|
||||||
|
capturetarget = 0 # Internal memory
|
||||||
|
nocfcardrelease = 0 # Allow capture without sd card
|
||||||
|
recordingmedia = 1 # Write to RAM
|
||||||
|
[CHECK]
|
||||||
|
acpower = 0 # we d'rather have this set to 0 which means we're running on AC
|
BIN
locales/en/LC_MESSAGES/template.mo
Normal file
BIN
locales/en/LC_MESSAGES/template.mo
Normal file
Binary file not shown.
BIN
locales/fr/LC_MESSAGES/template.mo
Normal file
BIN
locales/fr/LC_MESSAGES/template.mo
Normal file
Binary file not shown.
199
main_c.py
199
main_c.py
@ -1,82 +1,110 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
#
|
#
|
||||||
import collections
|
import collections
|
||||||
|
import gettext
|
||||||
|
# DLSR support
|
||||||
|
import gphoto2 as gp
|
||||||
|
from itertools import count
|
||||||
import locale
|
import locale
|
||||||
import os
|
import os
|
||||||
|
from PIL import Image, ImageTk, ImageFilter
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import filedialog
|
from tkinter import filedialog, messagebox
|
||||||
|
import tomllib
|
||||||
from PIL import Image, ImageTk, ImageFilter
|
|
||||||
# DLSR support
|
|
||||||
import gphoto2 as gp
|
|
||||||
# async FFMPEG exportation support
|
# async FFMPEG exportation support
|
||||||
import asyncio
|
import asyncio
|
||||||
from ffmpeg.asyncio import FFmpeg
|
from ffmpeg.asyncio import FFmpeg
|
||||||
|
|
||||||
from itertools import count
|
|
||||||
from send2trash import send2trash
|
from send2trash import send2trash
|
||||||
|
|
||||||
# TODO
|
# TODO : Todo List
|
||||||
#
|
#
|
||||||
# X wait event mode
|
# X wait event mode
|
||||||
# X remove frames
|
# X remove frames
|
||||||
# X keep images in memory (odict > list ?)
|
# X keep images in memory (odict > list ?)
|
||||||
# o resize images upon shooting/ crop output video ?
|
# / resize images upon shooting/crop output video ?
|
||||||
# o project workflow
|
# X project workflow
|
||||||
# X use a different folder for each project (A, B, C, etc...)
|
# X use a different folder for each project (A, B, C, etc...)
|
||||||
# X startup : check for existing folder with name A, B, C, etc.
|
# X startup : check for existing folder with name A, B, C, etc.
|
||||||
# - startup : offer to continue previous project or new project
|
# X startup : offer to continue previous project or new project
|
||||||
# - if continue, find last frame in folder X else, find next letter and create folder
|
# X if continue, find last frame in folder X else, find next letter and create folder
|
||||||
# o notify export endingS
|
# o notify export ending
|
||||||
# X colored onion skin frame (pillow filters)
|
# X colored onion skin frame (pillow filters)
|
||||||
# V startup frame
|
# X startup frame
|
||||||
# o open existing project screen
|
# o webcam support (pygame, win and linux only)
|
||||||
|
# X Import config values from config file
|
||||||
|
# X Translation
|
||||||
|
# o Better settings names
|
||||||
|
|
||||||
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']
|
||||||
|
|
||||||
project_settings = {
|
# l10n
|
||||||
|
LOCALE = os.getenv('LANG', 'en_EN')
|
||||||
|
_ = gettext.translation('template', localedir='locales', languages=[LOCALE]).gettext
|
||||||
|
|
||||||
|
# Config
|
||||||
|
# defaults
|
||||||
|
project_settings_defaults = {
|
||||||
'file_extension':'JPG',
|
'file_extension':'JPG',
|
||||||
'trigger_mode':'',
|
'trigger_mode': 'key',
|
||||||
'projects_folder': '',
|
'projects_folder': '',
|
||||||
# ~ 'project_letter': 'A'
|
# ~ 'project_letter': 'A'
|
||||||
|
'onion_skin_onstartup' : False,
|
||||||
|
'onionskin_alpha_default' : 0.4,
|
||||||
|
'onionskin_fx' : False,
|
||||||
|
'fullscreen_bool' : True,
|
||||||
|
'screen_w' : 640,
|
||||||
|
'screen_h' : 480,
|
||||||
|
'framerate' : 16,
|
||||||
|
'vflip' : False,
|
||||||
|
'hflip' : False,
|
||||||
|
'export_options' : 'scale=1920:-1,crop=1920:1080:0:102',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Camera Settings (Nikon D40x)
|
||||||
|
# See `gphoto2 --list-config` to find your camera's values
|
||||||
|
camera_settings = camera_status = {}
|
||||||
|
|
||||||
|
# Load from file
|
||||||
|
config_locations = ["./", "~/.", "~/.config/"]
|
||||||
|
config_found_msg = _("No configuration file found, using defaults.")
|
||||||
|
project_settings = project_settings_defaults
|
||||||
|
for location in config_locations:
|
||||||
|
# Optional config files, ~ is expanded to $HOME on *nix, %USERPROFILE% on windows
|
||||||
|
if os.path.exists( os.path.expanduser(os.path.join(location, 'config.toml'))):
|
||||||
|
with open(os.path.expanduser(location + 'config.toml'), 'rb') as config_file:
|
||||||
|
project_settings = tomllib.load(config_file)
|
||||||
|
if 'DEFAULT' in project_settings:
|
||||||
|
project_settings = project_settings['DEFAULT']
|
||||||
|
if 'CAMERA' in project_settings:
|
||||||
|
camera_settings = project_settings['CAMERA']
|
||||||
|
if 'CHECK' in project_settings:
|
||||||
|
camera_settings = project_settings['CHECK']
|
||||||
|
config_found_msg = _("Found configuration file in {}").format(os.path.expanduser(location))
|
||||||
|
print(config_found_msg)
|
||||||
|
# TODO : Check resulting config has needed settings, fill missing ones
|
||||||
|
|
||||||
class KISStopmo(tk.Tk):
|
class KISStopmo(tk.Tk):
|
||||||
|
|
||||||
def __init__(self, *args, **kargs):
|
def __init__(self, *args, **kargs):
|
||||||
|
self.check_config()
|
||||||
|
print(project_settings)
|
||||||
# Default config
|
# Default config
|
||||||
# TODO : Import value from config file
|
# Set script settings according to config file
|
||||||
# Script settings
|
self.onion_skin = project_settings['onion_skin_onstartup']
|
||||||
self.onion_skin = False
|
self.onionskin_alpha = project_settings['onionskin_alpha_default']
|
||||||
self.onionskin_alpha = 0.4
|
self.onionskin_fx = project_settings['onionskin_fx']
|
||||||
self.onionskin_fx = False
|
self.fullscreen_bool = project_settings['fullscreen_bool']
|
||||||
self.fullscreen_bool = True
|
self.screen_w, self.screen_h = project_settings['screen_w'], project_settings['screen_h']
|
||||||
self.screen_w, self.screen_h = 640, 480
|
self.framerate = project_settings['framerate']
|
||||||
self.framerate = 16
|
|
||||||
self.photo = None
|
|
||||||
# ~ self.savepath = os.getcwd()
|
|
||||||
|
|
||||||
|
# ~ for setting in camera_settings:
|
||||||
|
# ~ print(setting)
|
||||||
|
# ~ self.photo = None
|
||||||
self.end_thread = False
|
self.end_thread = False
|
||||||
|
|
||||||
# ~ self.project_settings = {'file_extension' : 'JPG'}
|
|
||||||
# ~ self.project_letter = "A"
|
|
||||||
|
|
||||||
# Camera Settings
|
|
||||||
self.camera_settings = {'capturemode': 3,
|
|
||||||
'imagesize': 2,
|
|
||||||
'whitebalance': 5,
|
|
||||||
'capturetarget': 0,
|
|
||||||
'nocfcardrelease': 0,
|
|
||||||
'recordingmedia' : 1
|
|
||||||
}
|
|
||||||
|
|
||||||
self.camera_status = { 'acpower': 0 # we d'rather have this set to 0 which means we're running on AC
|
|
||||||
}
|
|
||||||
|
|
||||||
# Window setup
|
# Window setup
|
||||||
self.screen_w, self.screen_h = root.winfo_screenwidth(), root.winfo_screenheight()
|
self.screen_w, self.screen_h = root.winfo_screenwidth(), root.winfo_screenheight()
|
||||||
root.wm_attributes("-fullscreen", self.fullscreen_bool)
|
root.wm_attributes("-fullscreen", self.fullscreen_bool)
|
||||||
@ -87,8 +115,7 @@ class KISStopmo(tk.Tk):
|
|||||||
self.label.pack()
|
self.label.pack()
|
||||||
|
|
||||||
# Savepath setup
|
# Savepath setup
|
||||||
|
self.projects_folder = tk.filedialog.askdirectory()
|
||||||
self.projects_folder = filedialog.askdirectory()
|
|
||||||
self.savepath = self.get_session_folder()
|
self.savepath = self.get_session_folder()
|
||||||
if len(self.savepath):
|
if len(self.savepath):
|
||||||
self.project_letter = self.savepath.split(os.sep)[-1]
|
self.project_letter = self.savepath.split(os.sep)[-1]
|
||||||
@ -100,8 +127,7 @@ class KISStopmo(tk.Tk):
|
|||||||
self.input_options = {"f": "image2", "r": str(self.framerate)}
|
self.input_options = {"f": "image2", "r": str(self.framerate)}
|
||||||
# ~ self.output_filename = "{folder}{sep}{filename}.mp4".format(folder=self.projects_folder, sep=os.sep, filename=self.savepath.split(os.sep)[-1])
|
# ~ self.output_filename = "{folder}{sep}{filename}.mp4".format(folder=self.projects_folder, sep=os.sep, filename=self.savepath.split(os.sep)[-1])
|
||||||
self.output_filename = "{filename}.mp4".format(filename=self.project_letter)
|
self.output_filename = "{filename}.mp4".format(filename=self.project_letter)
|
||||||
self.output_options = {"vf":"scale=1920:-1,crop=1920:1080:0:102"}
|
self.output_options = self.parse_export_options(project_settings['export_options'], project_settings['vflip'], project_settings['hflip'] )
|
||||||
|
|
||||||
# Get frames list
|
# Get frames list
|
||||||
self.img_list = {}
|
self.img_list = {}
|
||||||
self.img_list = self.get_frames_list(self.savepath)
|
self.img_list = self.get_frames_list(self.savepath)
|
||||||
@ -115,9 +141,9 @@ class KISStopmo(tk.Tk):
|
|||||||
self.current_camera_config = gp.check_result(gp.gp_camera_get_config(self.camera))
|
self.current_camera_config = gp.check_result(gp.gp_camera_get_config(self.camera))
|
||||||
self.apply_camera_settings(self.camera, self.current_camera_config)
|
self.apply_camera_settings(self.camera, self.current_camera_config)
|
||||||
if self.check_status(self.camera, self.current_camera_config) is False:
|
if self.check_status(self.camera, self.current_camera_config) is False:
|
||||||
print("Warning: Some settings are not set to the recommended value!")
|
print(_("Warning: Some settings are not set to the recommended value!"))
|
||||||
except:
|
except:
|
||||||
self.splash_text = 'Camera not found or busy.'
|
self.splash_text = _("Camera not found or busy.")
|
||||||
|
|
||||||
self.timeout = 3000 # milliseconds
|
self.timeout = 3000 # milliseconds
|
||||||
|
|
||||||
@ -152,14 +178,31 @@ class KISStopmo(tk.Tk):
|
|||||||
if project_settings['trigger_mode'] != 'event':
|
if project_settings['trigger_mode'] != 'event':
|
||||||
root.bind("<j>", self.capture_image)
|
root.bind("<j>", self.capture_image)
|
||||||
|
|
||||||
|
|
||||||
|
def check_config(self):
|
||||||
|
global project_settings
|
||||||
|
for setting in project_settings_defaults:
|
||||||
|
if setting not in project_settings:
|
||||||
|
project_settings[setting] = project_settings_defaults[setting]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_export_options(self, options:str, vflip:bool=False, hflip:bool=False):
|
||||||
|
options = {"vf" : options}
|
||||||
|
if vflip:
|
||||||
|
options['vf'] += ',vflip'
|
||||||
|
if hflip:
|
||||||
|
options['vf'] += ',hflip'
|
||||||
|
return options
|
||||||
|
|
||||||
def generate_splashscreen(self):
|
def generate_splashscreen(self):
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
img = Image.new('RGB', (self.screen_w, self.screen_h), (128,128,128))
|
img = Image.new('RGB', (self.screen_w, self.screen_h), (128,128,128))
|
||||||
d = ImageDraw.Draw(img)
|
d = ImageDraw.Draw(img)
|
||||||
if self.splash_text is not None:
|
if self.splash_text is not None:
|
||||||
fnt = ImageFont.truetype("FreeMono", 100)
|
fnt = ImageFont.truetype("FreeMono", 50)
|
||||||
d.text((900, 500 ), self.splash_text, fill=(255, 0, 0), font=fnt)
|
fnt_len = fnt.getlength(self.splash_text)
|
||||||
|
d.text((self.screen_w/2 - fnt_len/2, self.screen_h/2 ), self.splash_text, fill=(255, 0, 0), font=fnt)
|
||||||
# Save to file
|
# Save to file
|
||||||
# img.save('test.png')
|
# img.save('test.png')
|
||||||
# Use in-memory
|
# Use in-memory
|
||||||
@ -212,13 +255,19 @@ class KISStopmo(tk.Tk):
|
|||||||
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
|
||||||
next_letter = self.find_letter_after(last_letter)
|
next_letter = self.find_letter_after(last_letter)
|
||||||
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
|
||||||
|
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:
|
||||||
|
next_letter = last_letter
|
||||||
else:
|
else:
|
||||||
next_letter = 'A'
|
next_letter = 'A'
|
||||||
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)))
|
||||||
return os.path.join(project_folder, next_letter)
|
return os.path.join(project_folder, next_letter)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -234,9 +283,9 @@ class KISStopmo(tk.Tk):
|
|||||||
cam_file = self.camera.file_get(
|
cam_file = self.camera.file_get(
|
||||||
event_data.folder, event_data.name, gp.GP_FILE_TYPE_NORMAL)
|
event_data.folder, event_data.name, gp.GP_FILE_TYPE_NORMAL)
|
||||||
# ~ next_filename = prefix+next(filename)+ext
|
# ~ next_filename = prefix+next(filename)+ext
|
||||||
next_filename = self.return_next_frame_number(self.get_last_frame(os.getcwd()))
|
next_filename = self.return_next_frame_number(self.get_last_frame(self.savepath))
|
||||||
target_path = os.path.join(os.getcwd(), next_filename)
|
target_path = os.path.join(os.getcwd(), next_filename)
|
||||||
print("Image is being saved to {}".format(target_path))
|
print(_("Image is being saved to {}").format(target_path))
|
||||||
cam_file.save(target_path)
|
cam_file.save(target_path)
|
||||||
# ~ return 0
|
# ~ return 0
|
||||||
|
|
||||||
@ -252,6 +301,7 @@ class KISStopmo(tk.Tk):
|
|||||||
existing_animation_files = self.img_list
|
existing_animation_files = self.img_list
|
||||||
# ~ existing_animation_files =
|
# ~ existing_animation_files =
|
||||||
file_list = os.listdir(folder)
|
file_list = os.listdir(folder)
|
||||||
|
print(file_list)
|
||||||
for file in file_list:
|
for file in file_list:
|
||||||
if (file.startswith(self.project_letter) and file.endswith(project_settings['file_extension'])):
|
if (file.startswith(self.project_letter) and file.endswith(project_settings['file_extension'])):
|
||||||
if file not in existing_animation_files:
|
if file not in existing_animation_files:
|
||||||
@ -291,7 +341,7 @@ class KISStopmo(tk.Tk):
|
|||||||
# ~ os.rename(os.path.realpath(frame_list[i]), os.path.realpath("{}{}{}".format(project_settings['project_letter'], next(counter), project_settings['file_extension'])))
|
# ~ os.rename(os.path.realpath(frame_list[i]), os.path.realpath("{}{}{}".format(project_settings['project_letter'], next(counter), project_settings['file_extension'])))
|
||||||
os.rename(os.path.realpath(i), os.path.realpath("{}{}{}".format(self.project_letter, next(counter), project_settings['file_extension'])))
|
os.rename(os.path.realpath(i), os.path.realpath("{}{}{}".format(self.project_letter, next(counter), project_settings['file_extension'])))
|
||||||
else:
|
else:
|
||||||
print(str(i) + " does not exist")
|
print(_("{} does not exist").format(str(i)))
|
||||||
return self.get_frames_list(folder)
|
return self.get_frames_list(folder)
|
||||||
|
|
||||||
|
|
||||||
@ -304,7 +354,7 @@ class KISStopmo(tk.Tk):
|
|||||||
self.img_list[list(self.img_list.keys())[i]] = None
|
self.img_list[list(self.img_list.keys())[i]] = None
|
||||||
|
|
||||||
|
|
||||||
# FIXME
|
# FIXME: Does this still work ?
|
||||||
def remove_frame(self, event):
|
def remove_frame(self, event):
|
||||||
if len(list(self.img_list.items())):
|
if len(list(self.img_list.items())):
|
||||||
|
|
||||||
@ -316,7 +366,7 @@ class KISStopmo(tk.Tk):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
# ~ print(self.img_list)
|
# ~ print(self.img_list)
|
||||||
print("removing {}".format(frame_path))
|
print(_("Removing {}").format(frame_path))
|
||||||
# trash file
|
# trash file
|
||||||
send2trash(frame_path)
|
send2trash(frame_path)
|
||||||
# remove entry from dict
|
# remove entry from dict
|
||||||
@ -337,15 +387,18 @@ class KISStopmo(tk.Tk):
|
|||||||
self.update_image(None, self.img_index)
|
self.update_image(None, self.img_index)
|
||||||
|
|
||||||
# ~ def open_jpg(self, filepath:str, w:int, h:int):
|
# ~ def open_jpg(self, filepath:str, w:int, h:int):
|
||||||
def open_jpg(self, filetuple:tuple, w:int, h:int):
|
def open_jpg(self, filetuple:tuple, w:int, h:int, vflip:bool=False, hflip:bool=False):
|
||||||
# If pic not cached
|
# If pic not cached
|
||||||
if filetuple[-1] is None:
|
if filetuple[-1] is None:
|
||||||
try:
|
try:
|
||||||
# ~ image = Image.open(filepath)
|
# ~ image = Image.open(filepath)
|
||||||
# ~ print( filetuple[0] + "is not in cache")
|
# ~ print( filetuple[0] + "is not in cache")
|
||||||
image = Image.open(os.path.join(self.savepath, filetuple[0]))
|
image = Image.open(os.path.join(self.savepath, filetuple[0]))
|
||||||
# TODO : Keep aspect ratio
|
|
||||||
image = image.resize((w, h))
|
image = image.resize((w, h))
|
||||||
|
if vflip:
|
||||||
|
image = image.transpose(Image.Transpose.FLIP_TOP_BOTTOM)
|
||||||
|
if hflip:
|
||||||
|
image = image.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||||
self.img_list[filetuple[0]] = image
|
self.img_list[filetuple[0]] = image
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return False
|
return False
|
||||||
@ -381,7 +434,7 @@ class KISStopmo(tk.Tk):
|
|||||||
# ~ prev_image = prev_image.resize((self.screen_w, self.screen_h))
|
# ~ prev_image = prev_image.resize((self.screen_w, self.screen_h))
|
||||||
prev_image = list(self.img_list.items())[self.img_index-1]
|
prev_image = list(self.img_list.items())[self.img_index-1]
|
||||||
if prev_image[-1] is None:
|
if prev_image[-1] is None:
|
||||||
prev_image = self.open_jpg(prev_image, self.screen_w, self.screen_h)
|
prev_image = self.open_jpg(prev_image, project_settings['screen_w'], project_settings['screen_h'], project_settings['vflip'], project_settings['hflip'])
|
||||||
else:
|
else:
|
||||||
prev_image = prev_image[-1]
|
prev_image = prev_image[-1]
|
||||||
if fx:
|
if fx:
|
||||||
@ -402,7 +455,7 @@ class KISStopmo(tk.Tk):
|
|||||||
if list(self.img_list.items())[index][0] == "{}.{:04d}.{}".format(self.project_letter, -1, project_settings['file_extension']):
|
if list(self.img_list.items())[index][0] == "{}.{:04d}.{}".format(self.project_letter, -1, project_settings['file_extension']):
|
||||||
new_image = self.splashscreen
|
new_image = self.splashscreen
|
||||||
else:
|
else:
|
||||||
new_image = self.open_jpg(list(self.img_list.items())[index], self.screen_w, self.screen_h)
|
new_image = self.open_jpg(list(self.img_list.items())[index], project_settings['screen_w'], project_settings['screen_h'], project_settings['vflip'], project_settings['hflip'])
|
||||||
else:
|
else:
|
||||||
new_image = self.splashscreen
|
new_image = self.splashscreen
|
||||||
# ~ return False
|
# ~ return False
|
||||||
@ -412,7 +465,7 @@ class KISStopmo(tk.Tk):
|
|||||||
photo = ImageTk.PhotoImage(new_image)
|
photo = ImageTk.PhotoImage(new_image)
|
||||||
if self.onion_skin:
|
if self.onion_skin:
|
||||||
if index:
|
if index:
|
||||||
photo = self.apply_onionskin(new_image, self.onionskin_alpha, self.onionskin_fx)
|
photo = self.apply_onionskin(new_image, project_settings['onionskin_alpha'], project_settings['onionskin_fx'])
|
||||||
# ~ print(photo)
|
# ~ print(photo)
|
||||||
self.label.configure(image=photo)
|
self.label.configure(image=photo)
|
||||||
self.label.image = photo
|
self.label.image = photo
|
||||||
@ -489,14 +542,14 @@ class KISStopmo(tk.Tk):
|
|||||||
|
|
||||||
def apply_camera_settings(self, camera, config):
|
def apply_camera_settings(self, camera, config):
|
||||||
# iterate over the settings dictionary
|
# iterate over the settings dictionary
|
||||||
for setting in self.camera_settings:
|
for setting in camera_settings:
|
||||||
# find the capture mode config item
|
# find the capture mode config item
|
||||||
# ~ cur_setting = gp.check_result(gp.gp_widget_get_child_by_name(config, setting))
|
# ~ cur_setting = gp.check_result(gp.gp_widget_get_child_by_name(config, setting))
|
||||||
# ~ # find corresponding choice
|
# ~ # find corresponding choice
|
||||||
# ~ cur_setting_choice = gp.check_result(gp.gp_widget_get_choice(cur_setting, camera_settings[setting]))
|
# ~ cur_setting_choice = gp.check_result(gp.gp_widget_get_choice(cur_setting, camera_settings[setting]))
|
||||||
# ~ # set config value
|
# ~ # set config value
|
||||||
# ~ gp.check_result(gp.gp_widget_set_value(cur_setting, cur_setting_choice))
|
# ~ gp.check_result(gp.gp_widget_set_value(cur_setting, cur_setting_choice))
|
||||||
self.set_config_value(config, setting, self.camera_settings[setting])
|
self.set_config_value(config, setting, camera_settings[setting])
|
||||||
# validate config
|
# validate config
|
||||||
gp.check_result(gp.gp_camera_set_config(self.camera, config))
|
gp.check_result(gp.gp_camera_set_config(self.camera, config))
|
||||||
|
|
||||||
@ -512,11 +565,11 @@ class KISStopmo(tk.Tk):
|
|||||||
|
|
||||||
|
|
||||||
def check_status(self, camera, config):
|
def check_status(self, camera, config):
|
||||||
for value in self.camera_status:
|
for value in camera_status:
|
||||||
# ~ cur_check = gp.check_result(gp.gp_widget_get_child_by_name(config, value))
|
# ~ cur_check = gp.check_result(gp.gp_widget_get_child_by_name(config, value))
|
||||||
# ~ cur_check_choice = gp.check_result(gp.gp_widget_get_choice(cur_check, camera_status[value]))
|
# ~ cur_check_choice = gp.check_result(gp.gp_widget_get_choice(cur_check, camera_status[value]))
|
||||||
# ~ cur_check_value = gp.check_result(gp.gp_widget_get_value(cur_check))
|
# ~ cur_check_value = gp.check_result(gp.gp_widget_get_value(cur_check))
|
||||||
cur_check_value, cur_check_choice = self.check_status_value(config, value, self.camera_status)
|
cur_check_value, cur_check_choice = self.check_status_value(config, value, camera_status)
|
||||||
if cur_check_value == cur_check_choice:
|
if cur_check_value == cur_check_choice:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@ -535,7 +588,7 @@ class KISStopmo(tk.Tk):
|
|||||||
speed = 30
|
speed = 30
|
||||||
if current_lightmeter_value < 0: # or current_lightmeter_value > 7:
|
if current_lightmeter_value < 0: # or current_lightmeter_value > 7:
|
||||||
# ~ while current_lightmeter_value < -7:
|
# ~ while current_lightmeter_value < -7:
|
||||||
print("speed too high")
|
print(_("speed too high"))
|
||||||
while current_lightmeter_value < -7:
|
while current_lightmeter_value < -7:
|
||||||
self.set_config_value(config, 'shutterspeed2', speed) # 0 to 46
|
self.set_config_value(config, 'shutterspeed2', speed) # 0 to 46
|
||||||
gp.check_result(gp.gp_camera_set_config(camera, config))
|
gp.check_result(gp.gp_camera_set_config(camera, config))
|
||||||
@ -548,7 +601,7 @@ class KISStopmo(tk.Tk):
|
|||||||
print(speed)
|
print(speed)
|
||||||
print(str(current_lightmeter_value) + " - " + str(current_shutterspeed_value))
|
print(str(current_lightmeter_value) + " - " + str(current_shutterspeed_value))
|
||||||
if current_lightmeter_value > 0:
|
if current_lightmeter_value > 0:
|
||||||
print("speed too low")
|
print(_("Speed too low."))
|
||||||
while current_lightmeter_value > 7:
|
while current_lightmeter_value > 7:
|
||||||
self.set_config_value(config, 'shutterspeed2', speed) # 0 to 46
|
self.set_config_value(config, 'shutterspeed2', speed) # 0 to 46
|
||||||
gp.check_result(gp.gp_camera_set_config(camera, config))
|
gp.check_result(gp.gp_camera_set_config(camera, config))
|
||||||
@ -576,9 +629,9 @@ class KISStopmo(tk.Tk):
|
|||||||
# ~ self.camera.trigger_capture()
|
# ~ self.camera.trigger_capture()
|
||||||
new_frame = self.camera.file_get(
|
new_frame = self.camera.file_get(
|
||||||
new_frame_path.folder, new_frame_path.name, gp.GP_FILE_TYPE_NORMAL)
|
new_frame_path.folder, new_frame_path.name, gp.GP_FILE_TYPE_NORMAL)
|
||||||
print("saving " + new_frame_path.folder + new_frame_path.name )
|
print(_("Saving {}{}").format(new_frame_path.folder, new_frame_path.name))
|
||||||
else:
|
else:
|
||||||
print("getting file {}".format(event_data.name))
|
print(_("Getting file {}").format(event_data.name))
|
||||||
new_frame = self.camera.file_get(
|
new_frame = self.camera.file_get(
|
||||||
event_data.folder, event_data.name, gp.GP_FILE_TYPE_NORMAL)
|
event_data.folder, event_data.name, gp.GP_FILE_TYPE_NORMAL)
|
||||||
new_frame.save(target_path)
|
new_frame.save(target_path)
|
||||||
@ -593,9 +646,8 @@ class KISStopmo(tk.Tk):
|
|||||||
|
|
||||||
def wait_for_capture(self):
|
def wait_for_capture(self):
|
||||||
if self.end_thread == True:
|
if self.end_thread == True:
|
||||||
print("ending thread")
|
print(_("Ending thread"))
|
||||||
return
|
return
|
||||||
# ~ print("waiting for capture")
|
|
||||||
event_type, event_data = self.camera.wait_for_event(self.timeout)
|
event_type, event_data = self.camera.wait_for_event(self.timeout)
|
||||||
if event_type == gp.GP_EVENT_FILE_ADDED:
|
if event_type == gp.GP_EVENT_FILE_ADDED:
|
||||||
# ~ print("file added")
|
# ~ print("file added")
|
||||||
@ -623,16 +675,17 @@ class KISStopmo(tk.Tk):
|
|||||||
def trigger_export_animation(self, event):
|
def trigger_export_animation(self, event):
|
||||||
output_folder = filedialog.askdirectory()
|
output_folder = filedialog.askdirectory()
|
||||||
output_folder = "{folder}{sep}{filename}".format(folder=output_folder, sep=os.sep, filename=self.output_filename)
|
output_folder = "{folder}{sep}{filename}".format(folder=output_folder, sep=os.sep, filename=self.output_filename)
|
||||||
print("exporting to " + output_folder)
|
print(_("Exporting to {}").format(output_folder))
|
||||||
self.export_task = asyncio.run(self.export_animation())
|
self.export_task = asyncio.run(self.export_animation())
|
||||||
# check with self.export_task.done() == True ? https://stackoverflow.com/questions/69350645/proper-way-to-retrieve-the-result-of-tasks-in-asyncio
|
# check with self.export_task.done() == True ? https://stackoverflow.com/questions/69350645/proper-way-to-retrieve-the-result-of-tasks-in-asyncio
|
||||||
|
|
||||||
def end_bg_loop(KISStopmo):
|
def end_bg_loop(KISStopmo):
|
||||||
|
KISStopmo.camera.exit()
|
||||||
KISStopmo.end_thread = True
|
KISStopmo.end_thread = True
|
||||||
|
|
||||||
root = tk.Tk()
|
root = tk.Tk()
|
||||||
toot = KISStopmo(root)
|
toot = KISStopmo(root)
|
||||||
# ~ root.protocol('WM_DELETE_WINDOW', end_bg_loop(toot))
|
root.protocol('WM_DELETE_WINDOW', end_bg_loop(toot))
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
|
|
||||||
|
|
||||||
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
gphoto2
|
||||||
|
pillow
|
||||||
|
python-ffmpeg
|
||||||
|
Send2Trash
|
Loading…
Reference in New Issue
Block a user