mirror of
https://github.com/deepfakes/faceswap
synced 2025-06-08 20:13:52 -04:00
Dependency Updater: Improve by pluging in to setup.py setup.py: Bugfix handling of Conda aliases GUI: Revert console background colour sysinfo: Handle errors in obtaining information
260 lines
10 KiB
Python
260 lines
10 KiB
Python
#!/usr/bin python3
|
|
""" The Menu Bars for faceswap GUI """
|
|
|
|
import locale
|
|
import logging
|
|
import os
|
|
import sys
|
|
import tkinter as tk
|
|
|
|
from importlib import import_module
|
|
from subprocess import Popen, PIPE, STDOUT
|
|
|
|
from lib.multithreading import MultiThread
|
|
from lib.Serializer import JSONSerializer
|
|
|
|
import update_deps
|
|
from .utils import get_config
|
|
from .popup_configure import popup_config
|
|
|
|
|
|
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
|
|
|
|
|
|
class MainMenuBar(tk.Menu): # pylint:disable=too-many-ancestors
|
|
""" GUI Main Menu Bar """
|
|
def __init__(self, master=None):
|
|
logger.debug("Initializing %s", self.__class__.__name__)
|
|
super().__init__(master)
|
|
self.root = master
|
|
|
|
self.file_menu = FileMenu(self)
|
|
self.edit_menu = tk.Menu(self, tearoff=0)
|
|
self.tools_menu = ToolsMenu(self)
|
|
|
|
self.add_cascade(label="File", menu=self.file_menu, underline=0)
|
|
self.build_edit_menu()
|
|
self.add_cascade(label="Tools", menu=self.tools_menu, underline=0)
|
|
logger.debug("Initialized %s", self.__class__.__name__)
|
|
|
|
def build_edit_menu(self):
|
|
""" Add the edit menu to the menu bar """
|
|
logger.debug("Building Edit menu")
|
|
configs = self.scan_for_configs()
|
|
for name in sorted(list(configs.keys())):
|
|
label = "Configure {} Plugins...".format(name.title())
|
|
config = configs[name]
|
|
self.edit_menu.add_command(
|
|
label=label,
|
|
underline=10,
|
|
command=lambda conf=(name, config), root=self.root: popup_config(conf, root))
|
|
self.add_cascade(label="Edit", menu=self.edit_menu, underline=0)
|
|
logger.debug("Built Edit menu")
|
|
|
|
def scan_for_configs(self):
|
|
""" Scan for config.ini file locations """
|
|
root_path = os.path.abspath(os.path.dirname(sys.argv[0]))
|
|
plugins_path = os.path.join(root_path, "plugins")
|
|
logger.debug("Scanning path: '%s'", plugins_path)
|
|
configs = dict()
|
|
for dirpath, _, filenames in os.walk(plugins_path):
|
|
if "_config.py" in filenames:
|
|
plugin_type = os.path.split(dirpath)[-1]
|
|
config = self.load_config(plugin_type)
|
|
configs[plugin_type] = config
|
|
logger.debug("Configs loaded: %s", sorted(list(configs.keys())))
|
|
return configs
|
|
|
|
@staticmethod
|
|
def load_config(plugin_type):
|
|
""" Load the config to generate config file if it doesn't exist and get filename """
|
|
# Load config to generate default if doesn't exist
|
|
mod = ".".join(("plugins", plugin_type, "_config"))
|
|
module = import_module(mod)
|
|
config = module.Config(None)
|
|
logger.debug("Found '%s' config at '%s'", plugin_type, config.configfile)
|
|
return config
|
|
|
|
|
|
class FileMenu(tk.Menu): # pylint:disable=too-many-ancestors
|
|
""" File menu items and functions """
|
|
def __init__(self, parent):
|
|
logger.debug("Initializing %s", self.__class__.__name__)
|
|
super().__init__(parent, tearoff=0)
|
|
self.root = parent.root
|
|
self.config = get_config()
|
|
self.recent_menu = tk.Menu(self, tearoff=0, postcommand=self.refresh_recent_menu)
|
|
self.build()
|
|
logger.debug("Initialized %s", self.__class__.__name__)
|
|
|
|
def build(self):
|
|
""" Add the file menu to the menu bar """
|
|
logger.debug("Building File menu")
|
|
self.add_command(label="Load full config...", underline=0, command=self.config.load)
|
|
self.add_command(label="Save full config...", underline=0, command=self.config.save)
|
|
self.add_separator()
|
|
self.add_cascade(label="Open recent", underline=6, menu=self.recent_menu)
|
|
self.add_separator()
|
|
self.add_command(label="Reset all to default",
|
|
underline=0,
|
|
command=self.config.cli_opts.reset)
|
|
self.add_command(label="Clear all", underline=0, command=self.config.cli_opts.clear)
|
|
self.add_separator()
|
|
self.add_command(label="Quit", underline=0, command=self.root.close_app)
|
|
logger.debug("Built File menu")
|
|
|
|
def build_recent_menu(self):
|
|
""" Load recent files into menu bar """
|
|
logger.debug("Building Recent Files menu")
|
|
serializer = JSONSerializer
|
|
menu_file = os.path.join(self.config.pathcache, ".recent.json")
|
|
if not os.path.isfile(menu_file) or os.path.getsize(menu_file) == 0:
|
|
self.clear_recent_files(serializer, menu_file)
|
|
with open(menu_file, "rb") as inp:
|
|
recent_files = serializer.unmarshal(inp.read().decode("utf-8"))
|
|
logger.debug("Loaded recent files: %s", recent_files)
|
|
for recent_item in recent_files:
|
|
filename, command = recent_item
|
|
logger.debug("processing: ('%s', %s)", filename, command)
|
|
lbl_command = command if command else "All"
|
|
self.recent_menu.add_command(
|
|
label="{} ({})".format(filename, lbl_command.title()),
|
|
command=lambda fnm=filename, cmd=command: self.config.load(cmd, fnm))
|
|
self.recent_menu.add_separator()
|
|
self.recent_menu.add_command(
|
|
label="Clear recent files",
|
|
underline=0,
|
|
command=lambda srl=serializer, mnu=menu_file: self.clear_recent_files(srl, mnu))
|
|
|
|
logger.debug("Built Recent Files menu")
|
|
|
|
@staticmethod
|
|
def clear_recent_files(serializer, menu_file):
|
|
""" Creates or clears recent file list """
|
|
logger.debug("clearing recent files list: '%s'", menu_file)
|
|
recent_files = serializer.marshal(list())
|
|
with open(menu_file, "wb") as out:
|
|
out.write(recent_files.encode("utf-8"))
|
|
|
|
def refresh_recent_menu(self):
|
|
""" Refresh recent menu on save/load of files """
|
|
self.recent_menu.delete(0, "end")
|
|
self.build_recent_menu()
|
|
|
|
|
|
class ToolsMenu(tk.Menu): # pylint:disable=too-many-ancestors
|
|
""" Tools menu items and functions """
|
|
def __init__(self, parent):
|
|
logger.debug("Initializing %s", self.__class__.__name__)
|
|
super().__init__(parent, tearoff=0)
|
|
self.root = parent.root
|
|
self.build()
|
|
logger.debug("Initialized %s", self.__class__.__name__)
|
|
|
|
def build(self):
|
|
""" Build the tools menu """
|
|
logger.debug("Building Tools menu")
|
|
self.add_command(label="Check for updates...",
|
|
underline=0,
|
|
command=lambda action="update": self.in_thread(action))
|
|
self.add_command(label="Output System Information",
|
|
underline=0,
|
|
command=lambda action="output_sysinfo": self.in_thread(action))
|
|
logger.debug("Built Tools menu")
|
|
|
|
def in_thread(self, action):
|
|
""" Perform selected action inside a thread """
|
|
logger.debug("Performing tools action: %s", action)
|
|
thread = MultiThread(getattr(self, action), thread_count=1)
|
|
thread.start()
|
|
logger.debug("Performed tools action: %s", action)
|
|
|
|
@staticmethod
|
|
def clear_console():
|
|
""" Clear the console window """
|
|
get_config().tk_vars["consoleclear"].set(True)
|
|
|
|
def output_sysinfo(self):
|
|
""" Output system information to console """
|
|
logger.debug("Obtaining system information")
|
|
self.root.config(cursor="watch")
|
|
self.clear_console()
|
|
print("Obtaining system information...")
|
|
from lib.sysinfo import sysinfo
|
|
info = sysinfo
|
|
self.clear_console()
|
|
logger.debug("Obtained system information: %s", info)
|
|
print(info)
|
|
self.root.config(cursor="")
|
|
|
|
def update(self):
|
|
""" Check for updates and clone repo """
|
|
logger.debug("Updating Faceswap...")
|
|
self.root.config(cursor="watch")
|
|
encoding = locale.getpreferredencoding()
|
|
logger.debug("Encoding: %s", encoding)
|
|
success = False
|
|
if self.check_for_updates(encoding):
|
|
success = self.do_update(encoding)
|
|
update_deps.main(logger=logger)
|
|
if success:
|
|
logger.info("Please restart Faceswap to complete the update.")
|
|
self.root.config(cursor="")
|
|
|
|
@staticmethod
|
|
def check_for_updates(encoding):
|
|
""" Check whether an update is required """
|
|
# Do the check
|
|
logger.info("Checking for updates...")
|
|
update = False
|
|
msg = ""
|
|
gitcmd = "git remote update && git status -uno"
|
|
cmd = Popen(gitcmd, shell=True, stdout=PIPE, stderr=STDOUT)
|
|
stdout, _ = cmd.communicate()
|
|
retcode = cmd.poll()
|
|
logger.debug("'%s' output: %s", gitcmd, stdout.decode(encoding))
|
|
logger.debug("'%s' returncode: %s", gitcmd, retcode)
|
|
if retcode != 0:
|
|
msg = ("Git is not installed or you are not running a cloned repo. "
|
|
"Unable to check for updates")
|
|
else:
|
|
chk = stdout.decode(encoding).splitlines()
|
|
for line in chk:
|
|
if line.lower().startswith("your branch is ahead"):
|
|
msg = "Your branch is ahead of the remote repo. Not updating"
|
|
break
|
|
if line.lower().startswith("your branch is up to date"):
|
|
msg = "Faceswap is up to date."
|
|
break
|
|
if line.lower().startswith("your branch is behind"):
|
|
update = True
|
|
break
|
|
if "have diverged" in line.lower():
|
|
msg = "Your branch has diverged from the remote repo. Not updating"
|
|
break
|
|
if not update:
|
|
logger.info(msg)
|
|
logger.debug("Checked for update. Update required: %s", update)
|
|
return update
|
|
|
|
@staticmethod
|
|
def do_update(encoding):
|
|
""" Update Faceswap """
|
|
logger.info("A new version is available. Updating...")
|
|
gitcmd = "git pull"
|
|
cmd = Popen(gitcmd, shell=True, stdout=PIPE, stderr=STDOUT, bufsize=1)
|
|
while True:
|
|
output = cmd.stdout.readline().decode(encoding)
|
|
if output == "" and cmd.poll() is not None:
|
|
break
|
|
if output:
|
|
logger.debug("'%s' output: '%s'", gitcmd, output.strip())
|
|
print(output.strip())
|
|
retcode = cmd.poll()
|
|
logger.debug("'%s' returncode: %s", gitcmd, retcode)
|
|
if retcode != 0:
|
|
logger.info("An error occurred during update. return code: %s", retcode)
|
|
retval = False
|
|
else:
|
|
retval = True
|
|
return retval
|