class DirWidget(tk.Frame): """ Input widget for a file path """ def __init__(self, parent, *args, value=None, **kwargs): super().__init__(parent, *args, **kwargs) self.value = value self.layout() def layout(self): self.input_field = InputField(self) self.input_field.pack(side="left") self.browse_button = Button(self, text=_("Browse"), command=self.browse_button_click) self.browse_button.pack(side="left") if self.value: self._set(self.value) def get(self): return self.input_field.get() def _set(self, value): self.value = value self.input_field.delete(0, tk.END) self.input_field.insert(0, self.value) def browse_button_click(self): file_path = filedialog.askdirectory(initialdir="./", title=_("Select Directory")) self._set('"{}"'.format(file_path))
class AutomagicaIdInputWidget(tk.Frame): """ Input widget for an Automagica ID (generated by Automagica Wand) """ def __init__(self, parent, *args, value=None, **kwargs): super().__init__(parent, *args, **kwargs) self.value = value self.layout() def layout(self): self.input_field = InputField(self) self.input_field.pack(side="left") self.browse_button = Button( self, text=_("View"), command=self.view_button_click, font=(config.FONT, 10), ) self.browse_button.pack(side="left") self.record_button = Button( self, text=_("Record"), command=self.record_button_click, font=(config.FONT, 10), ) self.record_button.pack(side="left") if self.value: self._set(self.value) def get(self): return self.input_field.get() def _set(self, value): self.value = value self.input_field.delete(0, tk.END) self.input_field.insert(0, self.value) def view_button_click(self): import webbrowser automagica_id = self.get().replace('"', "") automagica_id = self.get().replace("'", "") url = os.environ.get("AUTOMAGICA_PORTAL_URL", "https://portal.automagica.com") webbrowser.open(f"{url}/ui-element/{automagica_id}") def record_button_click(self): from .windows import WandWindow def on_finish(automagica_id): self._set(f'"{automagica_id}"') _ = WandWindow(self, on_finish=on_finish)
class FilePathInputWidget(tk.Frame): """ Input widget for a file path """ def __init__(self, parent, *args, value=None, extensions=None, **kwargs): super().__init__(parent, *args, **kwargs) self.value = value if extensions: self.filetypes = ((ext, f"*.{ext}") for ext in extensions) else: self.filetypes = None self.layout() def layout(self): self.input_field = InputField(self) self.input_field.pack(side="left") self.browse_button = Button( self, text=_("Browse"), command=self.browse_button_click ) self.browse_button.pack(side="left") if self.value: self._set(self.value) def get(self): return self.input_field.get() def _set(self, value): self.value = value self.input_field.delete(0, tk.END) self.input_field.insert(0, self.value) def browse_button_click(self): if self.filetypes: file_path = filedialog.askopenfilename( initialdir="./", title=_("Select File"), filetypes=self.filetypes, ) else: file_path = filedialog.askopenfilename( initialdir="./", title=_("Select File") ) self._set('"{}"'.format(file_path))
class ConsoleFrame(tk.Frame): """ Interactive console frame """ def __init__(self, parent=None, bot=None, width=None, height=None): super().__init__(parent, width=width, height=height) self.parent = parent # Have the bot closely availble self.bot = bot # In this buffer we keep the previous commands self.buffer = [] # Console frame self.console_frame = self.create_console_frame() self.console_frame.pack(fill="both", expand=True) # Buttons in upper-right corner self.buttons_frame = self.create_buttons_frame() self.buttons_frame.place(anchor="ne", relx=1, rely=0) # Connect to the bot's logger self.logger = logging.getLogger("automagica.bot") self.logger.setLevel(logging.INFO) self.logger.addHandler(ConsoleHandler(self.write)) def on_up_press(self, event): """ When the user presses up in the command line box, the previous command should be prefilled in the command line box. """ command = self.command_entry.get() # Only do something if we have a buffer if self.buffer: try: index = self.buffer.index(command) except ValueError: index = False # Found an index? if index: buffered = self.buffer[index - 1] else: buffered = self.buffer[-1] self.command_entry.delete(0, tk.END) self.command_entry.insert(0, buffered) def create_console_frame(self): """ Console frame """ frame = tk.Frame(self, bg=config.COLOR_4) self.command_entry = InputField( frame, placeholder=_("Type command here and press <ENTER> to run..."), ) self.command_entry.configure(font=(config.FONT_MONO, "10")) self.command_entry.bind("<Return>", self.on_enter_pressed) self.command_entry.bind("<Up>", self.on_up_press) self.console_text = scrolledtext.ScrolledText( frame, wrap=tk.WORD, relief=tk.FLAT, bg=config.COLOR_5, fg=config.COLOR_1, insertbackground=config.COLOR_1, ) self.console_text.frame.configure(bd=0, highlightthickness=0, relief="ridge") self.command_entry.pack(fill="x", anchor="nw") self.console_text.pack(fill="both", padx=0, pady=0, expand=True) self.console_text.insert( "1.0", _("Welcome to Automagica Flow! \nUse this Interactive Console to your liking!\n\n" ), ) self.console_text.configure(font=(config.FONT_MONO, "10"), state="disabled") self.console_text.tag_config("error", foreground=config.COLOR_14) self.line_start = 0 return frame def create_buttons_frame(self): """ Buttons frame """ frame = tk.Frame(self, bg=config.COLOR_4) self.reset_bot_button = Button(frame, text=_("Reset Bot"), command=self.on_reset_bot_clicked) self.reset_bot_button.configure(font=(config.FONT, 8)) self.reset_bot_button.pack(side="left") self.clear_button = Button(frame, text=_("Clear Output"), command=self.on_clear_clicked) self.clear_button.configure(font=(config.FONT, 8)) self.clear_button.pack(side="left", padx=5) self.open_variable_explorer_button = Button( frame, text=_("Variable Explorer"), command=self.on_open_variable_explorer_clicked, ) self.open_variable_explorer_button.configure(font=(config.FONT, 8)) self.open_variable_explorer_button.pack(side="left") return frame def on_open_variable_explorer_clicked(self): from .windows import ( VariableExplorerWindow, ) # To avoid circular imports return VariableExplorerWindow(self, bot=self.bot) def on_reset_bot_clicked(self): self.bot.reset() def on_clear_clicked(self): self.console_text.configure(state="normal") self.console_text.delete("1.0", tk.END) self.console_text.configure(state="disabled") def destroy(self): """ Override tk.Frame.destroy """ # Additional clean-up pre-destroy self.bot.stop() self.console_text.destroy() # Call original method to destroy tk.Frame.destroy(self) def on_enter_pressed(self, e): command = self.command_entry.get() self.buffer.append(command) self.line_start += len(command) self.bot.run(command) self.command_entry.delete(0, tk.END) def write(self, record): """ Write to the console itself """ self.console_text.configure(state="normal") if record.levelname == "ERROR": self.console_text.insert(tk.END, record.getMessage() + "\n", "error") else: self.console_text.insert(tk.END, record.getMessage() + "\n") self.console_text.see(tk.END) self.console_text.configure(state="disabled")