#!/usr/bin python3 """ The command frame for Faceswap GUI """ import tkinter as tk from tkinter import ttk from .options import Config from .tooltip import Tooltip from .utils import Images, FileHandler class CommandNotebook(ttk.Notebook): """ Frame to hold each individual tab of the command notebook """ def __init__(self, parent, cli_options, tk_vars, scaling_factor): width = int(420 * scaling_factor) height = int(500 * scaling_factor) ttk.Notebook.__init__(self, parent, width=width, height=height) parent.add(self) self.cli_opts = cli_options self.tk_vars = tk_vars self.actionbtns = dict() self.set_running_task_trace() self.build_tabs() def set_running_task_trace(self): """ Set trigger action for the running task to change the action buttons text and command """ self.tk_vars["runningtask"].trace("w", self.change_action_button) def build_tabs(self): """ Build the tabs for the relevant command """ for category in self.cli_opts.categories: cmdlist = self.cli_opts.commands[category] for command in cmdlist: title = command.title() commandtab = CommandTab(self, category, command) self.add(commandtab, text=title) def change_action_button(self, *args): """ Change the action button to relevant control """ for cmd in self.actionbtns.keys(): btnact = self.actionbtns[cmd] if self.tk_vars["runningtask"].get(): ttl = "Terminate" hlp = "Exit the running process" else: ttl = cmd.title() hlp = "Run the {} script".format(cmd.title()) btnact.config(text=ttl) Tooltip(btnact, text=hlp, wraplength=200) class CommandTab(ttk.Frame): """ Frame to hold each individual tab of the command notebook """ def __init__(self, parent, category, command): ttk.Frame.__init__(self, parent) self.category = category self.cli_opts = parent.cli_opts self.actionbtns = parent.actionbtns self.tk_vars = parent.tk_vars self.command = command self.build_tab() def build_tab(self): """ Build the tab """ OptionsFrame(self) self.add_frame_separator() ActionFrame(self) def add_frame_separator(self): """ Add a separator between top and bottom frames """ sep = ttk.Frame(self, height=2, relief=tk.RIDGE) sep.pack(fill=tk.X, pady=(5, 0), side=tk.TOP) class OptionsFrame(ttk.Frame): """ Options Frame - Holds the Options for each command """ def __init__(self, parent): ttk.Frame.__init__(self, parent) self.pack(side=tk.TOP, fill=tk.BOTH, expand=True) self.opts = parent.cli_opts self.command = parent.command self.canvas = tk.Canvas(self, bd=0, highlightthickness=0) self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.optsframe = ttk.Frame(self.canvas) self.optscanvas = self.canvas.create_window((0, 0), window=self.optsframe, anchor=tk.NW) self.chkbtns = self.checkbuttons_frame() self.build_frame() self.opts.set_context_option(self.command) def checkbuttons_frame(self): """ Build and format frame for holding the check buttons """ container = ttk.Frame(self.optsframe) lbl = ttk.Label(container, text="Options", width=16, anchor=tk.W) lbl.pack(padx=5, pady=5, side=tk.LEFT, anchor=tk.N) chkframe = ttk.Frame(container) chkframe.pack(side=tk.BOTTOM, expand=True) chkleft = ttk.Frame(chkframe, name="leftFrame") chkleft.pack(side=tk.LEFT, anchor=tk.N, expand=True) chkright = ttk.Frame(chkframe, name="rightFrame") chkright.pack(side=tk.RIGHT, anchor=tk.N, expand=True) return container, chkframe def build_frame(self): """ Build the options frame for this command """ self.add_scrollbar() self.canvas.bind("", self.resize_frame) for option in self.opts.gen_command_options(self.command): optioncontrol = OptionControl(self.command, option, self.optsframe, self.chkbtns[1]) optioncontrol.build_full_control() if self.chkbtns[1].winfo_children(): self.chkbtns[0].pack(side=tk.BOTTOM, fill=tk.X, expand=True) def add_scrollbar(self): """ Add a scrollbar to the options frame """ scrollbar = ttk.Scrollbar(self, command=self.canvas.yview) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.canvas.config(yscrollcommand=scrollbar.set) self.optsframe.bind("", self.update_scrollbar) def update_scrollbar(self, event): """ Update the options frame scrollbar """ self.canvas.configure(scrollregion=self.canvas.bbox("all")) def resize_frame(self, event): """ Resize the options frame to fit the canvas """ canvas_width = event.width self.canvas.itemconfig(self.optscanvas, width=canvas_width) class OptionControl(object): """ Build the correct control for the option parsed and place it on the frame """ def __init__(self, command, option, option_frame, checkbuttons_frame): self.command = command self.option = option self.option_frame = option_frame self.chkbtns = checkbuttons_frame def build_full_control(self): """ Build the correct control type for the option passed through """ ctl = self.option["control"] ctltitle = self.option["control_title"] sysbrowser = self.option["filesystem_browser"] ctlhelp = " ".join(self.option.get("help", "").split()) ctlhelp = ". ".join(i.capitalize() for i in ctlhelp.split(". ")) ctlhelp = ctltitle + " - " + ctlhelp dflt = self.option.get("default", "") if self.option.get("nargs", None) and isinstance(dflt, (list, tuple)): dflt = ' '.join(str(val) for val in dflt) if ctl == ttk.Checkbutton: dflt = self.option.get("default", False) choices = self.option["choices"] if ctl == ttk.Combobox else None ctlframe = self.build_one_control_frame() if ctl != ttk.Checkbutton: self.build_one_control_label(ctlframe, ctltitle) ctlvars = (ctl, ctltitle, dflt, ctlhelp) self.option["value"] = self.build_one_control(ctlframe, ctlvars, choices, sysbrowser) def build_one_control_frame(self): """ Build the frame to hold the control """ frame = ttk.Frame(self.option_frame) frame.pack(fill=tk.X, expand=True) return frame @staticmethod def build_one_control_label(frame, control_title): """ Build and place the control label """ lbl = ttk.Label(frame, text=control_title, width=16, anchor=tk.W) lbl.pack(padx=5, pady=5, side=tk.LEFT, anchor=tk.N) def build_one_control(self, frame, controlvars, choices, sysbrowser): """ Build and place the option controls """ control, control_title, default, helptext = controlvars default = default if default is not None else "" var = tk.BooleanVar( frame) if control == ttk.Checkbutton else tk.StringVar(frame) var.set(default) if sysbrowser is not None: self.add_browser_buttons(frame, sysbrowser, var) if control == ttk.Checkbutton: self.checkbutton_to_checkframe(control, control_title, var, helptext) else: self.control_to_optionsframe(control, frame, var, choices, helptext) return var def checkbutton_to_checkframe(self, control, control_title, var, helptext): """ Add checkbuttons to the checkbutton frame """ leftframe = self.chkbtns.children["leftFrame"] rightframe = self.chkbtns.children["rightFrame"] chkbtn_count = len({**leftframe.children, **rightframe.children}) frame = leftframe if chkbtn_count % 2 == 0 else rightframe ctl = control(frame, variable=var, text=control_title) ctl.pack(side=tk.TOP, padx=5, pady=5, anchor=tk.W) Tooltip(ctl, text=helptext, wraplength=200) @staticmethod def control_to_optionsframe(control, frame, var, choices, helptext): """ Standard non-check buttons sit in the main options frame """ ctl = control(frame, textvariable=var) ctl.pack(padx=5, pady=5, fill=tk.X, expand=True) if control == ttk.Combobox: ctl["values"] = [choice for choice in choices] Tooltip(ctl, text=helptext, wraplength=200) def add_browser_buttons(self, frame, sysbrowser, filepath): """ Add correct file browser button for control """ img = Images().icons[sysbrowser] action = getattr(self, "ask_" + sysbrowser) filetypes = self.option.get("filetypes", "default") fileopn = ttk.Button(frame, image=img, command=lambda cmd=action: cmd(filepath, filetypes)) fileopn.pack(padx=(0, 5), side=tk.RIGHT) @staticmethod def ask_folder(filepath, filetypes=None): """ Pop-up to get path to a directory :param filepath: tkinter StringVar object that will store the path to a directory. :param filetypes: Unused argument to allow filetypes to be given in ask_load(). """ dirname = FileHandler("dir", filetypes).retfile if dirname: filepath.set(dirname) @staticmethod def ask_load(filepath, filetypes): """ Pop-up to get path to a file """ filename = FileHandler("filename", filetypes).retfile if filename: filepath.set(filename) @staticmethod def ask_save(filepath, filetypes=None): """ Pop-up to get path to save a new file """ filename = FileHandler("savefilename", filetypes).retfile if filename: filepath.set(filename) @staticmethod def ask_nothing(filepath, filetypes=None): """ Method that does nothing, used for disabling open/save pop up """ return def ask_context(self, filepath, filetypes): """ Method to pop the correct dialog depending on context """ selected_action = self.option["action_option"].get() selected_variable = self.option["dest"] filename = FileHandler("context", filetypes, command=self.command, action=selected_action, variable=selected_variable).retfile if filename: filepath.set(filename) class ActionFrame(ttk.Frame): """Action Frame - Displays action controls for the command tab """ def __init__(self, parent): ttk.Frame.__init__(self, parent) self.pack(fill=tk.BOTH, padx=5, pady=5, side=tk.BOTTOM, anchor=tk.N) self.command = parent.command self.title = self.command.title() self.add_action_button(parent.category, parent.actionbtns, parent.tk_vars) self.add_util_buttons(parent.cli_opts, parent.tk_vars) def add_action_button(self, category, actionbtns, tk_vars): """ Add the action buttons for page """ actframe = ttk.Frame(self) actframe.pack(fill=tk.X, side=tk.LEFT) var_value = "{},{}".format(category, self.command) btnact = ttk.Button(actframe, text=self.title, width=10, command=lambda: tk_vars["action"].set(var_value)) btnact.pack(side=tk.LEFT) Tooltip(btnact, text="Run the {} script".format(self.title), wraplength=200) actionbtns[self.command] = btnact btngen = ttk.Button(actframe, text="Generate", width=10, command=lambda: tk_vars["generate"].set(var_value)) btngen.pack(side=tk.RIGHT, padx=5) Tooltip(btngen, text="Output command line options to the console", wraplength=200) def add_util_buttons(self, cli_options, tk_vars): """ Add the section utility buttons """ utlframe = ttk.Frame(self) utlframe.pack(side=tk.RIGHT) config = Config(cli_options, tk_vars) for utl in ("load", "save", "clear", "reset"): img = Images().icons[utl] action_cls = config if utl in (("save", "load")) else cli_options action = getattr(action_cls, utl) btnutl = ttk.Button(utlframe, image=img, command=lambda cmd=action: cmd(self.command)) btnutl.pack(padx=2, side=tk.LEFT) Tooltip(btnutl, text=utl.capitalize() + " " + self.title + " config", wraplength=200)