From cd5b250ff1482405fb791b3f1f3f355011774341 Mon Sep 17 00:00:00 2001 From: wyller Date: Tue, 31 May 2022 00:44:14 -0300 Subject: [PATCH] Refactored --- assets/__init__.py | 1 + assets/cfg.py | 24 ++++++ {views/_gui => assets}/icons.py | 0 controller/_msgs.py => assets/msgs.py | 0 {views/_gui => assets}/style.py | 4 + {views/_gui => assets}/txt.py | 0 controller/__init__.py | 53 +----------- controller/_config.py | 11 --- controller/application.py | 62 ++++++++++++++ controller/form.py | 66 +++++++++++++++ controller/gifer.py | 16 ++-- controller/options_form.py | 97 ---------------------- main.py | 6 +- model/__init__.py | 1 + model/options.py | 37 +++++++++ views/__init__.py | 39 +++++---- views/_gui/__init__.py | 77 ----------------- views/_ui/__init__.py | 63 ++++++++++++++ views/{_gui/partial.py => _ui/controls.py} | 49 +++++++++-- 19 files changed, 337 insertions(+), 269 deletions(-) create mode 100644 assets/__init__.py create mode 100644 assets/cfg.py rename {views/_gui => assets}/icons.py (100%) rename controller/_msgs.py => assets/msgs.py (100%) rename {views/_gui => assets}/style.py (77%) rename {views/_gui => assets}/txt.py (100%) delete mode 100644 controller/_config.py create mode 100644 controller/application.py create mode 100644 controller/form.py delete mode 100644 controller/options_form.py create mode 100644 model/__init__.py create mode 100644 model/options.py delete mode 100644 views/_gui/__init__.py create mode 100644 views/_ui/__init__.py rename views/{_gui/partial.py => _ui/controls.py} (57%) diff --git a/assets/__init__.py b/assets/__init__.py new file mode 100644 index 0000000..521617a --- /dev/null +++ b/assets/__init__.py @@ -0,0 +1 @@ +from . import cfg, icons, msgs, style, txt diff --git a/assets/cfg.py b/assets/cfg.py new file mode 100644 index 0000000..8d34c8c --- /dev/null +++ b/assets/cfg.py @@ -0,0 +1,24 @@ +NO_TIME = '00' + +DFT_SPEED = 1.0 + +MAX_DURATION = 0 +DFT_DURATION = 5.0 +LIMIT_DURATION = 59.0 + +MAX_DURATION_TXT = "WHOLE VIDEO" +DFT_DURATION_TXT = "5 Seconds" + +DONATE_LINK = "https://www.paypal.com/donate/?hosted_button_id=RNDCMNV4YWHX4" + +DISABLED_DURATION = { + 'duration': MAX_DURATION, + 'slider': LIMIT_DURATION, + 'display': MAX_DURATION_TXT +} + +ENABLED_DURATION = { + 'duration': DFT_DURATION, + 'slider': DFT_DURATION, + 'display': DFT_DURATION_TXT +} diff --git a/views/_gui/icons.py b/assets/icons.py similarity index 100% rename from views/_gui/icons.py rename to assets/icons.py diff --git a/controller/_msgs.py b/assets/msgs.py similarity index 100% rename from controller/_msgs.py rename to assets/msgs.py diff --git a/views/_gui/style.py b/assets/style.py similarity index 77% rename from views/_gui/style.py rename to assets/style.py index 593299c..ecde2d5 100644 --- a/views/_gui/style.py +++ b/assets/style.py @@ -9,6 +9,10 @@ def BTN_COLOR() -> tuple: def BG_COLOR(): return sg.theme_background_color() +def STATE_COLOR(state): + colors = 'yellow', sg.theme_element_text_color() + return colors[state] + # FONTS: ################ F_14 = "Default 14" F_14_B = "Default 14 bold" diff --git a/views/_gui/txt.py b/assets/txt.py similarity index 100% rename from views/_gui/txt.py rename to assets/txt.py diff --git a/controller/__init__.py b/controller/__init__.py index 570f169..4da4a0b 100644 --- a/controller/__init__.py +++ b/controller/__init__.py @@ -1,52 +1 @@ -import webbrowser -from os import startfile -from os.path import realpath - -from PySimpleGUI import read_all_windows, WIN_CLOSED - -from views import DONE_POPUP, INFO_POPUP -from ._config import DONATE_LINK -from .gifer import Gifer -from .options_form import OptionsForm - - -class Controller: - def __init__(self, window): - self.view = window - self.options = OptionsForm(window) - - - def read_events(self): - window, event, values = read_all_windows() - - if event == "-START_BTN-": - if not self.options.validate(): - return - window.hide() - output = Gifer.run(self.options) - if DONE_POPUP(): - startfile(realpath(output)) - window.un_hide() - return - - if event == '-TRIM_CHECK-': - self.options.update_trim_state() - - if event == '-DURATION_SLIDER-': - self.options.update_duration(values) - - if event == '-SPEED_SLIDER-': - self.options.update_speed(values) - - if event == "-INFO_BTN-": - self.show_donate() - - if event == WIN_CLOSED: - return 'done' - - - def show_donate(self): - self.view.hide() - if INFO_POPUP() == 'Yes': - webbrowser.open(DONATE_LINK, new=0) - self.view.un_hide() +from .application import Application diff --git a/controller/_config.py b/controller/_config.py deleted file mode 100644 index f31f59c..0000000 --- a/controller/_config.py +++ /dev/null @@ -1,11 +0,0 @@ -DFT_TIME = '00' - -DFT_DURATION = "00:00:05" -DFT_DURATION_VAL = 5.0 -DFT_DURATION_TXT = "5 Seconds" - -MAX_DURATION = "00:00:00" -MAX_DURATION_VAL = 59.0 -MAX_DURATION_TXT = "WHOLE VIDEO" - -DONATE_LINK = "https://www.paypal.com/donate/?hosted_button_id=RNDCMNV4YWHX4" diff --git a/controller/application.py b/controller/application.py new file mode 100644 index 0000000..ea1fbeb --- /dev/null +++ b/controller/application.py @@ -0,0 +1,62 @@ +import webbrowser +import os + +import PySimpleGUI as sg + +import model +import views +import assets +from .gifer import Gifer +from .form import Form + + +class Application: + def __init__(self, window): + self.view = window + self.form = Form(window) + + + def read_events(self): + event, values = self.view.read(timeout=10) + + if event == "-START_BTN-": + options = self.read_form() + if not options: + return + self.run_gifer(options) + + if event == '-TRIM_CHECK-': + self.form.update_trim_state() + + if event == '-DURATION_SLIDER-': + self.form.update_duration(values) + + if event == '-SPEED_SLIDER-': + self.form.update_speed(values) + + if event == "-INFO_BTN-": + self.show_donate() + + if event == sg.WIN_CLOSED: + return 'done' + + def read_form(self) -> model.Options | None: + try: + options = model.Options(self.form.data) + except ValueError as err: + views.ERROR_POPUP(str(err)) + return None + return options + + def run_gifer(self, options): + self.view.hide() + output = Gifer.run(options) + if views.DONE_POPUP(): + os.startfile(os.path.realpath(output)) + self.view.un_hide() + + def show_donate(self): + self.view.hide() + if views.INFO_POPUP() == 'Yes': + webbrowser.open(assets.cfg.DONATE_LINK, new=0) + self.view.un_hide() diff --git a/controller/form.py b/controller/form.py new file mode 100644 index 0000000..4e3ef5d --- /dev/null +++ b/controller/form.py @@ -0,0 +1,66 @@ +import PySimpleGUI as sg + +import assets + + +class Form: + def __init__(self, view): + self._set_duration = assets.cfg.MAX_DURATION + self._set_speed = assets.cfg.DFT_SPEED + self._video_input: sg.Input = view["-VIDEO_IN-"] + self._trim_check: sg.Checkbox = view["-TRIM_CHECK-"] + self._s_hour: sg.Input = view["-HOUR_IN-"] + self._s_minute: sg.Input = view["-MINUTE_IN-"] + self._s_second: sg.Input = view["-SECOND_IN-"] + self._duration_slider: sg.Slider = view["-DURATION_SLIDER-"] + self._duration_display: sg.Text = view["-DURATION_TXT-"] + self._speed_slider: sg.Slider = view["-SPEED_SLIDER-"] + self._speed_display: sg.Text = view['-SPEED_TEXT-'] + + @property + def data(self): + hour: str = self._s_hour.get() + minute: str = self._s_minute.get() + sec: str = self._s_second.get() + return { + 'input_path': self._video_input.get(), + 'start': (hour, minute, sec), + 'duration': self._set_duration, + 'gif_speed': self._set_speed + } + + def update_speed(self, values) -> None: + value = values['-SPEED_SLIDER-'] + speed_txt = f"{value}x" + self._set_speed = value + self._speed_display.update(speed_txt) + + def update_duration(self, values) -> None: + secs = int(values['-DURATION_SLIDER-']) + txt = f"{secs:02d} Second" + txt += 's' if secs != 1 else ' ' + self._set_duration = secs + self._duration_display.update(txt) + + def update_trim_state(self) -> None: + checked = self._trim_check.get() + disable = not checked + self._start_inputs(disable) + self._duration_inputs(disable) + + def _start_inputs(self, disable: bool) -> None: + color = assets.style.STATE_COLOR(disable) + inputs = [self._s_hour, self._s_minute, self._s_second] + if disable: + [i.update(assets.cfg.NO_TIME) for i in inputs] + [i.update(disabled=disable, text_color=color) for i in inputs] + + def _duration_inputs(self, disable: bool) -> None: + color = assets.style.STATE_COLOR(disable) + if disable: + values = assets.cfg.DISABLED_DURATION + else: + values = assets.cfg.ENABLED_DURATION + self._set_duration = values['duration'] + self._duration_slider.update(values['slider'], disabled=disable) + self._duration_display(values['display'], text_color=color) diff --git a/controller/gifer.py b/controller/gifer.py index fb24129..1faf40a 100644 --- a/controller/gifer.py +++ b/controller/gifer.py @@ -2,14 +2,14 @@ import ffmpeg -from views import PROGRESS_POPUP -from . import _msgs -from .options_form import OptionsForm +import views +import model +import assets class Gifer: @staticmethod - def run(options: OptionsForm) -> str: + def run(options: model.Options) -> str: with ThreadPoolExecutor() as e: task = e.submit(Gifer._convert, options) SHOW_TASK_PROGRESS(task) @@ -17,11 +17,11 @@ def run(options: OptionsForm) -> str: return output @staticmethod - def _convert(options: OptionsForm): + def _convert(options: model.Options): output_file = options.output_path stream = ffmpeg.input(options.input_path) if options.duration != '00:00:00': - trim_args = options.start_at, options.duration + trim_args = options.start_time, options.duration stream = Gifer._trim(trim_args, stream) stream = Gifer._set_speed(options.gif_speed, stream) Gifer._make_file(output_file, stream) @@ -43,12 +43,12 @@ def _make_file(output_file, stream): def SHOW_TASK_PROGRESS(task: Future[str]): bar_end, reload_i, i = 100, 99, 0 - view = PROGRESS_POPUP(bar_end=bar_end) + view = views.PROGRESS_POPUP(bar_end=bar_end) while task.running(): view.read(timeout=10) if i == reload_i: i = 0 - msg = _msgs.SLOW_EXPORTING() + msg = assets.msgs.SLOW_EXPORTING() view['-TXT-'].update(msg) view['-PROG-'].update(current_count=(i + 1)) i += 1 diff --git a/controller/options_form.py b/controller/options_form.py deleted file mode 100644 index a76d54f..0000000 --- a/controller/options_form.py +++ /dev/null @@ -1,97 +0,0 @@ -from os.path import isfile, splitext - -import PySimpleGUI as sg - -from views import ERROR_POPUP -from ._config import ( - DFT_DURATION, DFT_DURATION_VAL, DFT_DURATION_TXT, - MAX_DURATION, MAX_DURATION_VAL, MAX_DURATION_TXT, - DFT_TIME -) -from ._msgs import ( - INVALID_FILE, INVALID_TRIM, INVALID_TIME -) - - -class OptionsForm: - def __init__(self, view): - self.duration = 0 - self.gif_speed = 0.25 - self._video_input = view["-VIDEO_IN-"] - self._trim_check = view["-TRIM_CHECK-"] - self._s_hour = view["-HOUR_IN-"] - self._s_minute = view["-MINUTE_IN-"] - self._s_second = view["-SECOND_IN-"] - self._duration_slider = view["-DURATION_SLIDER-"] - self._duration_display = view["-DURATION_TXT-"] - self._speed_slider = view["-SPEED_SLIDER-"] - self._speed_display = view['-SPEED_TEXT-'] - - @property - def input_path(self) -> str: - return self._video_input.get() - - @property - def output_path(self) -> str: - return f'{splitext(self.input_path)[0]}.gif' - - @property - def start_at(self) -> str: - hour = int(self._s_hour.get()) - minute = int(self._s_minute.get()) - sec = int(self._s_second.get()) - return f"{hour:02d}:{minute:02d}:{sec:02d}" - - def update_speed(self, values) -> None: - speed = values['-SPEED_SLIDER-'] - speed_txt = f"{speed}x" - self.gif_speed = round(0.25 / speed, 2) - self._speed_display.update(speed_txt) - - def update_duration(self, values) -> None: - secs = int(values['-DURATION_SLIDER-']) - txt = f"{secs:02d} Second{'s' if secs != 1 else ' '}" - self.duration = f"00:00:{secs:02d}" - self._duration_display.update(txt) - - def update_trim_state(self) -> None: - state = self._trim_check.get() - colors = (sg.theme_element_text_color(), 'yellow') - color = colors[state] - inputs = [self._s_hour, self._s_minute, self._s_second] - if state: - [inp.update(disabled=False, text_color=color) for inp in inputs] - self._enable_duration(color) - else: - [inp.update(DFT_TIME, disabled=True, text_color=color) for inp in inputs] - self._reset_duration(color) - - def validate(self) -> bool: - input_path = self.input_path - if not (input_path and isfile(input_path)): - ERROR_POPUP(INVALID_FILE) - return False - if not self._trim_check.get(): - return True - t_inputs = [self._s_hour, self._s_minute, self._s_second] - try: - [int(t_input.get()) for t_input in t_inputs] - except ValueError: - ERROR_POPUP(INVALID_TRIM) - return False - start_at = self.start_at - if len(start_at) != 8: - ERROR_POPUP(INVALID_TIME(start_at)) - return False - else: - return True - - def _enable_duration(self, color: str): - self.duration = DFT_DURATION - self._duration_slider.update(DFT_DURATION_VAL, disabled=False) - self._duration_display(DFT_DURATION_TXT, text_color=color) - - def _reset_duration(self, color: str): - self.duration = MAX_DURATION - self._duration_slider.update(MAX_DURATION_VAL, disabled=True) - self._duration_display(MAX_DURATION_TXT, text_color=color) diff --git a/main.py b/main.py index 37a7248..1a0a167 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,12 @@ -from controller import Controller +from controller import Application from views import MAIN_WINDOW -def main(app: Controller): +def main(app: Application): while True: status = app.read_events() if status == 'done': break if __name__ == "__main__": - main(Controller(MAIN_WINDOW())) + main(Application(MAIN_WINDOW())) diff --git a/model/__init__.py b/model/__init__.py new file mode 100644 index 0000000..988739d --- /dev/null +++ b/model/__init__.py @@ -0,0 +1 @@ +from .options import Options diff --git a/model/options.py b/model/options.py new file mode 100644 index 0000000..166aafd --- /dev/null +++ b/model/options.py @@ -0,0 +1,37 @@ +import os + +import assets + +class Options: + def __init__(self, form: dict): + self.input_path = _parse_file(form) + self.start_time = _parse_start_time(form) + self.duration = _parse_duration(form) + self.gif_speed = _parse_speed(form) + self.output_path = _out_from_in(self.input_path) + + +def _parse_start_time(form) -> str: + try: + hh, mm, ss = (int(val) for val in form['start']) + except ValueError: + raise ValueError(assets.msgs.INVALID_TRIM) + return f"{hh:02d}:{mm:02d}:{ss:02d}" + +def _parse_file(form) -> str: + file = form['input_path'] + if not (file and os.path.isfile(file)): + raise ValueError(assets.msgs.INVALID_FILE) + return file + +def _parse_speed(form) -> float: + spd = form['gif_speed'] + return round(0.25 / spd, 2) + +def _parse_duration(form) -> str: + ss = form['duration'] + return f"00:00:{ss:02d}" + +def _out_from_in(file) -> str: + out_path = f'{os.path.splitext(file)[0]}.gif' + return out_path diff --git a/views/__init__.py b/views/__init__.py index 7cb5411..409da8d 100644 --- a/views/__init__.py +++ b/views/__init__.py @@ -1,26 +1,24 @@ import PySimpleGUI as sg -from ._gui import ( - MAIN_TEXT, MAIN_CONTROLS, TRIM_FRAME, - SPEED_FRAME, INFO_BTN, txt, style -) +from . import _ui +import assets def MAIN_WINDOW(): return sg.Window( - txt.APP_TITLE, + assets.txt.APP_TITLE, [ - [MAIN_TEXT()], - [*MAIN_CONTROLS()], - [TRIM_FRAME()], - [SPEED_FRAME(), INFO_BTN()] + [_ui.HEADING()], + [*_ui.VIDEO_BROWSER()], + [_ui.TRIM_FRAME()], + [_ui.SPEED_FRAME(), _ui.INFO()] ], finalize=True ) def PROGRESS_POPUP(bar_end=100): layout = [ - [sg.Text(txt.EXPORTING, key='-TXT-', font='Default 12 bold')], + [sg.T(assets.txt.EXPORTING, key='-TXT-', font='Default 12 bold')], [sg.ProgressBar( bar_end, orientation='h', @@ -29,17 +27,30 @@ def PROGRESS_POPUP(bar_end=100): bar_color='#ff009b' )] ] - return sg.Window(txt.EXPORTING, layout, keep_on_top=True) + return sg.Window(assets.txt.EXPORTING, layout, keep_on_top=True) def INFO_POPUP(): - return sg.popup_yes_no(txt.INFO, font=style.F_14, no_titlebar=True, keep_on_top=True) + return sg.popup_yes_no( + assets.txt.INFO, + font=assets.style.F_14, + no_titlebar=True, + keep_on_top=True + ) def DONE_POPUP(): - return sg.popup_ok(txt.DONE, font=style.F_14, no_titlebar=True, keep_on_top=True) + return sg.popup_ok( + assets.txt.DONE, + font=assets.style.F_14, + no_titlebar=True, + keep_on_top=True) def ERROR_POPUP(msg: str): msg = f"\n{msg}\n" - return sg.popup_error(msg, font=style.F_14, keep_on_top=True) + return sg.popup_error( + msg, + font=assets.style.F_14, + keep_on_top=True + ) diff --git a/views/_gui/__init__.py b/views/_gui/__init__.py deleted file mode 100644 index e505152..0000000 --- a/views/_gui/__init__.py +++ /dev/null @@ -1,77 +0,0 @@ -import PySimpleGUI as sg - -import views._gui.style -from . import txt, icons, partial, style - - -def MAIN_TEXT() -> sg.Text: - pad = (10, 10), (20, 10) - return sg.T(txt.SELECT_VIDEO, font=style.F_14_B, p=pad) - - -def MAIN_CONTROLS() -> tuple: - BROWSE_BTN = sg.Button( - button_type=sg.BUTTON_TYPE_BROWSE_FILE, - image_data=icons.FOLDER(), - button_color=style.BTN_COLOR(), - border_width=0, - target='-VIDEO_IN-', - tooltip=txt.BROWSE_TOOLTIP - ) - INPUT_VIDEO = sg.Input( - k='-VIDEO_IN-', - size=(30, 4), - expand_x=True, - font=style.F_14 - ) - START_BTN = sg.Button( - button_type=sg.BUTTON_TYPE_READ_FORM, - image_data=icons.START(), - button_color=style.BTN_COLOR(), - border_width=0, - tooltip=txt.START_TOOLTIP, - key="-START_BTN-" - ) - return BROWSE_BTN, INPUT_VIDEO, START_BTN - - -def TRIM_FRAME() -> sg.Frame: - sep = sg.HSep(p=(5, 15), color=sg.theme_background_color()) - layout = [ - [sg.VPush()], - [*partial.TRIM_START_AT(), sg.Push()], - [sep], - [*partial.TRIM_DURATION_SLIDER()], - [sg.VPush()], - ] - return sg.Frame( - title=txt.TRIM, - layout=layout, - pad=(0, 15), - relief=sg.RELIEF_RAISED, - expand_x=True, - element_justification='left', - font=style.F_11_B - ) - - -def SPEED_FRAME() -> sg.Frame: - return sg.Frame( - title='Change speed?', - layout=[partial.SPEED_SLIDER()], - expand_x=True, - expand_y=True, - font=style.F_11_B, - relief=sg.RELIEF_RAISED, - vertical_alignment='top' - ) - - -def INFO_BTN() -> sg.Button: - return sg.Button( - image_data=icons.INFO(), - button_color=style.BTN_COLOR(), - border_width=0, - key="-INFO_BTN-", - enable_events=True - ) diff --git a/views/_ui/__init__.py b/views/_ui/__init__.py new file mode 100644 index 0000000..25e2b34 --- /dev/null +++ b/views/_ui/__init__.py @@ -0,0 +1,63 @@ +import PySimpleGUI as sg + +from . import controls +from assets import txt, icons, style + + +def HEADING() -> sg.Text: + pad = (10, 10), (20, 10) + return sg.Text( + txt.SELECT_VIDEO, + font=style.F_14_B, + p=pad + ) + + +def VIDEO_BROWSER() -> tuple: + return ( + controls.BROWSE_BTN(), + controls.VIDEO_INPUT(), + controls.START_BTN() + ) + + +def TRIM_FRAME() -> sg.Frame: + sep = sg.HSep(pad=(5, 15), color=style.BG_COLOR()) + layout = [ + [sg.VPush()], + [*controls.TRIM_START_AT(), sg.Push()], + [sep], + [*controls.TRIM_DURATION_SLIDER()], + [sg.VPush()], + ] + return sg.Frame( + title=txt.TRIM, + layout=layout, + pad=(0, 15), + relief=sg.RELIEF_RAISED, + expand_x=True, + element_justification='left', + font=style.F_11_B + ) + + +def SPEED_FRAME() -> sg.Frame: + return sg.Frame( + title='Change speed?', + layout=[controls.SPEED_SLIDER()], + expand_x=True, + expand_y=True, + font=style.F_11_B, + relief=sg.RELIEF_RAISED, + vertical_alignment='top' + ) + + +def INFO() -> sg.Button: + return sg.Button( + image_data=icons.INFO(), + button_color=style.BTN_COLOR(), + border_width=0, + key="-INFO_BTN-", + enable_events=True + ) diff --git a/views/_gui/partial.py b/views/_ui/controls.py similarity index 57% rename from views/_gui/partial.py rename to views/_ui/controls.py index c8a9aae..dada167 100644 --- a/views/_gui/partial.py +++ b/views/_ui/controls.py @@ -1,9 +1,40 @@ import PySimpleGUI as sg -from . import txt, style +from assets import txt, style, icons -def TRIM_TIME_INPUT(mode: str) -> tuple: +def BROWSE_BTN() -> sg.Button: + return sg.Button( + button_type=sg.BUTTON_TYPE_BROWSE_FILE, + image_data=icons.FOLDER(), + button_color=style.BTN_COLOR(), + border_width=0, + target='-VIDEO_IN-', + tooltip=txt.BROWSE_TOOLTIP + ) + + +def VIDEO_INPUT() -> sg.Input: + return sg.Input( + k='-VIDEO_IN-', + size=(30, 4), + expand_x=True, + font=style.F_14 + ) + + +def START_BTN() -> sg.Button: + return sg.Button( + button_type=sg.BUTTON_TYPE_READ_FORM, + image_data=icons.START(), + button_color=style.BTN_COLOR(), + border_width=0, + tooltip=txt.START_TOOLTIP, + key="-START_BTN-" + ) + + +def _time_input(mode: str) -> tuple: time_input = sg.Input( k=f'-{mode.upper()}_IN-', size=(3, 4), @@ -13,7 +44,7 @@ def TRIM_TIME_INPUT(mode: str) -> tuple: justification='center', disabled_readonly_background_color=style.BG_COLOR(), ) - input_display = sg.T(f'{mode}', font=style.F_9_B) + input_display = sg.Text(f'{mode}', font=style.F_9_B) return time_input, input_display @@ -25,9 +56,9 @@ def TRIM_START_AT() -> tuple: key='-TRIM_CHECK-', enable_events=True ) - hour = TRIM_TIME_INPUT('hour') - minute = TRIM_TIME_INPUT('minute') - second = TRIM_TIME_INPUT('second') + hour = _time_input('hour') + minute = _time_input('minute') + second = _time_input('second') return check, sg.P(), *hour, sg.P(), *minute, sg.P(), *second @@ -50,7 +81,11 @@ def TRIM_DURATION_SLIDER(): def SPEED_SLIDER() -> list: - display = sg.T(txt.SPEED_DISPLAY, font=style.F_11_B, key='-SPEED_TEXT-') + display = sg.Text( + txt.SPEED_DISPLAY, + font=style.F_11_B, + key='-SPEED_TEXT-' + ) slider = sg.Slider( range=(0.1, 3), default_value=1,