def button_handler_for_magiccmd_on_current_file(): # generate a magic command to submit to shell (shell will execute it) get_runner().execute_current(magiccmd) # hack to get a newline after magic command is done if get_runner().get_state() == "waiting_toplevel_command": get_workbench().get_view("ShellView").submit_command("\n")
def send_command(self, cmd): if self._proxy is None: return if not self._state_is_suitable(cmd): if isinstance(cmd, DebuggerCommand) and self.get_state() == "running": # probably waiting behind some InlineCommand self._postpone_command(cmd) return elif isinstance(cmd, InlineCommand): self._postpone_command(cmd) return else: raise AssertionError("Trying to send " + str(cmd) + " in state " + self.get_state()) if cmd.command in ("Run", "Debug", "Reset"): get_workbench().event_generate("BackendRestart") accepted = self._proxy.send_command(cmd) if (accepted and isinstance(cmd, (ToplevelCommand, DebuggerCommand, InlineCommand))): self._set_state("running") self._current_command = cmd if isinstance(cmd, ToplevelCommand): self._current_toplevel_command = cmd
def remember_recent_file(self, filename): recents = get_workbench().get_option("file.recent_files") if filename in recents: recents.remove(filename) recents.insert(0, filename) existing_recents = [name for name in recents if os.path.exists(name)] get_workbench().set_option("file.recent_files", existing_recents[:10])
def create_new_file(self): selected_path = self.get_selected_path() if not selected_path: return if os.path.isdir(selected_path): parent_path = selected_path else: parent_path = os.path.dirname(selected_path) initial_name = self.get_proposed_new_file_name(parent_path, ".py") name = askstring("File name", "Provide filename", initialvalue=initial_name, #selection_range=(0, len(initial_name)-3) ) if not name: return path = os.path.join(parent_path, name) if os.path.exists(path): showerror("Error", "The file '"+path+"' already exists") else: open(path, 'w').close() self.open_path_in_browser(path, True) get_workbench().get_editor_notebook().show_file(path)
def do_kill(): self._proc.kill() get_workbench().event_generate( "ProgramOutput", stream_name="stderr", data="KeyboardInterrupt: Forced reset") get_runner().reset_backend()
def on_double_click(self, event): path = self.get_selected_path() if os.path.isfile(path): get_workbench().get_editor_notebook().show_file(path) self.save_current_folder() elif os.path.isdir(path): self.refresh_tree(self.get_selected_node(), True)
def execute_script(self, script_path, args, working_directory=None, command_name="Run"): if (working_directory is not None and self._proxy.cwd != working_directory): # create compound command # start with %cd cmd_line = "%cd " + shlex.quote(working_directory) + "\n" next_cwd = working_directory else: # create simple command cmd_line = "" next_cwd = self._proxy.cwd # append main command (Run, run, Debug or debug) rel_filename = os.path.relpath(script_path, next_cwd) cmd_line += "%" + command_name + " " + shlex.quote(rel_filename) # append args for arg in args: cmd_line += " " + shlex.quote(arg) cmd_line += "\n" # submit to shell (shell will execute it) get_workbench().get_view("ShellView").submit_command(cmd_line)
def load_plugin(): get_workbench().add_view(HelpView, "Help", "ne") get_workbench().add_command("help_contents", "help", "Help contents", open_help, group=30)
def reset_backend(self): self.kill_backend() configuration = get_workbench().get_option("run.backend_configuration") backend_name, configuration_option = parse_configuration(configuration) backend_class = get_workbench().get_backends()[backend_name] self._proxy = backend_class(configuration_option) self.send_command(ToplevelCommand(command="Reset"))
def _perform_replace(self): #nothing is currently in found status if self.active_found_tag == None: return #get the found word bounds del_start = self.active_found_tag[0] del_end = self.active_found_tag[1] #erase all tags - these would not be correct anyway after new word is inserted self._remove_all_tags() toreplace = self.replace_entry.get().strip() #get the text to replace #delete the found word self.codeview.text.delete(del_start, del_end) #insert the new word self.codeview.text.insert(del_start, toreplace) #mark the inserted word boundaries self.last_processed_indexes = (del_start, self.codeview.text.index( "%s+%dc" % (del_start, len(toreplace)))) get_workbench().event_generate("Replace", widget=self.codeview.text, old_text=self.codeview.text.get( del_start, del_end), new_text=toreplace)
def handle_execute_from_shell(self, cmd_line): """ Handles all commands that take a filename and 0 or more extra arguments. Passes the command to backend. (Debugger plugin may also use this method) """ command, args = parse_shell_command(cmd_line) if len(args) >= 1: get_workbench().get_editor_notebook().save_all_named_editors() cmd = ToplevelCommand(command=command, filename=args[0], args=args[1:]) if os.path.isabs(cmd.filename): cmd.full_filename = cmd.filename else: cmd.full_filename = os.path.join(self.get_cwd(), cmd.filename) if command in ["Run", "run", "Debug", "debug"]: with tokenize.open(cmd.full_filename) as fp: cmd.source = fp.read() self.send_command(cmd) else: raise CommandSyntaxError("Command '%s' takes at least one argument", command)
def __init__(self, configuration_option): if configuration_option == DEFAULT_CPYTHON_INTERPRETER: self._prepare_private_venv() self._executable = get_private_venv_executable() else: self._executable = configuration_option # Rembember the usage of this non-default interpreter used_interpreters = get_workbench().get_option("run.used_interpreters") print(used_interpreters) if self._executable not in used_interpreters: used_interpreters.append(self._executable) get_workbench().set_option("run.used_interpreters", used_interpreters) cwd = get_workbench().get_option("run.working_directory") if os.path.exists(cwd): self.cwd = cwd else: self.cwd = os.path.expanduser("~") self._proc = None self._message_queue = None self._sys_path = [] self._gui_update_loop_id = None self.in_venv = None self._start_new_process()
def __init__(self, master, **kw): ttk.Frame.__init__(self, master, **kw) self.vert_scrollbar = ttk.Scrollbar(self, orient=tk.VERTICAL) self.vert_scrollbar.grid(row=0, column=2, sticky=tk.NSEW) self.text = ShellText( self, font=get_workbench().get_font("EditorFont"), #foreground="white", #background="#666666", highlightthickness=0, #highlightcolor="LightBlue", borderwidth=0, yscrollcommand=self.vert_scrollbar.set, padx=4, insertwidth=2, height=10, undo=True) get_workbench().event_generate("ShellTextCreated", text_widget=self.text) get_workbench().add_command("clear_shell", "edit", "Clear shell", self.clear_shell, group=200) self.text.grid(row=0, column=1, sticky=tk.NSEW) self.vert_scrollbar['command'] = self.text.yview self.columnconfigure(1, weight=1) self.rowconfigure(0, weight=1)
def interrupt(self): if self._proc is not None and self._proc.poll() is None: command_to_interrupt = get_runner().get_current_toplevel_command() if running_on_windows(): try: os.kill(self._proc.pid, signal.CTRL_BREAK_EVENT) # @UndefinedVariable except: logging.exception("Could not interrupt backend process") else: self._proc.send_signal(signal.SIGINT) # Tkinter programs can't be interrupted so easily: # http://stackoverflow.com/questions/13784232/keyboardinterrupt-taking-a-while # so let's chedule a hard kill in case the program refuses to be interrupted def go_hard(): if (get_runner().get_state() != "waiting_toplevel_command" and get_runner().get_current_toplevel_command() == command_to_interrupt): # still running same command self._proc.kill() get_workbench().event_generate("ProgramOutput", stream_name="stderr", data="KeyboardInterrupt: Forced reset") get_runner().reset_backend() # 100 ms was too little for Mac # 250 ms was too little for one of the Windows machines get_workbench().after(500, go_hard)
def execute_current(self, command_name, always_change_to_script_dir=False): """ This method's job is to create a command for running/debugging current file/script and submit it to shell """ editor = get_workbench().get_current_editor() if not editor: return filename = editor.get_filename(True) if not filename: return if editor.is_modified(): filename = editor.save_file() if not filename: return # changing dir may be required script_dir = os.path.realpath(os.path.dirname(filename)) if (get_workbench().get_option("run.auto_cd") and command_name[0].isupper() or always_change_to_script_dir): working_directory = script_dir else: working_directory = None self.execute_script(filename, [], working_directory, command_name)
def load_startup_files(self): """If no filename was sent from command line then load previous files (if setting allows)""" cmd_line_filenames = [ name for name in sys.argv[1:] if os.path.exists(name) ] if len(cmd_line_filenames) > 0: filenames = cmd_line_filenames elif get_workbench().get_option("file.reopen_all_files"): filenames = get_workbench().get_option("file.open_files") elif get_workbench().get_option("file.current_file"): filenames = [get_workbench().get_option("file.current_file")] else: filenames = [] if len(filenames) > 0: for filename in filenames: if os.path.exists(filename): self.show_file(filename) cur_file = get_workbench().get_option("file.current_file") # choose correct active file if len(cmd_line_filenames) > 0: self.show_file(cmd_line_filenames[0]) elif cur_file and os.path.exists(cur_file): self.show_file(cur_file) else: self._cmd_new_file() else: self._cmd_new_file() self._remember_open_files()
def __init__(self, master, frame_info): tk.Toplevel.__init__(self, master) self.transient(master) if misc_utils.running_on_windows(): self.wm_attributes('-toolwindow', 1) # TODO: take size from prefs editor_notebook = get_workbench().get_editor_notebook() if master.winfo_toplevel() == get_workbench(): position_reference = editor_notebook else: # align to previous frame position_reference = master.winfo_toplevel() self.geometry("{}x{}+{}+{}".format(editor_notebook.winfo_width(), editor_notebook.winfo_height()-20, position_reference.winfo_rootx(), position_reference.winfo_rooty())) self.protocol("WM_DELETE_WINDOW", self._on_close) self._init_layout_widgets(master, frame_info) FrameVisualizer.__init__(self, self._text_frame, frame_info) self._load_code(frame_info) self._text_frame.text.focus()
def set_colorer(self): # TODO: some problem when doing fast rewind return self.colorer = SyntaxColorer(self.code_view.text, get_workbench().get_font("EditorFont"), get_workbench().get_font("BoldEditorFont"))
def go_hard(): if (get_runner().get_state() != "waiting_toplevel_command" and get_runner().get_current_toplevel_command() == command_to_interrupt): # still running same command self._proc.kill() get_workbench().event_generate("ProgramOutput", stream_name="stderr", data="KeyboardInterrupt: Forced reset") get_runner().reset_backend()
def apply(self): if not self._configuration_variable.modified: return configuration = self._configuration_variable.get() get_workbench().set_option("run.backend_configuration", configuration) get_runner().reset_backend()
def _init_commands(self): get_workbench().add_command( "export_usage_logs", "tools", "Export usage logs...", self._cmd_export, group=110 )
def load_plugin(): def open_about(*args): AboutDialog(get_workbench(), get_workbench().get_version()) get_workbench().add_command("about", "help", "About Thonny", open_about) # For Mac get_workbench().createcommand("tkAboutDialog", open_about)
def get_credentials(): credentials = { 'address': get_workbench().get_option("ev3.ip"), 'username': get_workbench().get_option("ev3.username"), 'password': get_workbench().get_option("ev3.password") } return AttrDict(credentials)
def _load_file(self, filename): source, self.file_encoding = misc_utils.read_python_file( filename) # TODO: support also text files self._filename = filename get_workbench().event_generate("Open", editor=self, filename=filename) self._code_view.set_content(source) self._code_view.focus_set() self.master.remember_recent_file(filename)
def _create_private_venv(self, path, description, clear=False, upgrade=False): base_exe = sys.executable if sys.executable.endswith("thonny.exe"): # assuming that thonny.exe is in the same dir as "python.exe" base_exe = sys.executable.replace("thonny.exe", "python.exe") # Don't include system site packages # This way all students will have similar configuration # independently of system Python (if Thonny is used with system Python) # NB! Cant run venv.create directly, because in Windows # it tries to link venv to thonny.exe. # Need to run it via proper python cmd = [base_exe, "-m", "venv"] if clear: cmd.append("--clear") if upgrade: cmd.append("--upgrade") try: import ensurepip # @UnusedImport except ImportError: cmd.append("--without-pip") cmd.append(path) startupinfo = None if running_on_windows(): startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW proc = subprocess.Popen(cmd, startupinfo=startupinfo, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) from thonny.ui_utils import SubprocessDialog dlg = SubprocessDialog(get_workbench(), proc, "Preparing the backend", long_description=description) try: get_workbench().wait_window(dlg) except: # if using --without-pip the dialog may close very quickly # and for some reason wait_window would give error then logging.exception("Problem with waiting for venv creation dialog") get_workbench().become_topmost_window() # Otherwise focus may get stuck somewhere bindir = os.path.dirname(get_private_venv_executable()) # create private env marker marker_path = os.path.join(bindir, "is_private") with open(marker_path, mode="w") as fp: fp.write("# This file marks Thonny-private venv") # Create recommended pip conf to get rid of list deprecation warning # https://github.com/pypa/pip/issues/4058 pip_conf = "pip.ini" if running_on_windows() else "pip.conf" with open(os.path.join(path, pip_conf), mode="w") as fp: fp.write("[list]\nformat = columns") assert os.path.isdir(path)
def load_plugin(): get_workbench().add_command("autocomplete", "edit", "Auto-complete", handle_autocomplete_request, default_sequence="<Control-space>" # TODO: tester )
def _advance_background_tk_mainloop(self): """Enables running Tkinter programs which doesn't call mainloop. When mainloop is omitted, then program can be interacted with from the shell after it runs to the end. """ if self._proxy.get_state() == "waiting_toplevel_command": self._proxy.send_command(InlineCommand("tkupdate")) get_workbench().after(50, self._advance_background_tk_mainloop)
def _load_file(self, filename): with tokenize.open(filename) as fp: # TODO: support also text files source = fp.read() self._filename = filename get_workbench().event_generate("Open", editor=self, filename=filename) self._code_view.set_content(source) self._code_view.focus_set() self.master.remember_recent_file(filename)
def destroy(self): try: # Not sure if editor notebook is still living get_workbench().get_editor_notebook().unbind( "<<NotebookTabChanged>>", self._tab_changed_binding) except: pass self.vert_scrollbar["command"] = None ttk.Frame.destroy(self)
def _ok(self, event=None): for title in sorted(self._pages): try: page = self._pages[title] if page.apply() == False: return except: get_workbench().report_exception("Error when applying options in " + title) self.destroy()