def __init__(self, master, show_expand_buttons=True): super().__init__(master, show_expand_buttons=show_expand_buttons) get_workbench().bind("WindowFocusIn", self.on_window_focus_in, True) get_workbench().bind("LocalFileOperation", self.on_local_file_operation, True)
def load_plugin(): get_workbench().add_view(NotesView, _("Notes"), "ne", default_position_key="zz")
def open_frontend_pip_gui(*args): pg = PluginsPipDialog(get_workbench()) ui_utils.show_dialog(pg)
def cmd_run_current_script_enabled(self) -> bool: return ( get_workbench().get_editor_notebook().get_current_editor() is not None and "run" in get_runner().get_supported_features() )
def cmd_stop_restart(self) -> None: if get_workbench().in_simple_mode(): get_workbench().hide_view("VariablesView") self.restart_backend(True)
def _init_commands(self) -> None: global RUN_COMMAND_CAPTION, RUN_COMMAND_LABEL RUN_COMMAND_LABEL = tr("Run current script") RUN_COMMAND_CAPTION = tr("Run") get_workbench().set_default("run.run_in_terminal_python_repl", False) get_workbench().set_default("run.run_in_terminal_keep_open", True) try: import thonny.plugins.debugger # @UnusedImport debugger_available = True except ImportError: debugger_available = False get_workbench().add_command( "run_current_script", "run", RUN_COMMAND_LABEL, caption=RUN_COMMAND_CAPTION, handler=self.cmd_run_current_script, default_sequence="<F5>", extra_sequences=[select_sequence("<Control-r>", "<Command-r>")], tester=self.cmd_run_current_script_enabled, group=10, image="run-current-script", include_in_toolbar=not (get_workbench().in_simple_mode() and debugger_available), show_extra_sequences=True, ) get_workbench().add_command( "run_current_script_in_terminal", "run", tr("Run current script in terminal"), caption="RunT", handler=self._cmd_run_current_script_in_terminal, default_sequence="<Control-t>", extra_sequences=["<<CtrlTInText>>"], tester=self._cmd_run_current_script_in_terminal_enabled, group=35, image="terminal", ) get_workbench().add_command( "restart", "run", tr("Stop/Restart backend"), caption=tr("Stop"), handler=self.cmd_stop_restart, default_sequence="<Control-F2>", group=100, image="stop", include_in_toolbar=True, ) get_workbench().add_command( "interrupt", "run", tr("Interrupt execution"), handler=self._cmd_interrupt, tester=self._cmd_interrupt_enabled, default_sequence=INTERRUPT_SEQUENCE, skip_sequence_binding=True, # Sequence will be bound differently group=100, bell_when_denied=False, ) get_workbench().bind(INTERRUPT_SEQUENCE, self._cmd_interrupt_with_shortcut, True) get_workbench().add_command( "ctrld", "run", tr("Send EOF / Soft reboot"), self.ctrld, self.ctrld_enabled, group=100, default_sequence="<Control-d>", extra_sequences=["<<CtrlDInText>>"], ) get_workbench().add_command( "disconnect", "run", tr("Disconnect"), self.disconnect, self.disconnect_enabled, group=100, )
def close(self): get_workbench().unbind("InlineResponse", self._on_response) get_workbench().unbind("InlineProgress", self._on_progress) super(InlineCommandDialog, self).close() get_shell().set_ignore_program_output(False)
def _append_image(self, name, extra_tags=()): index = self.text.index("end-1c") self.text.image_create(index, image=get_workbench().get_image(name)) for tag in self._get_effective_tags(extra_tags): self.text.tag_add(tag, index)
def _get_current_editor(): return get_workbench().get_editor_notebook().get_current_editor()
def get_toggler_image_name(kind): if get_workbench().uses_dark_ui_theme(): return kind + "_light" else: return kind
def handle_click(event): get_workbench().open_url(node.attributes["refuri"])
def _visit_toggle_topic(self, node): tag = self._create_unique_tag() title_id_tag = tag + "_title" body_id_tag = tag + "_body" def get_toggler_image_name(kind): if get_workbench().uses_dark_ui_theme(): return kind + "_light" else: return kind if "open" in node.attributes["classes"]: initial_image = get_toggler_image_name("boxminus") initial_elide = False else: initial_image = get_toggler_image_name("boxplus") initial_elide = True label = tk.Label( self.text, image=get_workbench().get_image(initial_image), borderwidth=0, background=self.text["background"], cursor="arrow", ) def toggle_body(event=None): elide = self.text.tag_cget(body_id_tag, "elide") if elide == "1": elide = True elif elide == "0": elide = False else: elide = bool(elide) elide = not elide self.text.tag_configure(body_id_tag, elide=elide) if self.text.has_selection(): self.text.tag_remove("sel", "1.0", "end") if elide: label.configure(image=get_workbench().get_image( get_toggler_image_name("boxplus"))) else: label.configure(image=get_workbench().get_image( get_toggler_image_name("boxminus"))) assert isinstance(node.children[0], docutils.nodes.title) # self.text.tag_bind(title_id_tag, "<1>", toggle_body, True) self._add_tag(title_id_tag) self._append_window(label) label.bind("<1>", toggle_body, True) node.children[0].walkabout(self) self._pop_tag(title_id_tag) self.text.tag_configure(body_id_tag, elide=initial_elide) self._add_tag(body_id_tag) self._add_tag("topic_body") for child in list(node.children)[1:]: child.walkabout(self) self._pop_tag("topic_body") self._pop_tag(body_id_tag) if "tight" not in node.attributes["classes"]: self._append_text("\n") raise docutils.nodes.SkipNode()
def open_file(self, path): get_workbench().get_editor_notebook().show_file(path)
def destroy(self): super().destroy() get_workbench().unbind("WindowFocusIn", self.on_window_focus_in) get_workbench().unbind("LocalFileOperation", self.on_local_file_operation)
def open_about(*args): ui_utils.show_dialog(AboutDialog(get_workbench()))
def load_plugin(): add_program_analyzer(PylintAnalyzer) get_workbench().set_default("assistance.use_pylint", True)
def _publish_cwd(self, cwd): if self.uses_local_filesystem(): get_workbench().set_local_cwd(cwd)
def is_enabled(self): return get_workbench().get_option("assistance.use_pylint")
def get_environment_overrides_for_python_subprocess(target_executable): """Take care of not not confusing different interpreter with variables meant for bundled interpreter""" # At the moment I'm tweaking the environment only if current # exe is bundled for Thonny. # In remaining cases it is user's responsibility to avoid # calling Thonny with environment which may be confusing for # different Pythons called in a subprocess. this_executable = sys.executable.replace("pythonw.exe", "python.exe") target_executable = target_executable.replace("pythonw.exe", "python.exe") interpreter_specific_keys = [ "TCL_LIBRARY", "TK_LIBRARY", "LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH", "SSL_CERT_DIR", "SSL_CERT_FILE", "PYTHONHOME", "PYTHONPATH", "PYTHONNOUSERSITE", "PYTHONUSERBASE", ] result = {} if os.path.samefile( target_executable, this_executable ) or is_venv_interpreter_of_current_interpreter(target_executable): # bring out some important variables so that they can # be explicitly set in macOS Terminal # (If they are set then it's most likely because current exe is in Thonny bundle) for key in interpreter_specific_keys: if key in os.environ: result[key] = os.environ[key] # never pass some variables to different interpreter # (even if it's venv or symlink to current one) if not is_same_path(target_executable, this_executable): for key in ["PYTHONPATH", "PYTHONHOME", "PYTHONNOUSERSITE", "PYTHONUSERBASE"]: if key in os.environ: result[key] = None else: # interpreters are not related # interpreter specific keys most likely would confuse other interpreter for key in interpreter_specific_keys: if key in os.environ: result[key] = None # some keys should be never passed for key in [ "PYTHONSTARTUP", "PYTHONBREAKPOINT", "PYTHONDEBUG", "PYTHONNOUSERSITE", "PYTHONASYNCIODEBUG", ]: if key in os.environ: result[key] = None # venv may not find (correct) Tk without assistance (eg. in Ubuntu) if is_venv_interpreter_of_current_interpreter(target_executable): try: if "TCL_LIBRARY" not in os.environ or "TK_LIBRARY" not in os.environ: result["TCL_LIBRARY"] = get_workbench().tk.exprstring("$tcl_library") result["TK_LIBRARY"] = get_workbench().tk.exprstring("$tk_library") except Exception: logging.exception("Can't compute Tcl/Tk library location") return result
def load_plugin() -> None: get_workbench().add_configuration_page("General", GeneralConfigurationPage)
def send_command_and_wait(self, cmd: CommandToBackend, dialog_title: str) -> MessageFromBackend: dlg = InlineCommandDialog(get_workbench(), cmd, title=dialog_title + " ...") show_dialog(dlg) return dlg.response
def __init__(self, master): ConfigurationPage.__init__(self, master) self.add_checkbox( "general.single_instance", "Allow only single Thonny instance", row=1, columnspan=2, ) self.add_checkbox( "general.debug_mode", "Debug mode (provides more detailed logs)", row=3, columnspan=2, ) self.add_checkbox( "file.reopen_all_files", "Reopen all files from previous session", row=4, columnspan=2, ) self.add_checkbox( "general.disable_notification_sound", "Disable notification sound", row=5, columnspan=2, ) ttk.Label(self, text="UI mode").grid(row=6, column=0, sticky=tk.W, padx=(0, 10), pady=(10, 0)) self.add_combobox("general.ui_mode", ["simple", "regular", "expert"], row=6, column=1, pady=(10, 0)) self._scaling_var = get_workbench().get_variable("general.scaling") self._scaling_label = ttk.Label(self, text="UI scaling factor") self._scaling_label.grid(row=7, column=0, sticky=tk.W, padx=(0, 10), pady=(10, 0)) scalings = sorted( {0.5, 0.75, 1.0, 1.25, 1.33, 1.5, 2.0, 2.5, 3.0, 4.0}) self._scaling_combo = ttk.Combobox( self, width=7, exportselection=False, textvariable=self._scaling_var, state="readonly", height=15, values=["default"] + scalings, ) self._scaling_combo.grid(row=7, column=1, sticky=tk.W, pady=(10, 0)) self._font_scaling_var = get_workbench().get_variable( "general.font_scaling_mode") self._font_scaling_label = ttk.Label(self, text="Font scaling mode") self._font_scaling_label.grid(row=8, column=0, sticky=tk.W, padx=(0, 10), pady=(10, 0)) scalings = sorted( {0.5, 0.75, 1.0, 1.25, 1.33, 1.5, 2.0, 2.5, 3.0, 4.0}) self._font_scaling_combo = ttk.Combobox( self, width=10, exportselection=False, textvariable=self._font_scaling_var, state="readonly", height=15, values=["default", "extra", "automatic"], ) self._font_scaling_combo.grid(row=8, column=1, sticky=tk.W, pady=(10, 0)) reopen_label = ttk.Label( self, text="NB! Restart Thonny after changing these options" + "\nin order to see the full effect", font="BoldTkDefaultFont", ) reopen_label.grid(row=20, column=0, sticky=tk.W, pady=20, columnspan=2) self.columnconfigure(1, weight=1)
def cmd_run_current_script(self) -> None: if get_workbench().in_simple_mode(): get_workbench().hide_view("VariablesView") self.execute_current("Run")
import uuid import copy import threading import tkinter as tk import paho.mqtt.client as mqtt_client import paho.mqtt.publish as mqtt_publish import paho.mqtt.subscribe as mqtt_subscribe import thonnycontrib.codelive.utils as utils import thonnycontrib.codelive.client as thonny_client from thonnycontrib.codelive.user import UserDecoder, UserEncoder from thonny import get_workbench WORKBENCH = get_workbench() BROKER_URLS = [ "test.mosquitto.org", "mqtt.eclipse.org", "broker.hivemq.com", "mqtt.fluux.io", "broker.emqx.io", ] USER_COLORS = [ "blue", "green", "red", "pink", "orange", "black", "white", "purple" ] SINGLE_PUBLISH_HEADER = b"CODELIVE_MSG:"
def _start_background_process(self, clean=None, extra_args=[]): # deque, because in one occasion I need to put messages back self._response_queue = collections.deque() # prepare environment env = get_environment_for_python_subprocess(self._executable) # variables controlling communication with the back-end process env["PYTHONIOENCODING"] = "utf-8" # because cmd line option -u won't reach child processes # see https://github.com/thonny/thonny/issues/808 env["PYTHONUNBUFFERED"] = "1" # Let back-end know about plug-ins env["THONNY_USER_DIR"] = THONNY_USER_DIR env["THONNY_FRONTEND_SYS_PATH"] = repr(sys.path) env["THONNY_LANGUAGE"] = get_workbench().get_option("general.language") if thonny.in_debug_mode(): env["THONNY_DEBUG"] = "1" elif "THONNY_DEBUG" in env: del env["THONNY_DEBUG"] if not os.path.exists(self._executable): raise UserError( "Interpreter (%s) not found. Please recheck corresponding option!" % self._executable ) cmd_line = ( [ self._executable, "-u", # unbuffered IO "-B", # don't write pyo/pyc files # (to avoid problems when using different Python versions without write permissions) ] + self._get_launcher_with_args() + extra_args ) creationflags = 0 if running_on_windows(): creationflags = subprocess.CREATE_NEW_PROCESS_GROUP debug("Starting the backend: %s %s", cmd_line, get_workbench().get_local_cwd()) extra_params = {} if sys.version_info >= (3, 6): extra_params["encoding"] = "utf-8" self._proc = subprocess.Popen( cmd_line, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=self._get_launch_cwd(), env=env, universal_newlines=True, creationflags=creationflags, **extra_params ) # setup asynchronous output listeners Thread(target=self._listen_stdout, args=(self._proc.stdout,), daemon=True).start() Thread(target=self._listen_stderr, args=(self._proc.stderr,), daemon=True).start()
def destroy(self): MemoryFrame.destroy(self) get_workbench().unbind("ShowView", self._update_memory_model) get_workbench().unbind("HideView", self._update_memory_model)
def open_backend_pip_gui(*args): pg = BackendPipDialog(get_workbench()) ui_utils.show_dialog(pg)
def open_replayer(): win = ReplayWindow(get_workbench()) ui_utils.show_dialog(win)
def add_entry(self, option_name, row=None, column=0, pady=0, padx=0, columnspan=1, **kw): variable = get_workbench().get_variable(option_name) entry = ttk.Entry(self, textvariable=variable, **kw) entry.grid(row=row, column=column, sticky=tk.W, pady=pady, columnspan=columnspan, padx=padx)
def should_open_name_in_thonny(self, name): ext = self.get_extension_from_name(name) return get_workbench().get_option(get_file_handler_conf_key(ext), "system") == "thonny"