From 3d416caf9479dc0ac36476777e50ad387591a957 Mon Sep 17 00:00:00 2001 From: Matteo Percivaldi Date: Mon, 19 Dec 2022 08:44:26 +0100 Subject: [PATCH 01/14] feat: Separate algorithm from window to support multiple backends --- upscaler/algorithms/commons.py | 92 ++++++++++ upscaler/algorithms/realesrgan_ncnn_vulkan.py | 81 +++++++++ upscaler/gtk/window.blp | 10 +- upscaler/meson.build | 6 + upscaler/window.py | 172 ++++++------------ 5 files changed, 240 insertions(+), 121 deletions(-) create mode 100644 upscaler/algorithms/commons.py create mode 100644 upscaler/algorithms/realesrgan_ncnn_vulkan.py diff --git a/upscaler/algorithms/commons.py b/upscaler/algorithms/commons.py new file mode 100644 index 0000000..c351713 --- /dev/null +++ b/upscaler/algorithms/commons.py @@ -0,0 +1,92 @@ +from gi.repository import Gtk, Adw + +""" Raised when the algorithm has failed. """ +class AlgorithmFailed(Exception): + def __init__(self, result_code, output): + super().__init__() + self.result_code = result_code + self.output = output + + def __str__(self): + return f'Algorithm failed.\nResult code: {self.result_code}\nOutput: {self.output}' + +""" Raised when the output could be damaged. """ +class AlgorithmWarning(Exception): + pass + +""" Option base class. """ +class __Option: + def __init__(self, name, default_value, desc): + self.name = name + self.default_value = default_value + self.value = default_value + self.desc = desc + + def widget(self, **_): + raise NotImplementedError() + + def reset(self): + self.value = self.default_value + +""" String enum option. """ +class EnumOption(__Option): + def __init__(self, name, enum, desc): + super().__init__(name, enum, desc) + + self.Enum = type(enum) + + self.strings = [e.value for e in self.Enum] + string_list = Gtk.StringList.new(self.strings) + + self.row = Adw.ComboRow() + self.row.set_title(self.desc) + self.row.set_model(string_list) + self.row.connect('notify::selected', self.__on_row_selected) + + def widget(self): + return self.row + + def __on_row_selected(self, row, *_): + self.value = self.Enum(self.strings[row.get_selected()]) + + def reset(self): + super().reset() + self.row.set_selected(self.strings.index(self.default_value.value)) + +""" Algorithm base class. """ +class Algorithm: + def __init__(self, options): + self.cancelled = False + self.__options = options + + def __del__(self): + """ Cancel operation on destruction. """ + self.cancel() + + """ Get the list of all the options. """ + def get_options(self): + return self.__options + + """ Get the value of the option with 'name'. """ + def get_option(self, name): + for opt in self.__options: + if opt.name == name: + return opt.value + raise ValueError(f'Unable to find {name} option') + + """ Reset all the options to the default value. """ + def reset_options(self): + for opt in self.__options: + opt.reset() + + """ Starts the algorithm. """ + def start(self, + input_file_path, + output_file_path, + progress_callback, + done_callback): + raise NotImplementedError() + + """ Cancel the current operation. """ + def cancel(self): + raise NotImplementedError() diff --git a/upscaler/algorithms/realesrgan_ncnn_vulkan.py b/upscaler/algorithms/realesrgan_ncnn_vulkan.py new file mode 100644 index 0000000..a88593b --- /dev/null +++ b/upscaler/algorithms/realesrgan_ncnn_vulkan.py @@ -0,0 +1,81 @@ +from enum import Enum +import re +import subprocess +from gi.repository import GLib +from upscaler.algorithms.commons import (Algorithm, + EnumOption, + AlgorithmFailed, + AlgorithmWarning) +from upscaler.threading import RunAsync + +ALG_WARNINGS = [ + 'vkQueueSubmit failed' +] + +class ImageTypeEnum(Enum): + PHOTO = _("Photo") + CARTOON = _("Cartoon/Anime") + +MODEL_IMAGES = { + ImageTypeEnum.PHOTO: 'realesrgan-x4plus', + ImageTypeEnum.CARTOON: 'realesrgan-x4plus-anime', +} + +class RealesrganNcnnVulkan(Algorithm): + def __init__(self): + super().__init__([ + EnumOption('model', ImageTypeEnum.PHOTO, 'Type of Image') + ]) + + def start(self, + input_file_path, + output_file_path, + progress_callback, + done_callback): + self.cancelled = False + model = MODEL_IMAGES[self.get_option('model')] + + def run(): + command = ['realesrgan-ncnn-vulkan', + '-i', input_file_path, + '-o', output_file_path, + '-n', model, + '-s', '4'] + self.process = subprocess.Popen(command, + stderr=subprocess.PIPE, + universal_newlines=True) + print('Running: ', end='') + print(*command) + + """ Read each line, query the percentage and update the progress bar. """ + output = "" + bad = False + for line in iter(self.process.stderr.readline, ''): + print(line, end='') + output += line + res = re.match('^(\d*.\d+)%$', line) + if res: + GLib.idle_add(progress_callback, float(res.group(1))) + else: + """ Check if this line is a warning. """ + if bad: continue + for warn in ALG_WARNINGS: + if re.match(warn, line) is not None: + bad = True + continue + + """ Process algorithm output. """ + if self.cancelled: + return + result = self.process.poll() + if result != 0: + raise AlgorithmFailed(result, output) + if bad: + raise AlgorithmWarning + + RunAsync(run, done_callback) + + def cancel(self): + if self.process: + self.cancelled = True + self.process.kill() diff --git a/upscaler/gtk/window.blp b/upscaler/gtk/window.blp index 6535a13..f26c1f2 100644 --- a/upscaler/gtk/window.blp +++ b/upscaler/gtk/window.blp @@ -114,7 +114,7 @@ template UpscalerWindow : Adw.ApplicationWindow { } } - Adw.PreferencesGroup { + Adw.PreferencesGroup options { title: _("Options"); // Scaling is broken in Real-ESRGAN with values other than 4. @@ -138,14 +138,6 @@ template UpscalerWindow : Adw.ApplicationWindow { // } // } - Adw.ComboRow combo_models { - title: _("Type of Image"); - model: - StringList string_models {} - - ; - } - Adw.ActionRow { title: _("Save Location"); activatable-widget: button_output; diff --git a/upscaler/meson.build b/upscaler/meson.build index 096c57f..25bad00 100644 --- a/upscaler/meson.build +++ b/upscaler/meson.build @@ -51,4 +51,10 @@ upscaler_sources = [ 'app_profile.py', ] +algorithms_sources = [ + 'algorithms/commons.py', + 'algorithms/realesrgan_ncnn_vulkan.py', +] + install_data(upscaler_sources, install_dir: moduledir) +install_data(algorithms_sources, install_dir: join_paths(moduledir, 'algorithms')) diff --git a/upscaler/window.py b/upscaler/window.py index 3731205..5de1f8f 100644 --- a/upscaler/window.py +++ b/upscaler/window.py @@ -17,37 +17,18 @@ # SPDX-License-Identifier: GPL-3.0-only from os.path import basename, splitext -import subprocess -import re -from gi.repository import Adw, Gtk, GLib, Gdk, Gio, Pango, GdkPixbuf +from gi.repository import Adw, Gtk, GLib, Gdk, Gio, Pango from sys import exit -from upscaler.threading import RunAsync +from upscaler.algorithms.commons import AlgorithmWarning +from upscaler.algorithms.realesrgan_ncnn_vulkan import RealesrganNcnnVulkan from upscaler.file_chooser import FileChooser from filecmp import cmp import vulkan # type: ignore from typing import Any, Callable, Optional, Literal from gettext import gettext as _ -ALG_WARNINGS = [ - 'vkQueueSubmit failed' -] - UPSCALE_FACTOR = 4 -class AlgorithmFailed(Exception): - """Raise when the algorithm has failed.""" - def __init__(self, result_code: int, output: str) -> None: - super().__init__() - self.result_code = result_code - self.output = output - - def __str__(self) -> str: - return f'Algorithm failed.\nResult code: {self.result_code}\nOutput: {self.output}' - -class AlgorithmWarning(Exception): - """Raise when the output could be damaged.""" - pass - @Gtk.Template(resource_path='/io/gitlab/theevilskeleton/Upscaler/gtk/window.ui') class UpscalerWindow(Adw.ApplicationWindow): __gtype_name__ = 'UpscalerWindow' @@ -61,10 +42,9 @@ class UpscalerWindow(Adw.ApplicationWindow): button_upscale: Gtk.Button = Gtk.Template.Child() # type: ignore spinner_loading: Gtk.Spinner = Gtk.Template.Child() # type: ignore image: Gtk.Picture = Gtk.Template.Child() # type: ignore - # video = Gtk.Template.Child() # type: ignore - combo_models: Adw.ComboRow = Gtk.Template.Child() # type: ignore - string_models: Gtk.StringList = Gtk.Template.Child() # type: ignore - # spin_scale = Gtk.Template.Child() # type: ignore + # video = Gtk.Template.Child() + # spin_scale = Gtk.Template.Child() + options = Gtk.Template.Child() button_output: Gtk.Button = Gtk.Template.Child() # type: ignore label_output: Gtk.Label = Gtk.Template.Child() # type: ignore button_cancel: Gtk.Button = Gtk.Template.Child() # type: ignore @@ -89,19 +69,18 @@ class UpscalerWindow(Adw.ApplicationWindow): 'realesrgan-x4plus-anime': _('Cartoon/Anime'), } - self.process: Optional[subprocess.Popen[Any]] = None self.output_file_path: Optional[str] = None self.input_file_path: Optional[str] = None content = Gdk.ContentFormats.new_for_gtype(Gio.File) self.target = Gtk.DropTarget(formats=content, actions=Gdk.DragAction.COPY) - self.string_models.splice(0, 0, list(self.model_images.values())) self.previous_stack = 'stack_welcome_page' + self.algorithm = None + self.__set_algorithm(RealesrganNcnnVulkan) # Connect signals self.button_input.connect('clicked', self.open_file) self.button_upscale.connect('clicked', self.__upscale) self.button_output.connect('clicked', self.__output_location) - self.combo_models.connect('notify::selected', self.__set_model) self.button_cancel.connect('clicked', self.__cancel) self.target.connect('drop', self.__on_drop) self.target.connect('enter', self.__on_enter) @@ -128,10 +107,10 @@ class UpscalerWindow(Adw.ApplicationWindow): self.label_output.set_label(_('(None)')) self.button_upscale.set_sensitive(False) self.button_upscale.set_has_tooltip(True) - self.combo_models.set_selected(0) self.stack_upscaler.set_visible_child_name('stack_upscale') self.previous_stack = 'stack_upscale' self.spinner_loading.stop() + self.algorithm.reset_options() def __on_file_open_error(self, error: GLib.Error, file_path: str) -> None: """Display error if the format is incompatible.""" @@ -143,6 +122,18 @@ class UpscalerWindow(Adw.ApplicationWindow): """Open the file chooser to load the file.""" FileChooser.open_file(self) + def __set_algorithm(self, Algorithm): + """ Build algorithm and options. """ + if self.algorithm: + # Cleanup algorithm widgets. + for opt in self.algorithm.get_options(): + self.options.remove(opt.widget()) + + self.algorithm = Algorithm() + options = self.algorithm.get_options() + for opt in options: + self.options.add(opt.widget()) + def __output_location(self, *args: Any) -> None: """ Select output file location. @@ -191,86 +182,63 @@ class UpscalerWindow(Adw.ApplicationWindow): def __upscale_progress(self, progress: float) -> None: """Updates upscale progress.""" if self.stack_upscaler.get_visible_child_name() == 'stack_upscaling': - self.set_progress(progress) + self.progressbar.set_text(str(progress) + " %") + self.progressbar.set_fraction(progress / 100) def __upscale(self, *args: Any) -> None: - """Initialize algorithm and updates widgets.""" - # Since GTK is not thread safe, prepare some data in the main thread - self.cancelled = False - # Appropriately close child windows def reset_widgets() -> None: self.button_upscale.set_sensitive(True) self.progressbar.set_text(_('Loading…')) self.progressbar.set_fraction(0) - self.cancelled = False - - # Run in a separate thread - def run() -> None: - if self.input_file_path is None or self.output_file_path is None: - raise AlgorithmFailed(0, _("Unexpected error while running the algorithm")) - - command: list[str] = ['realesrgan-ncnn-vulkan', - '-i', self.input_file_path, - '-o', self.output_file_path, - '-n', list(self.model_images)[self.combo_models.get_selected()], - '-s', '4', - ] - - self.process = subprocess.Popen(command, stderr=subprocess.PIPE, universal_newlines=True) - print('Running: ', end='') - print(*command) - - # Read each line, query the percentage and update the progress bar - output = '' - bad = False - if self.process.stderr is not None: - for line in iter(self.process.stderr.readline, ''): - print(line, end='') - output += line - res = re.match('^(\d*.\d+)%$', line) - if res: - GLib.idle_add(self.__upscale_progress, float(res.group(1))) - else: - # Check if this line is a warning - if bad: continue - for warn in ALG_WARNINGS: - if re.match(warn, line) is not None: - bad = True - continue - - # Process algorithm output - result = self.process.poll() - if result != 0 or result is None: - if result is None: - result = 0; - raise AlgorithmFailed(result, output) - - if bad: - raise AlgorithmWarning - - # Run after run() function finishes - def callback(result: Gio.AsyncResult, error: Optional[Exception]) -> None: - if self.cancelled == True: + + # Run after algorithm finishes. + def callback(result, error): + if self.algorithm.cancelled: self.toast.add_toast(Adw.Toast.new(_('Upscaling Cancelled'))) else: - self.upscaling_completed_dialog(error) + self.__upscaling_completed_dialog(error) self.stack_upscaler.set_visible_child_name('stack_upscale') self.previous_stack = 'stack_upscale' reset_widgets() # Run functions asynchronously - RunAsync(run, callback) + self.algorithm.start(self.input_file_path, + self.output_file_path, + self.__upscale_progress, + callback) self.stack_upscaler.set_visible_child_name('stack_upscaling') self.previous_stack = 'stack_upscaling' self.button_upscale.set_sensitive(False) - def upscaling_completed_dialog(self, error: Optional[Exception]) -> None: + def __upscaling_completed_dialog(self, error: Optional[Exception]) -> None: """Ask the user if they want to open the file.""" if self.output_file_path is None: return + def response(_widget): + file = open(self.output_file_path, 'r') + fid = file.fileno() + connection = Gio.bus_get_sync(Gio.BusType.SESSION, None) + proxy = Gio.DBusProxy.new_sync(connection, + Gio.DBusProxyFlags.NONE, + None, + 'org.freedesktop.portal.Desktop', + '/org/freedesktop/portal/desktop', + 'org.freedesktop.portal.OpenURI', + None) + + try: + proxy.call_with_unix_fd_list_sync('OpenFile', + GLib.Variant('(sha{sv})', ('', 0, {'ask': GLib.Variant('b', True)})), + Gio.DBusCallFlags.NONE, + -1, + Gio.UnixFDList.new_from_array([fid]), + None) + except Exception as e: + print(f'Error: {e}') + toast = None notification = Gio.Notification() @@ -354,24 +322,7 @@ class UpscalerWindow(Adw.ApplicationWindow): if not self.props.is_active: self.app.send_notification('upscaling-done', notification) - def __set_model(self, *args: Any) -> None: - """Set model and print.""" - print(_('Model name: {}').format(list(self.model_images)[self.combo_models.get_selected()])) - - # Update post-upscale image size as the user adjusts the spinner - # def __update_post_upscale_image_size(self, *args): - # upscale_image_size = [ - # self.image_size[1] * int(self.spin_scale.get_value()), - # self.image_size[2] * int(self.spin_scale.get_value()), - # ] - # self.action_upscale_image_size.set_subtitle(f'{upscale_image_size[0]} × {upscale_image_size[1]}') - - def set_progress(self, progress: float) -> None: - """Update progress widget.""" - self.progressbar.set_text(str(progress) + " %") - self.progressbar.set_fraction(progress / 100) - - def close_dialog(self, function: Callable[[], None]) -> None: + def __close_dialog(self, function: Callable[[], None]) -> None: """Prompt the user to stop the algorithm when it is running.""" self.stop_upscaling_dialog = Adw.MessageDialog.new( self, @@ -394,12 +345,9 @@ class UpscalerWindow(Adw.ApplicationWindow): self.spinner_loading.start() def __cancel(self, *args: Any) -> None: - """Stop algorithm.""" - def function() -> None: - self.cancelled = True - if self.process: - self.process.kill() - self.close_dialog(function) + def function(): + self.algorithm.cancel() + self.__close_dialog(function) def __load_image_done(self, _obj: Any, result: Gio.AsyncResult, input_file_path: str) -> None: """Attempt to load image.""" @@ -465,6 +413,6 @@ class UpscalerWindow(Adw.ApplicationWindow): if self.stack_upscaler.get_visible_child_name() == 'stack_upscaling': def function() -> None: exit() - self.close_dialog(function) + self.__close_dialog(function) return True return False -- GitLab From b20ab9271a6fcdadb59aa0ed299144fd410af279 Mon Sep 17 00:00:00 2001 From: Matteo Percivaldi Date: Mon, 12 Dec 2022 08:16:14 +0100 Subject: [PATCH 02/14] commons, realesrgan_ncnn_vulkan: add copyright headers --- upscaler/algorithms/commons.py | 18 ++++++++++++++++++ upscaler/algorithms/realesrgan_ncnn_vulkan.py | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/upscaler/algorithms/commons.py b/upscaler/algorithms/commons.py index c351713..68c2466 100644 --- a/upscaler/algorithms/commons.py +++ b/upscaler/algorithms/commons.py @@ -1,3 +1,21 @@ +# commons.py: common data structures for algorithms +# +# Copyright (C) 2022 Upscaler Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# SPDX-License-Identifier: GPL-3.0-only + from gi.repository import Gtk, Adw """ Raised when the algorithm has failed. """ diff --git a/upscaler/algorithms/realesrgan_ncnn_vulkan.py b/upscaler/algorithms/realesrgan_ncnn_vulkan.py index a88593b..d13bf32 100644 --- a/upscaler/algorithms/realesrgan_ncnn_vulkan.py +++ b/upscaler/algorithms/realesrgan_ncnn_vulkan.py @@ -1,3 +1,21 @@ +# realesrgan_ncnn_vulkan.py: realesrgan_ncnn_vulkan implementation +# +# Copyright (C) 2022 Upscaler Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# SPDX-License-Identifier: GPL-3.0-only + from enum import Enum import re import subprocess -- GitLab From c45c27c9468cf88150bfafaf55eacf85b2d739b3 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Tue, 13 Dec 2022 07:17:46 +0000 Subject: [PATCH 03/14] Apply 1 suggestion(s) to 1 file(s) --- upscaler/algorithms/realesrgan_ncnn_vulkan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upscaler/algorithms/realesrgan_ncnn_vulkan.py b/upscaler/algorithms/realesrgan_ncnn_vulkan.py index d13bf32..b7ad03f 100644 --- a/upscaler/algorithms/realesrgan_ncnn_vulkan.py +++ b/upscaler/algorithms/realesrgan_ncnn_vulkan.py @@ -1,4 +1,4 @@ -# realesrgan_ncnn_vulkan.py: realesrgan_ncnn_vulkan implementation +# realesrgan_ncnn_vulkan.py: Real-ESRGAN ncnn Vulkan implementation # # Copyright (C) 2022 Upscaler Contributors # -- GitLab From 02f25dce7474f8f1049532652900d1bf0ca75a18 Mon Sep 17 00:00:00 2001 From: Matteo Percivaldi Date: Mon, 19 Dec 2022 09:36:43 +0100 Subject: [PATCH 04/14] feat: Dynamic properties and cleanup --- upscaler/algorithms/commons.py | 86 ++++++++++++++----- upscaler/algorithms/realesrgan_ncnn_vulkan.py | 41 +++++++-- upscaler/gtk/window.blp | 18 +--- upscaler/window.py | 30 +++---- 4 files changed, 115 insertions(+), 60 deletions(-) diff --git a/upscaler/algorithms/commons.py b/upscaler/algorithms/commons.py index 68c2466..b911f51 100644 --- a/upscaler/algorithms/commons.py +++ b/upscaler/algorithms/commons.py @@ -32,55 +32,94 @@ class AlgorithmFailed(Exception): class AlgorithmWarning(Exception): pass +class Prop: + def __init__(self, name, title, subtitle, tooltip): + self.name = name + self.__widget = Adw.ActionRow(title=title, + subtitle=subtitle, + tooltip_text=tooltip) + self.__widget.add_css_class('property') + + def set_title(self, title): + self.__widget.set_title(title) + + def set_subtitle(self, subtitle): + self.__widget.set_subtitle(subtitle) + + def widget(self, **_): + return self.__widget + """ Option base class. """ class __Option: - def __init__(self, name, default_value, desc): + def __init__(self, name, default_value, desc, on_changed): self.name = name - self.default_value = default_value - self.value = default_value - self.desc = desc + self.__default_value = default_value + self.__value = default_value + self.__desc = desc + self.__on_changed = on_changed def widget(self, **_): raise NotImplementedError() + def get_value(self): + return self.__value + + def set_value(self, value): + self.__value = value + if self.__on_changed is not None: + self.__on_changed() + def reset(self): - self.value = self.default_value + self.set_value(self.__default_value) """ String enum option. """ class EnumOption(__Option): - def __init__(self, name, enum, desc): - super().__init__(name, enum, desc) + def __init__(self, name, enum, desc, on_changed): + super().__init__(name, enum, desc, on_changed) self.Enum = type(enum) - self.strings = [e.value for e in self.Enum] - string_list = Gtk.StringList.new(self.strings) + self.__strings = [e.value for e in self.Enum] + string_list = Gtk.StringList.new(self.__strings) - self.row = Adw.ComboRow() - self.row.set_title(self.desc) - self.row.set_model(string_list) - self.row.connect('notify::selected', self.__on_row_selected) + self.__row = Adw.ComboRow(title=desc, + model=string_list) + self.__row.connect('notify::selected', self.__on_row_selected) def widget(self): - return self.row + return self.__row def __on_row_selected(self, row, *_): - self.value = self.Enum(self.strings[row.get_selected()]) + self.set_value(self.Enum(self.__strings[row.get_selected()])) def reset(self): super().reset() - self.row.set_selected(self.strings.index(self.default_value.value)) + self.__row.set_selected(self.__strings.index(self.get_value().value)) """ Algorithm base class. """ class Algorithm: - def __init__(self, options): + def __init__(self, props, options): + self.input_file_path = None + self.output_file_path = None self.cancelled = False + self.__props = props self.__options = options def __del__(self): """ Cancel operation on destruction. """ self.cancel() + """ Get the list of all props. """ + def get_props(self): + return self.__props + + """ Get the prop with 'name'. """ + def get_prop(self, name): + for prop in self.__props: + if prop.name == name: + return prop + raise ValueError(f'Unable to find {name} prop') + """ Get the list of all the options. """ def get_options(self): return self.__options @@ -89,7 +128,7 @@ class Algorithm: def get_option(self, name): for opt in self.__options: if opt.name == name: - return opt.value + return opt.get_value() raise ValueError(f'Unable to find {name} option') """ Reset all the options to the default value. """ @@ -97,10 +136,17 @@ class Algorithm: for opt in self.__options: opt.reset() + def set_input_file(self, file_path, pixbuf): + self.input_file_path = file_path + + def set_output_file(self, file_path): + self.output_file_path = file_path + + def default_output_file_name(self): + raise NotImplementedError() + """ Starts the algorithm. """ def start(self, - input_file_path, - output_file_path, progress_callback, done_callback): raise NotImplementedError() diff --git a/upscaler/algorithms/realesrgan_ncnn_vulkan.py b/upscaler/algorithms/realesrgan_ncnn_vulkan.py index b7ad03f..57f2880 100644 --- a/upscaler/algorithms/realesrgan_ncnn_vulkan.py +++ b/upscaler/algorithms/realesrgan_ncnn_vulkan.py @@ -16,11 +16,13 @@ # # SPDX-License-Identifier: GPL-3.0-only +from os.path import basename, splitext from enum import Enum import re import subprocess from gi.repository import GLib from upscaler.algorithms.commons import (Algorithm, + Prop, EnumOption, AlgorithmFailed, AlgorithmWarning) @@ -30,6 +32,8 @@ ALG_WARNINGS = [ 'vkQueueSubmit failed' ] +UPSCALE_FACTOR = 4 + class ImageTypeEnum(Enum): PHOTO = _("Photo") CARTOON = _("Cartoon/Anime") @@ -41,13 +45,36 @@ MODEL_IMAGES = { class RealesrganNcnnVulkan(Algorithm): def __init__(self): - super().__init__([ - EnumOption('model', ImageTypeEnum.PHOTO, 'Type of Image') - ]) + super().__init__( + [ + Prop('input', _('Image Size'), None, None), + Prop('output', + _('Post-upscale Image Size'), + None, + _('Image size after it is upscaled.')), + ], + [ + EnumOption('model', ImageTypeEnum.PHOTO, 'Type of Image', None), + ]) + self.process = None + + def set_input_file(self, file_path, pixbuf): + super().set_input_file(file_path, pixbuf) + prop = self.get_prop('input') + height = pixbuf.props.height + width = pixbuf.props.width + prop.set_subtitle(f'{width} × {height}') + + prop = self.get_prop('output') + prop.set_subtitle(f'{width * UPSCALE_FACTOR} × {height * UPSCALE_FACTOR}') + + base_path = basename(splitext(file_path)[0]) + self.__default_output = f'{base_path}-{width * UPSCALE_FACTOR}x{height * UPSCALE_FACTOR}-upscaled.png' + + def default_output_file_name(self): + return self.__default_output def start(self, - input_file_path, - output_file_path, progress_callback, done_callback): self.cancelled = False @@ -55,8 +82,8 @@ class RealesrganNcnnVulkan(Algorithm): def run(): command = ['realesrgan-ncnn-vulkan', - '-i', input_file_path, - '-o', output_file_path, + '-i', self.input_file_path, + '-o', self.output_file_path, '-n', model, '-s', '4'] self.process = subprocess.Popen(command, diff --git a/upscaler/gtk/window.blp b/upscaler/gtk/window.blp index f26c1f2..05b5f30 100644 --- a/upscaler/gtk/window.blp +++ b/upscaler/gtk/window.blp @@ -94,24 +94,8 @@ template UpscalerWindow : Adw.ApplicationWindow { height-request: 192; } - Adw.PreferencesGroup { + Adw.PreferencesGroup properties { title: _("Properties"); - - Adw.ActionRow action_image_size { - title: _("Image Size"); - - styles [ - "property", - ] - } - - Adw.ActionRow action_upscale_image_size { - title: _("Image Size After Upscaling"); - - styles [ - "property", - ] - } } Adw.PreferencesGroup options { diff --git a/upscaler/window.py b/upscaler/window.py index 5de1f8f..a625838 100644 --- a/upscaler/window.py +++ b/upscaler/window.py @@ -16,7 +16,7 @@ # # SPDX-License-Identifier: GPL-3.0-only -from os.path import basename, splitext +from os.path import basename from gi.repository import Adw, Gtk, GLib, Gdk, Gio, Pango from sys import exit from upscaler.algorithms.commons import AlgorithmWarning @@ -27,8 +27,6 @@ import vulkan # type: ignore from typing import Any, Callable, Optional, Literal from gettext import gettext as _ -UPSCALE_FACTOR = 4 - @Gtk.Template(resource_path='/io/gitlab/theevilskeleton/Upscaler/gtk/window.ui') class UpscalerWindow(Adw.ApplicationWindow): __gtype_name__ = 'UpscalerWindow' @@ -37,8 +35,7 @@ class UpscalerWindow(Adw.ApplicationWindow): toast: Adw.ToastOverlay = Gtk.Template.Child() # type: ignore stack_upscaler: Gtk.Stack = Gtk.Template.Child() # type: ignore button_input: Gtk.Button = Gtk.Template.Child() # type: ignore - action_image_size: Adw.ActionRow = Gtk.Template.Child() # type: ignore - action_upscale_image_size: Adw.ActionRow = Gtk.Template.Child() # type: ignore + properties = Gtk.Template.Child() button_upscale: Gtk.Button = Gtk.Template.Child() # type: ignore spinner_loading: Gtk.Spinner = Gtk.Template.Child() # type: ignore image: Gtk.Picture = Gtk.Template.Child() # type: ignore @@ -96,11 +93,9 @@ class UpscalerWindow(Adw.ApplicationWindow): def on_file_open(self, input_file_path: str, pixbuf: GdkPixbuf.Pixbuf) -> None: """Open and display file.""" self.input_file_path = input_file_path - self.image_size = (pixbuf.get_width(), pixbuf.get_height()) + self.algorithm.set_input_file(input_file_path, pixbuf) # Display image - self.action_image_size.set_subtitle(f'{self.image_size[0]} × {self.image_size[1]}') - self.action_upscale_image_size.set_subtitle(f'{self.image_size[0] * UPSCALE_FACTOR} × {self.image_size[1] * UPSCALE_FACTOR}') self.image.set_pixbuf(pixbuf) # Reset widgets @@ -126,11 +121,15 @@ class UpscalerWindow(Adw.ApplicationWindow): """ Build algorithm and options. """ if self.algorithm: # Cleanup algorithm widgets. + for prop in self.algorithm.get_props(): + self.properties.remove(prop.widget()) for opt in self.algorithm.get_options(): self.options.remove(opt.widget()) self.algorithm = Algorithm() options = self.algorithm.get_options() + for prop in self.algorithm.get_props(): + self.properties.add(prop.widget()) for opt in options: self.options.add(opt.widget()) @@ -143,6 +142,7 @@ class UpscalerWindow(Adw.ApplicationWindow): def good(output_file_path: str) -> None: # Set variables self.output_file_path = output_file_path + self.algorithm.set_output_file(output_file_path) # Update widgets self.button_upscale.set_sensitive(True) @@ -156,13 +156,13 @@ class UpscalerWindow(Adw.ApplicationWindow): if message: self.toast.add_toast(Adw.Toast.new(message)) - if self.input_file_path is None: - return + try: + default_output = self.algorithm.default_output_file_name() + except NotImplementedError: + default_output = 'output.png' - base_path = basename(splitext(self.input_file_path)[0]) - image_size = [x * UPSCALE_FACTOR for x in self.image_size] FileChooser.output_file(self, - f'{base_path}-{image_size[0]}x{image_size[1]}-upscaled.png', + default_output, good, bad) @@ -204,9 +204,7 @@ class UpscalerWindow(Adw.ApplicationWindow): reset_widgets() # Run functions asynchronously - self.algorithm.start(self.input_file_path, - self.output_file_path, - self.__upscale_progress, + self.algorithm.start(self.__upscale_progress, callback) self.stack_upscaler.set_visible_child_name('stack_upscaling') self.previous_stack = 'stack_upscaling' -- GitLab From fc9121d2c21750084f4de8446cbcd8332a06b21a Mon Sep 17 00:00:00 2001 From: Matteo Percivaldi Date: Sat, 15 Apr 2023 10:14:44 +0200 Subject: [PATCH 05/14] misc: Added typings and cleanup --- upscaler/algorithms/__init__.py | 0 upscaler/algorithms/commons.py | 106 ++++++++++-------- upscaler/algorithms/realesrgan_ncnn_vulkan.py | 52 +++++---- upscaler/window.py | 62 ++++------ 4 files changed, 109 insertions(+), 111 deletions(-) create mode 100644 upscaler/algorithms/__init__.py diff --git a/upscaler/algorithms/__init__.py b/upscaler/algorithms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/upscaler/algorithms/commons.py b/upscaler/algorithms/commons.py index b911f51..e671ff1 100644 --- a/upscaler/algorithms/commons.py +++ b/upscaler/algorithms/commons.py @@ -16,16 +16,18 @@ # # SPDX-License-Identifier: GPL-3.0-only +from typing import Any, Callable, Optional +from enum import Enum from gi.repository import Gtk, Adw """ Raised when the algorithm has failed. """ class AlgorithmFailed(Exception): - def __init__(self, result_code, output): + def __init__(self, result_code: int, output: str) -> None: super().__init__() self.result_code = result_code self.output = output - def __str__(self): + def __str__(self) -> str: return f'Algorithm failed.\nResult code: {self.result_code}\nOutput: {self.output}' """ Raised when the output could be damaged. """ @@ -33,49 +35,55 @@ class AlgorithmWarning(Exception): pass class Prop: - def __init__(self, name, title, subtitle, tooltip): - self.name = name - self.__widget = Adw.ActionRow(title=title, - subtitle=subtitle, - tooltip_text=tooltip) + def __init__(self, title: str, subtitle: Optional[str], tooltip: Optional[str]) -> None: + self.__widget = Adw.ActionRow(title=title) + if subtitle: + self.__widget.set_subtitle(subtitle) + if tooltip: + self.__widget.set_tooltip_text(tooltip) self.__widget.add_css_class('property') - def set_title(self, title): + def set_title(self, title: str) -> None: self.__widget.set_title(title) - def set_subtitle(self, subtitle): + def set_subtitle(self, subtitle: str) -> None: self.__widget.set_subtitle(subtitle) - def widget(self, **_): + def widget(self) -> Gtk.Widget: return self.__widget """ Option base class. """ -class __Option: - def __init__(self, name, default_value, desc, on_changed): - self.name = name +class Option: + def __init__(self, + default_value: Any, + desc: str, + on_changed: Optional[Callable[[], None]]) -> None: self.__default_value = default_value self.__value = default_value self.__desc = desc self.__on_changed = on_changed - def widget(self, **_): + def widget(self) -> Gtk.Widget: raise NotImplementedError() - def get_value(self): + def get_value(self) -> Any: return self.__value - def set_value(self, value): + def set_value(self, value: Any) -> None: self.__value = value if self.__on_changed is not None: self.__on_changed() - def reset(self): + def reset(self) -> None: self.set_value(self.__default_value) """ String enum option. """ -class EnumOption(__Option): - def __init__(self, name, enum, desc, on_changed): - super().__init__(name, enum, desc, on_changed) +class EnumOption(Option): + def __init__(self, + enum: Enum, + desc: str, + on_changed: Optional[Callable[[], None]]) -> None: + super().__init__(enum, desc, on_changed) self.Enum = type(enum) @@ -86,71 +94,71 @@ class EnumOption(__Option): model=string_list) self.__row.connect('notify::selected', self.__on_row_selected) - def widget(self): + def widget(self) -> Gtk.Widget: return self.__row - def __on_row_selected(self, row, *_): + def __on_row_selected(self, row: Adw.ComboRow, *_: Any) -> None: self.set_value(self.Enum(self.__strings[row.get_selected()])) - def reset(self): + def reset(self) -> None: super().reset() self.__row.set_selected(self.__strings.index(self.get_value().value)) """ Algorithm base class. """ class Algorithm: - def __init__(self, props, options): - self.input_file_path = None - self.output_file_path = None + def __init__(self, props: dict[str, Prop], options: dict[str, Option]) -> None: + self.input_file_path: Optional[str] = None + self.output_file_path: Optional[str] = None self.cancelled = False self.__props = props self.__options = options - def __del__(self): + def __del__(self) -> None: """ Cancel operation on destruction. """ self.cancel() """ Get the list of all props. """ - def get_props(self): - return self.__props + def get_props(self) -> list[Prop]: + return list(self.__props.values()) """ Get the prop with 'name'. """ - def get_prop(self, name): - for prop in self.__props: - if prop.name == name: - return prop - raise ValueError(f'Unable to find {name} prop') + def get_prop(self, name: str) -> Prop: + if not name in self.__props: + raise ValueError(f'Unable to find {name} prop') + + return self.__props[name] """ Get the list of all the options. """ - def get_options(self): - return self.__options + def get_options(self) -> list[Option]: + return list(self.__options.values()) """ Get the value of the option with 'name'. """ - def get_option(self, name): - for opt in self.__options: - if opt.name == name: - return opt.get_value() - raise ValueError(f'Unable to find {name} option') + def get_option(self, name: str) -> Any: + if not name in self.__options: + raise ValueError(f'Unable to find {name} option') + + return self.__options[name].get_value() """ Reset all the options to the default value. """ - def reset_options(self): - for opt in self.__options: + def reset_options(self) -> None: + for opt in self.get_options(): opt.reset() - def set_input_file(self, file_path, pixbuf): + def set_input_file(self, file_path: str, *args: Any) -> None: self.input_file_path = file_path - def set_output_file(self, file_path): + def set_output_file(self, file_path: str) -> None: self.output_file_path = file_path - def default_output_file_name(self): + def default_output_file_name(self) -> str: raise NotImplementedError() """ Starts the algorithm. """ def start(self, - progress_callback, - done_callback): + progress_callback: Callable[[float], None], + done_callback: Callable[[Any, Optional[Exception]], None]) -> None: raise NotImplementedError() """ Cancel the current operation. """ - def cancel(self): + def cancel(self) -> None: raise NotImplementedError() diff --git a/upscaler/algorithms/realesrgan_ncnn_vulkan.py b/upscaler/algorithms/realesrgan_ncnn_vulkan.py index 57f2880..2ac2ced 100644 --- a/upscaler/algorithms/realesrgan_ncnn_vulkan.py +++ b/upscaler/algorithms/realesrgan_ncnn_vulkan.py @@ -20,13 +20,15 @@ from os.path import basename, splitext from enum import Enum import re import subprocess -from gi.repository import GLib +from gi.repository import GLib, GdkPixbuf from upscaler.algorithms.commons import (Algorithm, Prop, EnumOption, AlgorithmFailed, AlgorithmWarning) from upscaler.threading import RunAsync +from typing import Any, IO, Callable, Optional, cast +from gettext import gettext as _ ALG_WARNINGS = [ 'vkQueueSubmit failed' @@ -44,21 +46,21 @@ MODEL_IMAGES = { } class RealesrganNcnnVulkan(Algorithm): - def __init__(self): + def __init__(self) -> None: super().__init__( - [ - Prop('input', _('Image Size'), None, None), - Prop('output', - _('Post-upscale Image Size'), - None, - _('Image size after it is upscaled.')), - ], - [ - EnumOption('model', ImageTypeEnum.PHOTO, 'Type of Image', None), - ]) - self.process = None - - def set_input_file(self, file_path, pixbuf): + { + 'input': Prop(_('Image Size'), None, None), + 'output': Prop(_('Post-upscale Image Size'), + None, + _('Image size after it is upscaled.')), + }, + { + 'model': EnumOption(ImageTypeEnum.PHOTO, 'Type of Image', None), + }) + self.process: Optional[subprocess.Popen[str]] = None + + def set_input_file(self, file_path: str, *args: Any) -> None: + pixbuf: GdkPixbuf.Pixbuf = args[0] super().set_input_file(file_path, pixbuf) prop = self.get_prop('input') height = pixbuf.props.height @@ -71,16 +73,19 @@ class RealesrganNcnnVulkan(Algorithm): base_path = basename(splitext(file_path)[0]) self.__default_output = f'{base_path}-{width * UPSCALE_FACTOR}x{height * UPSCALE_FACTOR}-upscaled.png' - def default_output_file_name(self): + def default_output_file_name(self) -> str: return self.__default_output def start(self, - progress_callback, - done_callback): + progress_callback: Callable[[float], None], + done_callback: Callable[[Any, Optional[Exception]], None]) -> None: self.cancelled = False model = MODEL_IMAGES[self.get_option('model')] - def run(): + def run() -> None: + if self.input_file_path is None or self.output_file_path is None: + return + command = ['realesrgan-ncnn-vulkan', '-i', self.input_file_path, '-o', self.output_file_path, @@ -95,10 +100,10 @@ class RealesrganNcnnVulkan(Algorithm): """ Read each line, query the percentage and update the progress bar. """ output = "" bad = False - for line in iter(self.process.stderr.readline, ''): + for line in iter(cast(IO[str], self.process.stderr).readline, ''): print(line, end='') output += line - res = re.match('^(\d*.\d+)%$', line) + res = re.match('^(\\d*.\\d+)%$', line) if res: GLib.idle_add(progress_callback, float(res.group(1))) else: @@ -113,14 +118,15 @@ class RealesrganNcnnVulkan(Algorithm): if self.cancelled: return result = self.process.poll() - if result != 0: + if result is None or result != 0: + if result is None: result = -1 raise AlgorithmFailed(result, output) if bad: raise AlgorithmWarning RunAsync(run, done_callback) - def cancel(self): + def cancel(self) -> None: if self.process: self.cancelled = True self.process.kill() diff --git a/upscaler/window.py b/upscaler/window.py index a625838..f4dcd3e 100644 --- a/upscaler/window.py +++ b/upscaler/window.py @@ -17,14 +17,14 @@ # SPDX-License-Identifier: GPL-3.0-only from os.path import basename -from gi.repository import Adw, Gtk, GLib, Gdk, Gio, Pango +from gi.repository import Adw, Gtk, GLib, Gdk, Gio, Pango, GdkPixbuf from sys import exit -from upscaler.algorithms.commons import AlgorithmWarning +from upscaler.algorithms.commons import Algorithm, AlgorithmWarning from upscaler.algorithms.realesrgan_ncnn_vulkan import RealesrganNcnnVulkan from upscaler.file_chooser import FileChooser from filecmp import cmp import vulkan # type: ignore -from typing import Any, Callable, Optional, Literal +from typing import Any, Optional, Literal, Callable, cast from gettext import gettext as _ @Gtk.Template(resource_path='/io/gitlab/theevilskeleton/Upscaler/gtk/window.ui') @@ -35,13 +35,13 @@ class UpscalerWindow(Adw.ApplicationWindow): toast: Adw.ToastOverlay = Gtk.Template.Child() # type: ignore stack_upscaler: Gtk.Stack = Gtk.Template.Child() # type: ignore button_input: Gtk.Button = Gtk.Template.Child() # type: ignore - properties = Gtk.Template.Child() + properties: Adw.PreferencesGroup = Gtk.Template.Child() # type: ignore button_upscale: Gtk.Button = Gtk.Template.Child() # type: ignore spinner_loading: Gtk.Spinner = Gtk.Template.Child() # type: ignore image: Gtk.Picture = Gtk.Template.Child() # type: ignore # video = Gtk.Template.Child() # spin_scale = Gtk.Template.Child() - options = Gtk.Template.Child() + options: Adw.PreferencesGroup = Gtk.Template.Child() # type: ignore button_output: Gtk.Button = Gtk.Template.Child() # type: ignore label_output: Gtk.Label = Gtk.Template.Child() # type: ignore button_cancel: Gtk.Button = Gtk.Template.Child() # type: ignore @@ -71,7 +71,8 @@ class UpscalerWindow(Adw.ApplicationWindow): content = Gdk.ContentFormats.new_for_gtype(Gio.File) self.target = Gtk.DropTarget(formats=content, actions=Gdk.DragAction.COPY) self.previous_stack = 'stack_welcome_page' - self.algorithm = None + + self.algorithm: Optional[Algorithm] = None self.__set_algorithm(RealesrganNcnnVulkan) # Connect signals @@ -92,6 +93,10 @@ class UpscalerWindow(Adw.ApplicationWindow): def on_file_open(self, input_file_path: str, pixbuf: GdkPixbuf.Pixbuf) -> None: """Open and display file.""" + if self.algorithm is None: + return + + # Set variables self.input_file_path = input_file_path self.algorithm.set_input_file(input_file_path, pixbuf) @@ -117,7 +122,7 @@ class UpscalerWindow(Adw.ApplicationWindow): """Open the file chooser to load the file.""" FileChooser.open_file(self) - def __set_algorithm(self, Algorithm): + def __set_algorithm(self, A: Any) -> None: """ Build algorithm and options. """ if self.algorithm: # Cleanup algorithm widgets. @@ -126,10 +131,10 @@ class UpscalerWindow(Adw.ApplicationWindow): for opt in self.algorithm.get_options(): self.options.remove(opt.widget()) - self.algorithm = Algorithm() - options = self.algorithm.get_options() + self.algorithm = cast(Algorithm, A()) for prop in self.algorithm.get_props(): self.properties.add(prop.widget()) + options = self.algorithm.get_options() for opt in options: self.options.add(opt.widget()) @@ -142,7 +147,7 @@ class UpscalerWindow(Adw.ApplicationWindow): def good(output_file_path: str) -> None: # Set variables self.output_file_path = output_file_path - self.algorithm.set_output_file(output_file_path) + cast(Algorithm, self.algorithm).set_output_file(output_file_path) # Update widgets self.button_upscale.set_sensitive(True) @@ -157,7 +162,7 @@ class UpscalerWindow(Adw.ApplicationWindow): self.toast.add_toast(Adw.Toast.new(message)) try: - default_output = self.algorithm.default_output_file_name() + default_output = cast(Algorithm, self.algorithm).default_output_file_name() except NotImplementedError: default_output = 'output.png' @@ -193,8 +198,8 @@ class UpscalerWindow(Adw.ApplicationWindow): self.progressbar.set_fraction(0) # Run after algorithm finishes. - def callback(result, error): - if self.algorithm.cancelled: + def callback(result: Any, error: Optional[Exception]) -> None: + if cast(Algorithm, self.algorithm).cancelled: self.toast.add_toast(Adw.Toast.new(_('Upscaling Cancelled'))) else: self.__upscaling_completed_dialog(error) @@ -204,8 +209,8 @@ class UpscalerWindow(Adw.ApplicationWindow): reset_widgets() # Run functions asynchronously - self.algorithm.start(self.__upscale_progress, - callback) + cast(Algorithm, self.algorithm).start(self.__upscale_progress, + callback) self.stack_upscaler.set_visible_child_name('stack_upscaling') self.previous_stack = 'stack_upscaling' self.button_upscale.set_sensitive(False) @@ -215,28 +220,6 @@ class UpscalerWindow(Adw.ApplicationWindow): if self.output_file_path is None: return - def response(_widget): - file = open(self.output_file_path, 'r') - fid = file.fileno() - connection = Gio.bus_get_sync(Gio.BusType.SESSION, None) - proxy = Gio.DBusProxy.new_sync(connection, - Gio.DBusProxyFlags.NONE, - None, - 'org.freedesktop.portal.Desktop', - '/org/freedesktop/portal/desktop', - 'org.freedesktop.portal.OpenURI', - None) - - try: - proxy.call_with_unix_fd_list_sync('OpenFile', - GLib.Variant('(sha{sv})', ('', 0, {'ask': GLib.Variant('b', True)})), - Gio.DBusCallFlags.NONE, - -1, - Gio.UnixFDList.new_from_array([fid]), - None) - except Exception as e: - print(f'Error: {e}') - toast = None notification = Gio.Notification() @@ -343,8 +326,9 @@ class UpscalerWindow(Adw.ApplicationWindow): self.spinner_loading.start() def __cancel(self, *args: Any) -> None: - def function(): - self.algorithm.cancel() + def function() -> None: + if self.algorithm: + self.algorithm.cancel() self.__close_dialog(function) def __load_image_done(self, _obj: Any, result: Gio.AsyncResult, input_file_path: str) -> None: -- GitLab From 62f4f4130dfdb716c813ece1b974b1631d537006 Mon Sep 17 00:00:00 2001 From: Matteo Percivaldi Date: Sat, 15 Apr 2023 10:21:22 +0200 Subject: [PATCH 06/14] fix: Fix strings --- upscaler/algorithms/realesrgan_ncnn_vulkan.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/upscaler/algorithms/realesrgan_ncnn_vulkan.py b/upscaler/algorithms/realesrgan_ncnn_vulkan.py index 2ac2ced..09f9cc0 100644 --- a/upscaler/algorithms/realesrgan_ncnn_vulkan.py +++ b/upscaler/algorithms/realesrgan_ncnn_vulkan.py @@ -50,9 +50,7 @@ class RealesrganNcnnVulkan(Algorithm): super().__init__( { 'input': Prop(_('Image Size'), None, None), - 'output': Prop(_('Post-upscale Image Size'), - None, - _('Image size after it is upscaled.')), + 'output': Prop(_('Image Size After Upscaling'), None, None), }, { 'model': EnumOption(ImageTypeEnum.PHOTO, 'Type of Image', None), -- GitLab From 93faa7ccfef7920f1f743f958a44939b621407cc Mon Sep 17 00:00:00 2001 From: Matteo Percivaldi Date: Tue, 2 May 2023 16:23:45 +0200 Subject: [PATCH 07/14] misc: Fix comments and documentation strings --- upscaler/algorithms/commons.py | 26 +++++++++---------- upscaler/algorithms/realesrgan_ncnn_vulkan.py | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/upscaler/algorithms/commons.py b/upscaler/algorithms/commons.py index e671ff1..fda63f7 100644 --- a/upscaler/algorithms/commons.py +++ b/upscaler/algorithms/commons.py @@ -20,8 +20,8 @@ from typing import Any, Callable, Optional from enum import Enum from gi.repository import Gtk, Adw -""" Raised when the algorithm has failed. """ class AlgorithmFailed(Exception): + """ Raised when the algorithm has failed. """ def __init__(self, result_code: int, output: str) -> None: super().__init__() self.result_code = result_code @@ -30,8 +30,8 @@ class AlgorithmFailed(Exception): def __str__(self) -> str: return f'Algorithm failed.\nResult code: {self.result_code}\nOutput: {self.output}' -""" Raised when the output could be damaged. """ class AlgorithmWarning(Exception): + """ Raised when the output could be damaged. """ pass class Prop: @@ -52,8 +52,8 @@ class Prop: def widget(self) -> Gtk.Widget: return self.__widget -""" Option base class. """ class Option: + """ Option base class. """ def __init__(self, default_value: Any, desc: str, @@ -77,8 +77,8 @@ class Option: def reset(self) -> None: self.set_value(self.__default_value) -""" String enum option. """ class EnumOption(Option): + """ String enum option. """ def __init__(self, enum: Enum, desc: str, @@ -104,8 +104,8 @@ class EnumOption(Option): super().reset() self.__row.set_selected(self.__strings.index(self.get_value().value)) -""" Algorithm base class. """ class Algorithm: + """ Algorithm base class. """ def __init__(self, props: dict[str, Prop], options: dict[str, Option]) -> None: self.input_file_path: Optional[str] = None self.output_file_path: Optional[str] = None @@ -114,33 +114,33 @@ class Algorithm: self.__options = options def __del__(self) -> None: - """ Cancel operation on destruction. """ + # Cancel operation on destruction self.cancel() - """ Get the list of all props. """ def get_props(self) -> list[Prop]: + """ Get the list of all props. """ return list(self.__props.values()) - """ Get the prop with 'name'. """ def get_prop(self, name: str) -> Prop: + """ Get the prop with 'name'. """ if not name in self.__props: raise ValueError(f'Unable to find {name} prop') return self.__props[name] - """ Get the list of all the options. """ def get_options(self) -> list[Option]: + """ Get the list of all the options. """ return list(self.__options.values()) - """ Get the value of the option with 'name'. """ def get_option(self, name: str) -> Any: + """ Get the value of the option with 'name'. """ if not name in self.__options: raise ValueError(f'Unable to find {name} option') return self.__options[name].get_value() - """ Reset all the options to the default value. """ def reset_options(self) -> None: + """ Reset all the options to the default value. """ for opt in self.get_options(): opt.reset() @@ -153,12 +153,12 @@ class Algorithm: def default_output_file_name(self) -> str: raise NotImplementedError() - """ Starts the algorithm. """ def start(self, progress_callback: Callable[[float], None], done_callback: Callable[[Any, Optional[Exception]], None]) -> None: + """ Starts the algorithm. """ raise NotImplementedError() - """ Cancel the current operation. """ def cancel(self) -> None: + """ Cancel the current operation. """ raise NotImplementedError() diff --git a/upscaler/algorithms/realesrgan_ncnn_vulkan.py b/upscaler/algorithms/realesrgan_ncnn_vulkan.py index 09f9cc0..0406893 100644 --- a/upscaler/algorithms/realesrgan_ncnn_vulkan.py +++ b/upscaler/algorithms/realesrgan_ncnn_vulkan.py @@ -95,7 +95,7 @@ class RealesrganNcnnVulkan(Algorithm): print('Running: ', end='') print(*command) - """ Read each line, query the percentage and update the progress bar. """ + # Read each line, query the percentage and update the progress bar output = "" bad = False for line in iter(cast(IO[str], self.process.stderr).readline, ''): @@ -112,7 +112,7 @@ class RealesrganNcnnVulkan(Algorithm): bad = True continue - """ Process algorithm output. """ + # Process algorithm output if self.cancelled: return result = self.process.poll() -- GitLab From 4fde7f89f0172047705c46401890d37ba4dc8796 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Mon, 22 May 2023 17:24:17 +0000 Subject: [PATCH 08/14] Apply suggestions --- upscaler/algorithms/commons.py | 6 +++--- upscaler/algorithms/realesrgan_ncnn_vulkan.py | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/upscaler/algorithms/commons.py b/upscaler/algorithms/commons.py index fda63f7..fa39f43 100644 --- a/upscaler/algorithms/commons.py +++ b/upscaler/algorithms/commons.py @@ -1,6 +1,6 @@ # commons.py: common data structures for algorithms # -# Copyright (C) 2022 Upscaler Contributors +# Copyright (C) 2023 Upscaler Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ from enum import Enum from gi.repository import Gtk, Adw class AlgorithmFailed(Exception): - """ Raised when the algorithm has failed. """ + """ Raise when the algorithm has failed. """ def __init__(self, result_code: int, output: str) -> None: super().__init__() self.result_code = result_code @@ -31,7 +31,7 @@ class AlgorithmFailed(Exception): return f'Algorithm failed.\nResult code: {self.result_code}\nOutput: {self.output}' class AlgorithmWarning(Exception): - """ Raised when the output could be damaged. """ + """ Raise when the output could be damaged. """ pass class Prop: diff --git a/upscaler/algorithms/realesrgan_ncnn_vulkan.py b/upscaler/algorithms/realesrgan_ncnn_vulkan.py index 0406893..d169c61 100644 --- a/upscaler/algorithms/realesrgan_ncnn_vulkan.py +++ b/upscaler/algorithms/realesrgan_ncnn_vulkan.py @@ -1,6 +1,6 @@ # realesrgan_ncnn_vulkan.py: Real-ESRGAN ncnn Vulkan implementation # -# Copyright (C) 2022 Upscaler Contributors +# Copyright (C) 2023 Upscaler Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -34,6 +34,7 @@ ALG_WARNINGS = [ 'vkQueueSubmit failed' ] +# Real-ESRGAN ncnn Vulkan only properly supports 4x. UPSCALE_FACTOR = 4 class ImageTypeEnum(Enum): -- GitLab From 545095641c557e3f07d3c3620f9863fb781d96cd Mon Sep 17 00:00:00 2001 From: Matteo Percivaldi Date: Tue, 23 May 2023 08:14:57 +0200 Subject: [PATCH 09/14] window: Make the code more readable --- upscaler/window.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/upscaler/window.py b/upscaler/window.py index f4dcd3e..212a0fa 100644 --- a/upscaler/window.py +++ b/upscaler/window.py @@ -122,7 +122,7 @@ class UpscalerWindow(Adw.ApplicationWindow): """Open the file chooser to load the file.""" FileChooser.open_file(self) - def __set_algorithm(self, A: Any) -> None: + def __set_algorithm(self, AlgorithmType: Any) -> None: """ Build algorithm and options. """ if self.algorithm: # Cleanup algorithm widgets. @@ -131,7 +131,7 @@ class UpscalerWindow(Adw.ApplicationWindow): for opt in self.algorithm.get_options(): self.options.remove(opt.widget()) - self.algorithm = cast(Algorithm, A()) + self.algorithm = cast(Algorithm, AlgorithmType()) for prop in self.algorithm.get_props(): self.properties.add(prop.widget()) options = self.algorithm.get_options() -- GitLab From e5536030ce5cfcde48c503e9864afe4f1d167336 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Wed, 24 May 2023 18:23:08 +0000 Subject: [PATCH 10/14] Apply suggestions --- upscaler/algorithms/commons.py | 24 +++++++++---------- upscaler/algorithms/realesrgan_ncnn_vulkan.py | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/upscaler/algorithms/commons.py b/upscaler/algorithms/commons.py index fa39f43..13fb016 100644 --- a/upscaler/algorithms/commons.py +++ b/upscaler/algorithms/commons.py @@ -21,7 +21,7 @@ from enum import Enum from gi.repository import Gtk, Adw class AlgorithmFailed(Exception): - """ Raise when the algorithm has failed. """ + """Raise when the algorithm has failed.""" def __init__(self, result_code: int, output: str) -> None: super().__init__() self.result_code = result_code @@ -31,7 +31,7 @@ class AlgorithmFailed(Exception): return f'Algorithm failed.\nResult code: {self.result_code}\nOutput: {self.output}' class AlgorithmWarning(Exception): - """ Raise when the output could be damaged. """ + """Raise when the output could be damaged.""" pass class Prop: @@ -53,7 +53,7 @@ class Prop: return self.__widget class Option: - """ Option base class. """ + """Option base class.""" def __init__(self, default_value: Any, desc: str, @@ -78,7 +78,7 @@ class Option: self.set_value(self.__default_value) class EnumOption(Option): - """ String enum option. """ + """String enum option.""" def __init__(self, enum: Enum, desc: str, @@ -105,7 +105,7 @@ class EnumOption(Option): self.__row.set_selected(self.__strings.index(self.get_value().value)) class Algorithm: - """ Algorithm base class. """ + """Algorithm base class.""" def __init__(self, props: dict[str, Prop], options: dict[str, Option]) -> None: self.input_file_path: Optional[str] = None self.output_file_path: Optional[str] = None @@ -118,29 +118,29 @@ class Algorithm: self.cancel() def get_props(self) -> list[Prop]: - """ Get the list of all props. """ + """Get the list of all props.""" return list(self.__props.values()) def get_prop(self, name: str) -> Prop: - """ Get the prop with 'name'. """ + """Get the prop with 'name'.""" if not name in self.__props: raise ValueError(f'Unable to find {name} prop') return self.__props[name] def get_options(self) -> list[Option]: - """ Get the list of all the options. """ + """Get the list of all the options.""" return list(self.__options.values()) def get_option(self, name: str) -> Any: - """ Get the value of the option with 'name'. """ + """Get the value of the option with 'name'.""" if not name in self.__options: raise ValueError(f'Unable to find {name} option') return self.__options[name].get_value() def reset_options(self) -> None: - """ Reset all the options to the default value. """ + """Reset all the options to the default value.""" for opt in self.get_options(): opt.reset() @@ -156,9 +156,9 @@ class Algorithm: def start(self, progress_callback: Callable[[float], None], done_callback: Callable[[Any, Optional[Exception]], None]) -> None: - """ Starts the algorithm. """ + """Start the algorithm.""" raise NotImplementedError() def cancel(self) -> None: - """ Cancel the current operation. """ + """Cancel the current operation.""" raise NotImplementedError() diff --git a/upscaler/algorithms/realesrgan_ncnn_vulkan.py b/upscaler/algorithms/realesrgan_ncnn_vulkan.py index d169c61..adffa75 100644 --- a/upscaler/algorithms/realesrgan_ncnn_vulkan.py +++ b/upscaler/algorithms/realesrgan_ncnn_vulkan.py @@ -97,7 +97,7 @@ class RealesrganNcnnVulkan(Algorithm): print(*command) # Read each line, query the percentage and update the progress bar - output = "" + output = '' bad = False for line in iter(cast(IO[str], self.process.stderr).readline, ''): print(line, end='') -- GitLab From 9890e0ca6a2090c679a8a29e8d2bb4141300e690 Mon Sep 17 00:00:00 2001 From: Matteo Percivaldi Date: Sat, 27 May 2023 09:25:41 +0200 Subject: [PATCH 11/14] realesrgan: Add documentation --- upscaler/algorithms/realesrgan_ncnn_vulkan.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/upscaler/algorithms/realesrgan_ncnn_vulkan.py b/upscaler/algorithms/realesrgan_ncnn_vulkan.py index adffa75..7e4dde2 100644 --- a/upscaler/algorithms/realesrgan_ncnn_vulkan.py +++ b/upscaler/algorithms/realesrgan_ncnn_vulkan.py @@ -78,6 +78,8 @@ class RealesrganNcnnVulkan(Algorithm): def start(self, progress_callback: Callable[[float], None], done_callback: Callable[[Any, Optional[Exception]], None]) -> None: + """Start the algorithm.""" + self.cancelled = False model = MODEL_IMAGES[self.get_option('model')] -- GitLab From c15bc9dbd71f5ed4ea1e649622501fcab030658b Mon Sep 17 00:00:00 2001 From: Matteo Percivaldi Date: Sat, 27 May 2023 09:30:18 +0200 Subject: [PATCH 12/14] misc: Change prop to property --- upscaler/algorithms/commons.py | 22 +++++++++---------- upscaler/algorithms/realesrgan_ncnn_vulkan.py | 14 ++++++------ upscaler/window.py | 8 +++---- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/upscaler/algorithms/commons.py b/upscaler/algorithms/commons.py index 13fb016..12f28c4 100644 --- a/upscaler/algorithms/commons.py +++ b/upscaler/algorithms/commons.py @@ -34,7 +34,7 @@ class AlgorithmWarning(Exception): """Raise when the output could be damaged.""" pass -class Prop: +class Property: def __init__(self, title: str, subtitle: Optional[str], tooltip: Optional[str]) -> None: self.__widget = Adw.ActionRow(title=title) if subtitle: @@ -106,27 +106,27 @@ class EnumOption(Option): class Algorithm: """Algorithm base class.""" - def __init__(self, props: dict[str, Prop], options: dict[str, Option]) -> None: + def __init__(self, properties: dict[str, Property], options: dict[str, Option]) -> None: self.input_file_path: Optional[str] = None self.output_file_path: Optional[str] = None self.cancelled = False - self.__props = props + self.__properties = properties self.__options = options def __del__(self) -> None: # Cancel operation on destruction self.cancel() - def get_props(self) -> list[Prop]: - """Get the list of all props.""" - return list(self.__props.values()) + def get_properties(self) -> list[Property]: + """Get the list of all properties.""" + return list(self.__properties.values()) - def get_prop(self, name: str) -> Prop: - """Get the prop with 'name'.""" - if not name in self.__props: - raise ValueError(f'Unable to find {name} prop') + def get_property(self, name: str) -> Property: + """Get the property with 'name'.""" + if not name in self.__properties: + raise ValueError(f'Unable to find {name} property') - return self.__props[name] + return self.__properties[name] def get_options(self) -> list[Option]: """Get the list of all the options.""" diff --git a/upscaler/algorithms/realesrgan_ncnn_vulkan.py b/upscaler/algorithms/realesrgan_ncnn_vulkan.py index 7e4dde2..a972890 100644 --- a/upscaler/algorithms/realesrgan_ncnn_vulkan.py +++ b/upscaler/algorithms/realesrgan_ncnn_vulkan.py @@ -22,7 +22,7 @@ import re import subprocess from gi.repository import GLib, GdkPixbuf from upscaler.algorithms.commons import (Algorithm, - Prop, + Property, EnumOption, AlgorithmFailed, AlgorithmWarning) @@ -50,8 +50,8 @@ class RealesrganNcnnVulkan(Algorithm): def __init__(self) -> None: super().__init__( { - 'input': Prop(_('Image Size'), None, None), - 'output': Prop(_('Image Size After Upscaling'), None, None), + 'input': Property(_('Image Size'), None, None), + 'output': Property(_('Image Size After Upscaling'), None, None), }, { 'model': EnumOption(ImageTypeEnum.PHOTO, 'Type of Image', None), @@ -61,13 +61,13 @@ class RealesrganNcnnVulkan(Algorithm): def set_input_file(self, file_path: str, *args: Any) -> None: pixbuf: GdkPixbuf.Pixbuf = args[0] super().set_input_file(file_path, pixbuf) - prop = self.get_prop('input') + property = self.get_property('input') height = pixbuf.props.height width = pixbuf.props.width - prop.set_subtitle(f'{width} × {height}') + property.set_subtitle(f'{width} × {height}') - prop = self.get_prop('output') - prop.set_subtitle(f'{width * UPSCALE_FACTOR} × {height * UPSCALE_FACTOR}') + property = self.get_property('output') + property.set_subtitle(f'{width * UPSCALE_FACTOR} × {height * UPSCALE_FACTOR}') base_path = basename(splitext(file_path)[0]) self.__default_output = f'{base_path}-{width * UPSCALE_FACTOR}x{height * UPSCALE_FACTOR}-upscaled.png' diff --git a/upscaler/window.py b/upscaler/window.py index 212a0fa..0dad462 100644 --- a/upscaler/window.py +++ b/upscaler/window.py @@ -126,14 +126,14 @@ class UpscalerWindow(Adw.ApplicationWindow): """ Build algorithm and options. """ if self.algorithm: # Cleanup algorithm widgets. - for prop in self.algorithm.get_props(): - self.properties.remove(prop.widget()) + for property in self.algorithm.get_properties(): + self.properties.remove(property.widget()) for opt in self.algorithm.get_options(): self.options.remove(opt.widget()) self.algorithm = cast(Algorithm, AlgorithmType()) - for prop in self.algorithm.get_props(): - self.properties.add(prop.widget()) + for property in self.algorithm.get_properties(): + self.properties.add(property.widget()) options = self.algorithm.get_options() for opt in options: self.options.add(opt.widget()) -- GitLab From 40b8fcb6e39238a954e94b2f77bdb00d7f26480c Mon Sep 17 00:00:00 2001 From: Matteo Percivaldi Date: Sat, 27 May 2023 09:41:18 +0200 Subject: [PATCH 13/14] misc: Use python conventions for private variables --- upscaler/algorithms/commons.py | 64 +++++++++---------- upscaler/algorithms/realesrgan_ncnn_vulkan.py | 20 +++--- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/upscaler/algorithms/commons.py b/upscaler/algorithms/commons.py index 12f28c4..844d5cf 100644 --- a/upscaler/algorithms/commons.py +++ b/upscaler/algorithms/commons.py @@ -36,21 +36,21 @@ class AlgorithmWarning(Exception): class Property: def __init__(self, title: str, subtitle: Optional[str], tooltip: Optional[str]) -> None: - self.__widget = Adw.ActionRow(title=title) + self._widget = Adw.ActionRow(title=title) if subtitle: - self.__widget.set_subtitle(subtitle) + self._widget.set_subtitle(subtitle) if tooltip: - self.__widget.set_tooltip_text(tooltip) - self.__widget.add_css_class('property') + self._widget.set_tooltip_text(tooltip) + self._widget.add_css_class('property') def set_title(self, title: str) -> None: - self.__widget.set_title(title) + self._widget.set_title(title) def set_subtitle(self, subtitle: str) -> None: - self.__widget.set_subtitle(subtitle) + self._widget.set_subtitle(subtitle) def widget(self) -> Gtk.Widget: - return self.__widget + return self._widget class Option: """Option base class.""" @@ -58,24 +58,24 @@ class Option: default_value: Any, desc: str, on_changed: Optional[Callable[[], None]]) -> None: - self.__default_value = default_value - self.__value = default_value - self.__desc = desc - self.__on_changed = on_changed + self._default_value = default_value + self._value = default_value + self._desc = desc + self._on_changed = on_changed def widget(self) -> Gtk.Widget: raise NotImplementedError() def get_value(self) -> Any: - return self.__value + return self._value def set_value(self, value: Any) -> None: - self.__value = value - if self.__on_changed is not None: - self.__on_changed() + self._value = value + if self._on_changed is not None: + self._on_changed() def reset(self) -> None: - self.set_value(self.__default_value) + self.set_value(self._default_value) class EnumOption(Option): """String enum option.""" @@ -87,22 +87,22 @@ class EnumOption(Option): self.Enum = type(enum) - self.__strings = [e.value for e in self.Enum] - string_list = Gtk.StringList.new(self.__strings) + self._strings = [e.value for e in self.Enum] + string_list = Gtk.StringList.new(self._strings) - self.__row = Adw.ComboRow(title=desc, + self._row = Adw.ComboRow(title=desc, model=string_list) - self.__row.connect('notify::selected', self.__on_row_selected) + self._row.connect('notify::selected', self._on_row_selected) def widget(self) -> Gtk.Widget: - return self.__row + return self._row - def __on_row_selected(self, row: Adw.ComboRow, *_: Any) -> None: - self.set_value(self.Enum(self.__strings[row.get_selected()])) + def _on_row_selected(self, row: Adw.ComboRow, *_: Any) -> None: + self.set_value(self.Enum(self._strings[row.get_selected()])) def reset(self) -> None: super().reset() - self.__row.set_selected(self.__strings.index(self.get_value().value)) + self._row.set_selected(self._strings.index(self.get_value().value)) class Algorithm: """Algorithm base class.""" @@ -110,8 +110,8 @@ class Algorithm: self.input_file_path: Optional[str] = None self.output_file_path: Optional[str] = None self.cancelled = False - self.__properties = properties - self.__options = options + self._properties = properties + self._options = options def __del__(self) -> None: # Cancel operation on destruction @@ -119,25 +119,25 @@ class Algorithm: def get_properties(self) -> list[Property]: """Get the list of all properties.""" - return list(self.__properties.values()) + return list(self._properties.values()) def get_property(self, name: str) -> Property: """Get the property with 'name'.""" - if not name in self.__properties: + if not name in self._properties: raise ValueError(f'Unable to find {name} property') - return self.__properties[name] + return self._properties[name] def get_options(self) -> list[Option]: """Get the list of all the options.""" - return list(self.__options.values()) + return list(self._options.values()) def get_option(self, name: str) -> Any: """Get the value of the option with 'name'.""" - if not name in self.__options: + if not name in self._options: raise ValueError(f'Unable to find {name} option') - return self.__options[name].get_value() + return self._options[name].get_value() def reset_options(self) -> None: """Reset all the options to the default value.""" diff --git a/upscaler/algorithms/realesrgan_ncnn_vulkan.py b/upscaler/algorithms/realesrgan_ncnn_vulkan.py index a972890..8e074de 100644 --- a/upscaler/algorithms/realesrgan_ncnn_vulkan.py +++ b/upscaler/algorithms/realesrgan_ncnn_vulkan.py @@ -56,7 +56,8 @@ class RealesrganNcnnVulkan(Algorithm): { 'model': EnumOption(ImageTypeEnum.PHOTO, 'Type of Image', None), }) - self.process: Optional[subprocess.Popen[str]] = None + self._process: Optional[subprocess.Popen[str]] = None + self._default_output: Optional[str] = None def set_input_file(self, file_path: str, *args: Any) -> None: pixbuf: GdkPixbuf.Pixbuf = args[0] @@ -70,10 +71,13 @@ class RealesrganNcnnVulkan(Algorithm): property.set_subtitle(f'{width * UPSCALE_FACTOR} × {height * UPSCALE_FACTOR}') base_path = basename(splitext(file_path)[0]) - self.__default_output = f'{base_path}-{width * UPSCALE_FACTOR}x{height * UPSCALE_FACTOR}-upscaled.png' + self._default_output = f'{base_path}-{width * UPSCALE_FACTOR}x{height * UPSCALE_FACTOR}-upscaled.png' def default_output_file_name(self) -> str: - return self.__default_output + if self._default_output: + return self._default_output + else: + raise NotImplementedError def start(self, progress_callback: Callable[[float], None], @@ -92,7 +96,7 @@ class RealesrganNcnnVulkan(Algorithm): '-o', self.output_file_path, '-n', model, '-s', '4'] - self.process = subprocess.Popen(command, + self._process = subprocess.Popen(command, stderr=subprocess.PIPE, universal_newlines=True) print('Running: ', end='') @@ -101,7 +105,7 @@ class RealesrganNcnnVulkan(Algorithm): # Read each line, query the percentage and update the progress bar output = '' bad = False - for line in iter(cast(IO[str], self.process.stderr).readline, ''): + for line in iter(cast(IO[str], self._process.stderr).readline, ''): print(line, end='') output += line res = re.match('^(\\d*.\\d+)%$', line) @@ -118,7 +122,7 @@ class RealesrganNcnnVulkan(Algorithm): # Process algorithm output if self.cancelled: return - result = self.process.poll() + result = self._process.poll() if result is None or result != 0: if result is None: result = -1 raise AlgorithmFailed(result, output) @@ -128,6 +132,6 @@ class RealesrganNcnnVulkan(Algorithm): RunAsync(run, done_callback) def cancel(self) -> None: - if self.process: + if self._process: self.cancelled = True - self.process.kill() + self._process.kill() -- GitLab From 886c069dc382bdc948717a607637d4b34563e0e9 Mon Sep 17 00:00:00 2001 From: Matteo Percivaldi Date: Mon, 29 May 2023 08:21:14 +0200 Subject: [PATCH 14/14] window: Remove model_images --- upscaler/window.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/upscaler/window.py b/upscaler/window.py index 0dad462..dbb45f1 100644 --- a/upscaler/window.py +++ b/upscaler/window.py @@ -60,12 +60,6 @@ class UpscalerWindow(Adw.ApplicationWindow): raise ValueError("Application should be passed to UpscalerWindow") self.app: Gio.Application = app - # Declare default models and variables - self.model_images = { - 'realesrgan-x4plus': _('Photo'), - 'realesrgan-x4plus-anime': _('Cartoon/Anime'), - } - self.output_file_path: Optional[str] = None self.input_file_path: Optional[str] = None content = Gdk.ContentFormats.new_for_gtype(Gio.File) -- GitLab