1
0
Fork 0
mirror of https://github.com/deepfakes/faceswap synced 2025-06-07 19:05:02 -04:00
faceswap/lib/gui/options.py
torzdf 9c588045aa
GUI Updates (#940)
* lib.gui: Update icons
* tools.cli: Add video filetype flag to mask input
* lib.gui.popup_configure: Refresh GUI on config save
* lib.gui._config: Add Icon size and load last session options
* lib.gui.control_helper - Add control modification tracking
* lib.gui - Add Projects and Last Session
- Main root title handled by projects
- Add Hotkeys
- Create projects module
- split fsw and fst filetypes
- Add last_session saving

* lib.project
- Documentation
- Ask confirm on closing unsaved project
-Fixups

* Track model folder changes
- Shuffle some globals
- Activate System Verbosity for GUI

* lib.gui.utils - Documentation and clean up
- lib.gui.custom_widgets - Create and document

* Add GUI config option to disable auto loading model stats
2019-11-22 19:20:23 +00:00

277 lines
11 KiB
Python

#!/usr/bin python3
""" Cli Options for the GUI """
import inspect
from argparse import SUPPRESS
import logging
import re
from collections import OrderedDict
from lib import cli
import tools.cli as ToolsCli
from .utils import get_images
from .control_helper import ControlPanelOption
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
class CliOptions():
""" Class and methods for the command line options """
def __init__(self):
logger.debug("Initializing %s", self.__class__.__name__)
self.categories = ("faceswap", "tools")
self.commands = dict()
self.opts = dict()
self.build_options()
logger.debug("Initialized %s", self.__class__.__name__)
def build_options(self):
""" Get the commands that belong to each category """
for category in self.categories:
logger.debug("Building '%s'", category)
src = ToolsCli if category == "tools" else cli
mod_classes = self.get_cli_classes(src)
self.commands[category] = self.sort_commands(category, mod_classes)
self.opts.update(self.extract_options(src, mod_classes))
logger.debug("Built '%s'", category)
@staticmethod
def get_cli_classes(cli_source):
""" Parse the cli scripts for the argument classes """
mod_classes = list()
for name, obj in inspect.getmembers(cli_source):
if inspect.isclass(obj) and name.lower().endswith("args") \
and name.lower() not in (("faceswapargs",
"extractconvertargs",
"guiargs")):
mod_classes.append(name)
logger.debug(mod_classes)
return mod_classes
def sort_commands(self, category, classes):
""" Format classes into command names and sort:
Specific workflow order for faceswap.
Alphabetical for all others """
commands = sorted(self.format_command_name(command)
for command in classes)
if category == "faceswap":
ordered = ["extract", "train", "convert"]
commands = ordered + [command for command in commands
if command not in ordered]
logger.debug(commands)
return commands
@staticmethod
def format_command_name(classname):
""" Format args class name to command """
return classname.lower()[:-4]
def extract_options(self, cli_source, mod_classes):
""" Extract the existing ArgParse Options
into master options Dictionary """
subopts = dict()
for classname in mod_classes:
logger.debug("Processing: (classname: '%s')", classname)
command = self.format_command_name(classname)
info, options = self.get_cli_arguments(cli_source, classname, command)
options = self.process_options(options, command)
options["helptext"] = info
logger.debug("Processed: (classname: '%s', command: '%s', options: %s)",
classname, command, options)
subopts[command] = options
return subopts
@staticmethod
def get_cli_arguments(cli_source, classname, command):
""" Extract the options from the main and tools cli files """
meth = getattr(cli_source, classname)(None, command)
return meth.info, meth.argument_list + meth.optional_arguments + meth.global_arguments
def process_options(self, command_options, command):
""" Process the options for a single command """
gui_options = OrderedDict()
for opt in command_options:
logger.trace("Processing: %s", opt)
if opt.get("help", "") == SUPPRESS:
logger.trace("Skipping suppressed option: %s", opt)
continue
title = self.set_control_title(opt["opts"])
cpanel_option = ControlPanelOption(
title,
self.get_data_type(opt),
group=opt.get("group", None),
default=opt.get("default", None),
choices=opt.get("choices", None),
is_radio=opt.get("action", "") == cli.Radio,
rounding=self.get_rounding(opt),
min_max=opt.get("min_max", None),
sysbrowser=self.get_sysbrowser(opt, command_options, command),
helptext=opt["help"],
track_modified=True,
command=command)
gui_options[title] = dict(cpanel_option=cpanel_option,
opts=opt["opts"],
nargs=opt.get("nargs", None))
logger.trace("Processed: %s", gui_options)
return gui_options
@staticmethod
def set_control_title(opts):
""" Take the option switch and format it nicely """
ctltitle = opts[1] if len(opts) == 2 else opts[0]
ctltitle = ctltitle.replace("-", " ").replace("_", " ").strip().title()
return ctltitle
@staticmethod
def get_data_type(opt):
""" Return a datatype for passing into control_helper.py to get the correct control """
if opt.get("type", None) is not None and isinstance(opt["type"], type):
retval = opt["type"]
elif opt.get("action", "") in ("store_true", "store_false"):
retval = bool
else:
retval = str
return retval
@staticmethod
def get_rounding(opt):
""" Return rounding if correct data type, else None """
dtype = opt.get("type", None)
if dtype == float:
retval = opt.get("rounding", 2)
elif dtype == int:
retval = opt.get("rounding", 1)
else:
retval = None
return retval
def get_sysbrowser(self, option, options, command):
""" Return the system file browser and file types if required else None """
action = option.get("action", None)
if action not in (cli.FullPaths,
cli.DirFullPaths,
cli.FileFullPaths,
cli.FilesFullPaths,
cli.DirOrFileFullPaths,
cli.SaveFileFullPaths,
cli.ContextFullPaths):
return None
retval = dict()
action_option = None
if option.get("action_option", None) is not None:
self.expand_action_option(option, options)
action_option = option["action_option"]
retval["filetypes"] = option.get("filetypes", "default")
if action == cli.FileFullPaths:
retval["browser"] = ["load"]
elif action == cli.FilesFullPaths:
retval["browser"] = ["multi_load"]
elif action == cli.SaveFileFullPaths:
retval["browser"] = ["save"]
elif action == cli.DirOrFileFullPaths:
retval["browser"] = ["folder", "load"]
elif action == cli.ContextFullPaths and action_option:
retval["browser"] = ["context"]
retval["command"] = command
retval["action_option"] = action_option
retval["destination"] = option.get("dest", option["opts"][1].replace("--", ""))
else:
retval["browser"] = ["folder"]
logger.debug(retval)
return retval
@staticmethod
def expand_action_option(option, options):
""" Expand the action option to the full command name """
opts = {opt["opts"][0]: opt["opts"][-1]
for opt in options}
old_val = option["action_option"]
new_val = opts[old_val]
logger.debug("Updating action option from '%s' to '%s'", old_val, new_val)
option["action_option"] = new_val
def gen_command_options(self, command):
""" Yield each option for specified command """
for key, val in self.opts[command].items():
if not isinstance(val, dict):
continue
yield key, val
def options_to_process(self, command=None):
""" Return a consistent object for processing regardless of whether processing all commands
or just one command for reset and clear. Removes helptext from return value """
if command is None:
options = [opt for opts in self.opts.values()
for opt in opts.values() if isinstance(opt, dict)]
else:
options = [opt for opt in self.opts[command].values() if isinstance(opt, dict)]
return options
def reset(self, command=None):
""" Reset the options for all or passed command
back to default value """
logger.debug("Resetting options to default. (command: '%s'", command)
for option in self.options_to_process(command):
cp_opt = option["cpanel_option"]
default = "" if cp_opt.default is None else cp_opt.default
if (option.get("nargs", None)
and isinstance(default, (list, tuple))):
default = ' '.join(str(val) for val in default)
cp_opt.set(default)
def clear(self, command=None):
""" Clear the options values for all or passed commands """
logger.debug("Clearing options. (command: '%s'", command)
for option in self.options_to_process(command):
cp_opt = option["cpanel_option"]
if isinstance(cp_opt.get(), bool):
cp_opt.set(False)
elif isinstance(cp_opt.get(), (int, float)):
cp_opt.set(0)
else:
cp_opt.set("")
def get_option_values(self, command=None):
""" Return all or single command control titles with the associated tk_var value """
ctl_dict = dict()
for cmd, opts in self.opts.items():
if command and command != cmd:
continue
cmd_dict = dict()
for key, val in opts.items():
if not isinstance(val, dict):
continue
cmd_dict[key] = val["cpanel_option"].get()
ctl_dict[cmd] = cmd_dict
logger.debug("command: '%s', ctl_dict: %s", command, ctl_dict)
return ctl_dict
def get_one_option_variable(self, command, title):
""" Return a single tk_var for the specified
command and control_title """
for opt_title, option in self.gen_command_options(command):
if opt_title == title:
return option["cpanel_option"].tk_var
return None
def gen_cli_arguments(self, command):
""" Return the generated cli arguments for the selected command """
for _, option in self.gen_command_options(command):
optval = str(option["cpanel_option"].get())
opt = option["opts"][0]
if command in ("extract", "convert") and opt == "-o":
get_images().set_faceswap_output_path(optval)
if optval in ("False", ""):
continue
elif optval == "True":
yield (opt, )
else:
if option.get("nargs", None):
if "\"" in optval:
optval = [arg[1:-1] for arg in re.findall(r"\".+?\"", optval)]
else:
optval = optval.split(" ")
opt = [opt] + optval
else:
opt = (opt, optval)
yield opt