def _create_widgets(self, parent): bg = "#ffff99" banner = tk.Label(parent, background=bg) banner.grid(row=0, column=0, sticky="nsew") banner_msg = (_( "This dialog is for managing Thonny plug-ins and their dependencies.\n" + "If you want to install packages for your own programs then choose 'Tools → Manage packages...'" ) + "\n") runner = get_runner() if (runner is not None and runner.get_local_executable() is not None and is_same_path(self._get_interpreter(), get_runner().get_local_executable())): banner_msg += (_( "(In this case Thonny's back-end uses same interpreter, so both dialogs manage same packages.)" ) + "\n") banner_msg += "\n" + _( "NB! You need to restart Thonny after installing / upgrading / uninstalling a plug-in." ) banner_text = tk.Label(banner, text=banner_msg, background=bg, justify="left") banner_text.grid(pady=10, padx=10) PipDialog._create_widgets(self, parent)
def write_remote_file(self, save_filename, content_bytes, save_copy): if get_runner().ready_for_remote_file_operations(show_message=True): target_filename = extract_target_path(save_filename) get_runner().send_command( InlineCommand( "write_file", path=target_filename, content_bytes=content_bytes, editor_id=id(self), blocking=True, description=_("Saving") + "...", )) if not save_copy: self._code_view.text.edit_modified(False) self.update_title() # NB! edit_modified is not falsed yet! get_workbench().event_generate("RemoteFileOperation", path=target_filename, operation="save") return True else: return False
def check_issue_command(self, command, **kwargs): cmd = DebuggerCommand(command, **kwargs) self._last_debugger_command = cmd if get_runner().is_waiting_debugger_command(): logging.debug("_check_issue_debugger_command: %s", cmd) # tell VM the state we are seeing cmd.setdefault( frame_id=self._last_progress_message.stack[-1].id, breakpoints=self.get_effective_breakpoints(command), state=self._last_progress_message.stack[-1].event, focus=self._last_progress_message.stack[-1].focus, allow_stepping_into_libraries=get_workbench().get_option( "debugger.allow_stepping_into_libraries"), ) if command == "run_to_cursor": # cursor position was added as another breakpoint cmd.name = "resume" get_runner().send_command(cmd) if command == "resume": self.clear_last_frame() else: logging.debug("Bad state for sending debugger command " + str(command))
def check_upload_download_response(command_name, command_response): if command_response and command_response.get("existing_files"): # command was not performed because overwriting existing files need confirmation existing = sorted(command_response["existing_files"][:25]) if len(command_response["existing_files"]) > 25: existing.append("...") user_response = messagebox.askokcancel( "Overwriting", "Some file(s) will be overwritten:\n\n" + " " + "\n ".join(existing), icon="info", ) if not user_response: return else: get_runner().send_command( InlineCommand( command_name, allow_overwrite=True, source_paths=command_response["source_paths"], target_dir=command_response["target_dir"], blocking=True, description=command_response["description"], ) )
def _request_debug(command_name): # Don't assume Debug command gets issued after this # This may just call the %cd command # or the user may deny saving current editor if get_workbench().in_simple_mode(): get_workbench().show_view("VariablesView") get_runner().execute_current(command_name)
def _start_update_list(self, name_to_show=None): assert self._get_state() in [None, "idle"] self._set_state("listing") get_workbench().bind("get_active_distributions_response", self._complete_update_list, True) self._last_name_to_show = name_to_show get_runner().send_command(InlineCommand("get_active_distributions"))
def handle_frame_click(event, frame_id=frame_id, filename=filename, lineno=lineno): get_runner().send_command( InlineCommand("get_frame_info", frame_id=frame_id)) if os.path.exists(filename): get_workbench().get_editor_notebook().show_file( filename, lineno, set_focus=False)
def choose_node_for_file_operations(master, prompt): if get_runner().supports_remote_files(): dlg = NodeChoiceDialog(master, prompt) show_dialog(dlg, master) if dlg.result == "remote" and not get_runner().ready_for_remote_file_operations( show_message=True ): return None return dlg.result else: return "local"
def handle_autocomplete_request(self): row, column = self._get_position() source = self.text.get("1.0", "end-1c") get_runner().send_command( InlineCommand( "editor_autocomplete", source=source, row=row, column=column, filename=self._get_filename(), ))
def __init__(self, master, kind, initial_dir): super().__init__(master=master) self.result = None self.updating_selection = False self.kind = kind if kind == "open": self.title("Open from " + get_runner().get_node_label()) else: assert kind == "save" self.title("Save to " + get_runner().get_node_label()) background = ttk.Frame(self) background.grid(row=0, column=0, sticky="nsew") self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) self.browser = DialogRemoteFileBrowser(background, self) self.browser.grid(row=0, column=0, columnspan=4, sticky="nsew", pady=20, padx=20) self.browser.configure(borderwidth=1, relief="groove") self.browser.tree.configure(selectmode="browse") self.name_label = ttk.Label(background, text="File name:") self.name_label.grid(row=1, column=0, pady=(0, 20), padx=20, sticky="w") self.name_var = create_string_var("") self.name_entry = ttk.Entry( background, textvariable=self.name_var, state="normal" if kind == "save" else "disabled" ) self.name_entry.grid(row=1, column=1, pady=(0, 20), padx=(0, 20), sticky="we") self.name_entry.bind("<KeyRelease>", self.on_name_edit, True) self.ok_button = ttk.Button(background, text="OK", command=self.on_ok) self.ok_button.grid(row=1, column=2, pady=(0, 20), padx=(0, 20), sticky="e") self.cancel_button = ttk.Button(background, text="Cancel", command=self.on_cancel) self.cancel_button.grid(row=1, column=3, pady=(0, 20), padx=(0, 20), sticky="e") background.rowconfigure(0, weight=1) background.columnconfigure(1, weight=1) self.bind("<Escape>", self.on_cancel, True) self.bind("<Return>", self.on_ok, True) self.protocol("WM_DELETE_WINDOW", self.on_cancel) self.tree_select_handler_id = self.browser.tree.bind( "<<TreeviewSelect>>", self.on_tree_select, True ) self.browser.request_focus_into(initial_dir)
def request_focus_into(self, path): if not get_runner().ready_for_remote_file_operations(show_message=True): return False super().request_focus_into(path) if not get_runner().supports_remote_directories(): assert path == "" self.focus_into(path) elif self.current_focus == path: # refreshes self.focus_into(path) else: self.request_new_focus(path)
def upload(): selection = self.get_selection_info(True) if not selection: return if "dir" in selection["kinds"] and not proxy.supports_remote_directories(): messagebox.showerror( "Can't upload directory", "%s does not support directories.\n" % proxy.get_node_label() + "You can only upload files.", ) else: response = get_runner().send_command( InlineCommand( "upload", allow_overwrite=False, source_paths=selection["paths"], target_dir=target_dir, blocking=True, description=_("Uploading %s to %s") % (selection["description"], target_dir), ) ) check_upload_download_response("upload", response) self.master.remote_files.refresh_tree()
def supports_directories(self): runner = get_runner() if not runner: return False proxy = runner.get_backend_proxy() if not proxy: return False return proxy.supports_remote_directories()
def apply(self): if self._current_page is None: return None result = self._current_page.apply() if result is False: return False backend_desc = self._combo_variable.get() backend_name = self._backend_specs_by_desc[backend_desc].name get_workbench().set_option("run.backend_name", backend_name) if getattr(self._combo_variable, "modified") or self._current_page.should_restart(): get_runner().restart_backend(False) return None
def ask_backend_path(master, dialog_kind): proxy = get_runner().get_backend_proxy() if not proxy: return None assert proxy.supports_remote_files() dlg = BackendFileDialog(master, dialog_kind, proxy.get_cwd()) show_dialog(dlg, master) return dlg.result
def command_enabled(self, command): if not get_runner().is_waiting_debugger_command(): return False if command == "run_to_cursor": return self.get_run_to_cursor_breakpoint() is not None elif command == "step_back": return (self._last_progress_message and self._last_progress_message["tracer_class"] == "NiceTracer") else: return True
def request_focus_into(self, path): if path == "": if running_on_windows(): # list of drives, can't cd return self.focus_into(path) else: path = "/" if not os.path.isdir(path): return proxy = get_runner().get_backend_proxy() if ( proxy and proxy.uses_local_filesystem() and proxy.get_cwd() != path and get_runner().is_waiting_toplevel_command() ): get_shell().submit_magic_command(construct_cd_command(path)) else: # it's OK, if it's already focused into this directory # focus again to refresh self.focus_into(path) get_workbench().set_local_cwd(path)
def _update_run_or_resume_button(): if not get_workbench().in_simple_mode(): return state = get_runner().get_state() if state == "waiting_debugger_command": caption = RESUME_COMMAND_CAPTION image = get_workbench().get_image("resume") elif state == "waiting_toplevel_command": caption = running.RUN_COMMAND_CAPTION image = get_workbench().get_image("run-current-script") else: return button = get_workbench().get_toolbar_button("runresume") button.configure(text=caption, image=image)
def download(): selection = self.get_selection_info(True) if not selection: return response = get_runner().send_command( InlineCommand( "download", allow_overwrite=False, source_paths=selection["paths"], target_dir=target_dir, blocking=True, description=_("Downloading %s to %s") % (selection["description"], target_dir), ) ) check_upload_download_response("download", response) self.master.local_files.refresh_tree()
def handle_toplevel_response(self, msg: ToplevelResponse) -> None: # Can be called by event system or by Workbench # (if Assistant wasn't created yet but an error came) if not msg.get("user_exception") and msg.get("command_name") in [ "execute_system_command", "execute_source", ]: # Shell commands may be used to investigate the problem, don't clear assistance return self._clear() if not isinstance(get_runner().get_backend_proxy(), CPythonProxy): return # prepare for snapshot key = msg.get("filename", "<pyshell>") self._current_snapshot = { "timestamp": datetime.datetime.now().isoformat()[:19], "main_file_path": key, } self._snapshots_per_main_file.setdefault(key, []) self._snapshots_per_main_file[key].append(self._current_snapshot) if msg.get("user_exception"): if not msg["user_exception"].get("message", None): msg["user_exception"]["message"] = "<no message>" self._exception_info = msg["user_exception"] self._explain_exception(msg["user_exception"]) if get_workbench().get_option( "assistance.open_assistant_on_errors"): get_workbench().show_view("AssistantView", set_focus=False) else: self._exception_info = None if msg.get("filename") and os.path.exists(msg["filename"]): self.main_file_path = msg["filename"] source = read_source(msg["filename"]) self._start_program_analyses( msg["filename"], source, _get_imported_user_files(msg["filename"], source)) else: self.main_file_path = None self._present_conclusion()
def _get_3rd_party_modules(self): proxy = get_runner().get_backend_proxy() if not isinstance(proxy, CPythonProxy): return [] try: sys_path = proxy.get_sys_path() except Exception: logging.exception("Can't get sys path from proxy") return [] module_names = set() for item in sys_path: if os.path.isdir(item) and ("site-packages" in item or "dist-packages" in item): module_names.update(self._get_module_names(item)) for name in os.listdir(item): if "-" not in name: module_names.add(name.replace(".py", "")) return module_names
def reset_remote(self, msg=None): runner = get_runner() if not runner: return proxy = runner.get_backend_proxy() if not proxy: self.hide_remote() return if proxy.supports_remote_files(): # remote pane is needed if not self.remote_added: self.add(self.remote_files, before=self.local_files, minsize=minsize) self.remote_added = True self.restore_split() self.remote_files.clear() self.remote_files.check_update_focus() else: # remote pane not needed self.hide_remote()
def __init__(self, master, prompt): super().__init__(master=master) self.result = None self.title(prompt) background = ttk.Frame(self) background.grid(row=0, column=0, sticky="nsew") self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) local_caption = LOCAL_FILES_ROOT_TEXT remote_caption = get_runner().get_node_label() button_width = max(len(local_caption), len(remote_caption)) + 10 self.local_button = ttk.Button( background, text=" \n" + local_caption + "\n ", width=button_width, command=self.on_local, ) self.local_button.grid(row=0, column=0, pady=20, padx=20) self.remote_button = ttk.Button( background, text=" \n" + remote_caption + "\n ", width=button_width, command=self.on_remote, ) self.remote_button.grid(row=1, column=0, pady=(0, 20), padx=20) self.local_button.focus_set() self.bind("<Escape>", self.on_cancel, True) self.bind("<Return>", self.on_return, True) self.bind("<Down>", self.on_down, True) self.bind("<Up>", self.on_up, True) self.protocol("WM_DELETE_WINDOW", self.on_cancel)
def _load_remote_file(self, filename): self._filename = filename self._code_view.set_content("") self._code_view.text.set_read_only(True) target_filename = extract_target_path(self._filename) self.update_title() response = get_runner().send_command( InlineCommand("read_file", path=target_filename, blocking=True, description=_("Loading") + "...")) if response.get("error"): # TODO: make it softer raise RuntimeError(response["error"]) content = response["content_bytes"] self._code_view.text.set_read_only(False) self._code_view.set_content_as_bytes(content) self.get_text_widget().edit_modified(False) self.update_title()
def run_enabled(): widget = get_workbench().focus_get() return isinstance( widget, CodeViewText) and get_runner().is_waiting_toplevel_command()
def _request_heap_data(self, msg=None, even_when_hidden=False): if self.winfo_ismapped() or even_when_hidden: # TODO: update itself also when it becomes visible if get_runner() is not None: get_runner().send_command(InlineCommand("get_heap"))
def show_remote_file(self, target_filename): if not get_runner().ready_for_remote_file_operations( show_message=True): return None else: return self.show_file(make_remote_path(target_filename))
def make_remote_path(target_path): return get_runner().get_node_label() + REMOTE_PATH_MARKER + target_path
def _run_or_resume(): state = get_runner().get_state() if state == "waiting_debugger_command": _issue_debugger_command("resume") elif state == "waiting_toplevel_command": get_runner().cmd_run_current_script()
def _run_or_resume_enabled(): return get_runner().cmd_run_current_script_enabled( ) or _debugger_command_enabled("resume")