【问题标题】:Associate idle keyboard events to widget text tkinter将空闲键盘事件关联到小部件文本 tkinter
【发布时间】:2021-10-05 20:27:12
【问题描述】:

我正在尝试创建自己的 python 代码编辑器。为此,我尝试使用 tkcode 模块。 Tkcode 部分满足了我的需求(例如文本中的颜色),但它没有 Idle 具有的键盘事件(按 enter 时自动缩进,按 tab 时放置 4 个空格等)。其中一些我可以尝试重新创建,但自动缩进很困难。有没有办法使用 idlelib 将 Idle 事件与我的代码编辑器相关联,而无需创建另一个窗口(因为我正在制作笔记本)?翻遍了Idle的源码,没找到方法。

我知道这个网站不是来征求建议的,但是如果你推荐一个更好的模块来创建一个允许我创建这个编辑器的文本小部件,那也很棒。

这是我编写的代码:

from tkcode import CodeEditor
from tkinter import ttk
import tkinter as tk
import re

class CodeEditor(CodeEditor):
    def __init__(Self, *args, **kargs):
        super().__init__(*args, **kargs)

        Self.bind("<Return>", Self.enter)

    def get_current_line(Self):
        return Self.get("insert linestart", "insert lineend")

    def enter(Self, event):
        for index, char in enumerate(Self.get_current_line()):
            if(char != " "):
                break
        else:
            index += 1

        Self.insert("insert", "\n")
        Self.insert("insert", " "*index)
        return "break"
        

class FileHandler:
    def __init__(Self, progs_path, filetabs):
        Self.files = {}
        Self.progs_path = progs_path
        Self.filetabs = filetabs
    
    def askopen(Self):
        v = tk.Toplevel()
        v.transient()
        v.resizable(0, 0)

        prog = ttk.Entry(v)
        prog.pack(padx=10, pady=10)

        prog.bind("<Return>", lambda Event:(Self.open(prog.get()), v.destroy()))

    def open(Self, prog):
        progfile = str(prog)[0]
        progfile = f"prog{progfile}00A{progfile}99.py"

        if(progfile in Self.files):
            text = Self.files[progfile][prog]
        else:
            functions = {}
            name = None

            with open(f"{Self.progs_path}/{progfile}") as file:
                for line in file:
                    match = re.match("(def|class) prog(\w+)", line)
                    
                    if(match):
                        name = match[2]
                        functions[name] = line

                    if(line.startswith("    ") and name):
                        functions[name] += line

            Self.files[progfile] = functions
            text = functions[prog]

        frame = ttk.Frame(Self.filetabs)

        code_editor = CodeEditor(frame, language="python", highlighter="mariana", font="TkFixedFont", autofocus=True, padx=10, pady=10)
        code_editor.pack(fill="both", expand=True)
        code_editor.content = text

        Self.filetabs.add(frame, text="prog"+prog)





v = tk.Tk()
filetabs = ttk.Notebook()

fh = FileHandler(".", filetabs)

menubar = tk.Menu(tearoff=0)

file = tk.Menu(tearoff=0)
menubar.add_cascade(label="Archivo", menu=file)
file.add_command(label="Abrir prog", command=fh.askopen)

v["menu"] = menubar

filetabs.pack(fill="both", expand=True)

fh.open("833")

【问题讨论】:

    标签: python tkinter python-idle


    【解决方案1】:

    最后我能够修复它。除了其中一个功能外,我几乎可以重新创建所有功能。对于最后一个,我为idlelib.editor.EditorWindow 制作了一个模拟类,以便能够自动缩进,此外还为tkcode.CodeEditor 添加了两个新功能。

    但是,我发现此解决方案非常不稳定。任何更好的答案仍然值得赞赏c:

    这是代码:

    from tkcode import CodeEditor
    from tkinter import ttk
    from idlelib.editor import EditorWindow
    import tkinter as tk
    import re
    
    class FakeEditorWindow(EditorWindow):
        def __init__(Self, text):
            Self.text = text
            Self.indentwidth = 4
            Self.tabwidth = 4
            Self.prompt_last_line = ''
            Self.num_context_lines = 50, 500, 5000000
            Self.usetabs = False
    
        def is_char_in_string(Self, text_index):
            return 1
        
    
    class CodeEditor(CodeEditor):
        def __init__(Self, *args, **kargs):
            super().__init__(*args, **kargs)
    
            Self.fake_editor_window = FakeEditorWindow(Self)
    
            Self.bind("<Tab>", Self.tab)
            Self.bind("<Control-Shift_L>", Self.dedent)
            Self.bind("<BackSpace>", Self.backspace)
            Self.bind("<Home>", Self.inicio)
            Self.bind("<Return>", Self.enter)
    
        def undo_block_start(Self):
            pass
    
        def undo_block_stop(Self):
            pass
            
        def get_current_line(Self):
            return Self.get("insert linestart", "insert lineend")
    
        def selection_get(Self):
            if(Self.tag_ranges("sel")):
                return Self.get("sel.first", "sel.last")
            else:
                return ""
    
        def get_selection_zone(Self):
            return (map(int, Self.index('sel.first').split(".", 1)),
                    map(int, Self.index('sel.last').split(".", 1)))
    
        def tab(Self, event):
            selection = Self.selection_get()
    
            if(selection):
                (startline, startcolumn), (endline, endcolumn) = Self.get_selection_zone()
    
                if(startcolumn == 0):
                    for line in range(startline, endline+1):
                        Self.insert(f"{line}.0", " "*4)
    
                    Self.tag_add("sel", f"{startline}.0", "sel.last")
                    Self.mark_set("insert", f"{endline+1}.0")
            else:
                Self.insert("insert", " "*4)
    
            return "break"
    
        def dedent(Self, event):
            if(Self.tag_ranges("sel")):
                (startline, startcolumn), (endline, endcolumn) = Self.get_selection_zone()
    
                if(startcolumn == 0):
                    for line in range(startline, endline+1):
                        if(Self.get(f"{line}.0", f"{line}.4") == " "*4):
                            Self.delete(f"{line}.0", f"{line}.4")
    
        def backspace(Self, event):
            if(not Self.tag_ranges("sel") and Self.get("insert linestart", "insert").isspace()):
                cursor_line, cursor_col = map(int, Self.index("insert").split(".", 1))
                Self.delete(f"{cursor_line}.{cursor_col-4}", "insert")
                return "break"
    
        def inicio(Self, event):
            cursor_line, cursor_column = map(int, Self.index('insert').split(".", 1))
    
            if(not Self.get("insert linestart", f"{cursor_line}.{cursor_column}").isspace()):
                for i in range(cursor_column, -1, -1):      
                    if(Self.get("insert linestart", f"{cursor_line}.{i}").isspace()):
                        Self.mark_set("insert", f"{cursor_line}.{i}")
                        return "break"
    
        def enter(Self, event):
            return EditorWindow.newline_and_indent_event(Self.fake_editor_window, event)                
    
    class FileHandler:
        def __init__(Self, progs_path, filetabs):
            Self.files = {}
            Self.progs_path = progs_path
            Self.filetabs = filetabs
        
        def askopen(Self):
            v = tk.Toplevel()
            v.transient()
            v.resizable(0, 0)
    
            prog = ttk.Entry(v)
            prog.pack(padx=10, pady=10)
    
            prog.bind("<Return>", lambda Event:(Self.open(prog.get()), v.destroy()))
    
        def open(Self, prog):
            progfile = str(prog)[0]
            progfile = f"prog{progfile}00A{progfile}99.py"
    
            if(progfile in Self.files):
                text = Self.files[progfile][prog]
            else:
                functions = {}
                name = None
    
                with open(f"{Self.progs_path}/{progfile}") as file:
                    for line in file:
                        match = re.match("(def|class) prog(\w+)", line)
                        
                        if(match):
                            name = match[2]
                            functions[name] = line
    
                        if(line.startswith("    ") and name):
                            functions[name] += line
    
                Self.files[progfile] = functions
                text = functions[prog]
    
            frame = ttk.Frame(Self.filetabs)
    
            code_editor = CodeEditor(frame, language="python", highlighter="mariana", font="TkFixedFont", autofocus=True, padx=10, pady=10)
            code_editor.pack(fill="both", expand=True)
            code_editor.content = text
    
            Self.filetabs.add(frame, text="prog"+prog)
    
    
    
    
    
    v = tk.Tk()
    filetabs = ttk.Notebook()
    
    fh = FileHandler(".", filetabs)
    
    menubar = tk.Menu(tearoff=0)
    
    file = tk.Menu(tearoff=0)
    menubar.add_cascade(label="Archivo", menu=file)
    file.add_command(label="Abrir prog", command=fh.askopen)
    
    v["menu"] = menubar
    
    filetabs.pack(fill="both", expand=True)
    
    fh.open("833")
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-19
      • 1970-01-01
      • 2017-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多