コード例 #1
0
class Editor(ttk.Frame):
    def __init__(self, master, filename=None):

        ttk.Frame.__init__(self, master)
        assert isinstance(master, EditorNotebook)

        # parent of codeview will be workbench so that it can be maximized
        self._code_view = CodeView(get_workbench(),
                                   propose_remove_line_numbers=True,
                                   font=get_workbench().get_font("EditorFont"))
        get_workbench().event_generate("EditorTextCreated",
                                       editor=self,
                                       text_widget=self.get_text_widget())

        self._code_view.grid(row=0, column=0, sticky=tk.NSEW, in_=self)
        self._code_view.home_widget = self  # don't forget home
        self.maximizable_widget = self._code_view

        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)

        self._filename = None

        if filename is not None:
            self._load_file(filename)
            self._code_view.text.edit_modified(False)

        self._code_view.text.bind("<<Modified>>", self._on_text_modified, True)
        self._code_view.text.bind("<<TextChange>>", self._on_text_change, True)
        self._code_view.text.bind("<Control-Tab>", self._control_tab, True)

        get_workbench().bind("AfterKnownMagicCommand",
                             self._listen_for_execute, True)
        get_workbench().bind("ToplevelResult",
                             self._listen_for_toplevel_result, True)

        self.update_appearance()

    def get_text_widget(self):
        return self._code_view.text

    def get_code_view(self):
        # TODO: try to get rid of this
        return self._code_view

    def get_filename(self, try_hard=False):
        if self._filename is None and try_hard:
            self.save_file()

        return self._filename

    def get_long_description(self):

        if self._filename is None:
            result = "<untitled>"
        else:
            result = self._filename

        try:
            index = self._code_view.text.index("insert")
            if index and "." in index:
                line, col = index.split(".")
                result += "  @  {} : {}".format(line, int(col) + 1)
        except:
            exception("Finding cursor location")

        return result

    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 is_modified(self):
        return self._code_view.text.edit_modified()

    def save_file_enabled(self):
        return self.is_modified() or not self.get_filename()

    def save_file(self, ask_filename=False):
        if self._filename is not None and not ask_filename:
            filename = self._filename
            get_workbench().event_generate("Save",
                                           editor=self,
                                           filename=filename)
        else:
            # http://tkinter.unpythonic.net/wiki/tkFileDialog
            filename = asksaveasfilename(
                filetypes=_dialog_filetypes,
                defaultextension=".py",
                initialdir=get_workbench().get_option("run.working_directory"))
            if filename in [
                    "", (), None
            ]:  # Different tkinter versions may return different values
                return None

            # Seems that in some Python versions defaultextension
            # acts funny
            if filename.lower().endswith(".py.py"):
                filename = filename[:-3]

            get_workbench().event_generate("SaveAs",
                                           editor=self,
                                           filename=filename)

        content = self._code_view.get_content()
        encoding = "UTF-8"  # TODO: check for marker in the head of the code
        try:
            f = open(
                filename,
                mode="wb",
            )
            f.write(content.encode(encoding))
            f.close()
        except PermissionError:
            if askyesno(
                    "Permission Error",
                    "Looks like this file or folder is not writable.\n\n" +
                    "Do you want to save under another folder and/or filename?"
            ):
                return self.save_file(True)
            else:
                return None

        self._filename = filename
        self.master.remember_recent_file(filename)

        self._code_view.text.edit_modified(False)

        return self._filename

    def show(self):
        self.master.select(self)

    def update_appearance(self):
        self._code_view.set_line_numbers(
            get_workbench().get_option("view.show_line_numbers"))
        self._code_view.set_line_length_margin(
            get_workbench().get_option("view.recommended_line_length"))
        self._code_view.text.event_generate("<<UpdateAppearance>>")

    def _listen_for_execute(self, event):
        command, args = parse_shell_command(event.cmd_line)
        # Go read-only
        if command.lower() == "debug":
            if len(args) == 0:
                return
            filename = args[0]
            self_filename = self.get_filename()
            if self_filename is not None and os.path.basename(
                    self_filename) == filename:
                # Not that command has only basename
                # so this solution may make more editors read-only than necessary
                self._code_view.text.set_read_only(True)

    def _listen_for_toplevel_result(self, event):
        self._code_view.text.set_read_only(False)

    def _control_tab(self, event):
        if event.state & 1:  # shift was pressed
            direction = -1
        else:
            direction = 1
        self.master.select_next_prev_editor(direction)
        return "break"

    def _shift_control_tab(self, event):
        self.master.select_next_prev_editor(-1)
        return "break"

    def select_range(self, text_range):
        self._code_view.select_range(text_range)

    def focus_set(self):
        self._code_view.focus_set()

    def is_focused(self):
        return self.focus_displayof() == self._code_view.text

    def _on_text_modified(self, event):
        self.master.update_editor_title(self)

    def _on_text_change(self, event):
        self.master.update_editor_title(self)
        runner = get_runner()
        if (runner.get_state() in [
                "running", "waiting_input", "waiting_debugger_command"
        ] and isinstance(runner.get_current_command(),
                         (ToplevelCommand,
                          DebuggerCommand))):  # exclude running InlineCommands
            runner.interrupt_backend()

    def destroy(self):
        get_workbench().unbind("AfterKnownMagicCommand",
                               self._listen_for_execute)
        get_workbench().unbind("ToplevelResult",
                               self._listen_for_toplevel_result)
        ttk.Frame.destroy(self)
コード例 #2
0
ファイル: code.py プロジェクト: Blue-Design/thonny
class Editor(ttk.Frame):
    def __init__(self, master, filename=None):

        ttk.Frame.__init__(self, master)
        assert isinstance(master, EditorNotebook)

        # parent of codeview will be workbench so that it can be maximized
        self._code_view = CodeView(get_workbench(),
                                   propose_remove_line_numbers=True,
                                   font=get_workbench().get_font("EditorFont"))
        get_workbench().event_generate("EditorTextCreated",
                                       editor=self,
                                       text_widget=self.get_text_widget())

        self._code_view.grid(row=0, column=0, sticky=tk.NSEW, in_=self)
        self._code_view.home_widget = self  # don't forget home
        self.maximizable_widget = self._code_view

        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)

        self._filename = None
        self.file_encoding = None

        if filename is not None:
            self._load_file(filename)
            self._code_view.text.edit_modified(False)

        self._code_view.text.bind("<<Modified>>",
                                  lambda e: master.update_editor_title(self),
                                  True)
        self._code_view.text.bind("<Control-Tab>", self._control_tab, True)

        get_workbench().bind("AfterKnownMagicCommand",
                             self._listen_for_execute, True)
        get_workbench().bind("ToplevelResult",
                             self._listen_for_toplevel_result, True)

        self.update_appearance()

    def get_text_widget(self):
        return self._code_view.text

    def get_code_view(self):
        # TODO: try to get rid of this
        return self._code_view

    def get_filename(self, try_hard=False):
        if self._filename is None and try_hard:
            self.save_file()

        return self._filename

    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 is_modified(self):
        return self._code_view.text.edit_modified()

    def save_file_enabled(self):
        return self.is_modified() or not self.get_filename()

    def save_file(self, ask_filename=False):
        if self._filename is not None and not ask_filename:
            filename = self._filename
            get_workbench().event_generate("Save",
                                           editor=self,
                                           filename=filename)
        else:
            # http://tkinter.unpythonic.net/wiki/tkFileDialog
            filename = asksaveasfilename(
                filetypes=_dialog_filetypes,
                defaultextension=".py",
                initialdir=get_workbench().get_option("run.working_directory"))
            if filename in [
                    "", (), None
            ]:  # Different tkinter versions may return different values
                return None

            # Seems that in some Python versions defaultextension
            # acts funny
            if filename.lower().endswith(".py.py"):
                filename = filename[:-3]

            get_workbench().event_generate("SaveAs",
                                           editor=self,
                                           filename=filename)

        content = self._code_view.get_content()
        encoding = self.file_encoding or "UTF-8"
        f = open(
            filename,
            mode="wb",
        )
        f.write(content.encode(encoding))
        f.close()

        self._filename = filename
        self.master.remember_recent_file(filename)

        self._code_view.text.edit_modified(False)

        return self._filename

    def show(self):
        self.master.select(self)

    def update_appearance(self):
        self._code_view.set_line_numbers(
            get_workbench().get_option("view.show_line_numbers"))
        self._code_view.set_line_length_margin(
            get_workbench().get_option("view.recommended_line_length"))
        self._code_view.text.event_generate("<<UpdateAppearance>>")

    def _listen_for_execute(self, event):
        command, args = parse_shell_command(event.cmd_line)
        if command.lower() in ["run", "debug"]:
            if len(args) == 0:
                return
            filename = args[0]
            self_filename = self.get_filename()
            if self_filename is not None and os.path.basename(
                    self_filename) == filename:
                # Not that command has only basename
                # so this solution may make more editors read-only than necessary
                self._code_view.text.set_read_only(True)

    def _listen_for_toplevel_result(self, event):
        self._code_view.text.set_read_only(False)

    def _control_tab(self, event):
        if event.state & 1:  # shift was pressed
            direction = -1
        else:
            direction = 1
        self.master.select_next_prev_editor(direction)
        return "break"

    def _shift_control_tab(self, event):
        self.master.select_next_prev_editor(-1)
        return "break"

    def select_range(self, text_range):
        self._code_view.select_range(text_range)

    def focus_set(self):
        self._code_view.focus_set()

    def is_focused(self):
        return self.focus_displayof() == self._code_view.text

    def destroy(self):
        get_workbench().unbind("AfterKnownMagicCommand",
                               self._listen_for_execute)
        get_workbench().unbind("ToplevelResult",
                               self._listen_for_toplevel_result)
        ttk.Frame.destroy(self)