diff --git a/editor.py b/editor.py index 25b1531..0d43299 100644 --- a/editor.py +++ b/editor.py @@ -1,194 +1,200 @@ """ -Lity3D Editor - 引擎集成开发环境 -双击打开,点 Run 运行游戏,点 Build 打包 exe +Lity3D Editor - 集成开发环境 +双击打开 → 写代码 → F5 运行 → F7 打包 +支持 11 种语言切换 """ import tkinter as tk from tkinter import ttk, filedialog, scrolledtext, messagebox import os, subprocess, json, sys, time ENGINE_DIR = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, ENGINE_DIR) +import i18n class LityEditor: def __init__(self): + i18n.load_language() self.root = tk.Tk() - self.root.title("Lity3D Editor") self.root.geometry("1400x900") self.root.configure(bg="#2b2d30") - self.current_file = None self.process = None - self._setup_styles() - self._build_menu() - self._build_toolbar() - self._build_main_layout() - self._build_statusbar() - - self._load_recent() + self._build_ui() + i18n.on_language_change(self._refresh_ui) self.root.protocol("WM_DELETE_WINDOW", self._on_close) - + + def _t(self, key): + return i18n.t(key) + def _setup_styles(self): s = ttk.Style() s.theme_use("clam") s.configure("Dark.TFrame", background="#2b2d30") s.configure("Dark.TLabel", background="#2b2d30", foreground="#cccccc") - s.configure("Tool.TButton", padding=4) - s.configure("Run.TButton", padding=6, font=("Segoe UI", 10, "bold")) - + + def _build_ui(self): + self._build_menu() + self._build_toolbar() + self._build_main() + self._build_statusbar() + self._set_status(self._t("ready")) + + def _refresh_ui(self): + self.root.title(self._t("app_title")) + for w in self.root.winfo_children(): + if isinstance(w, tk.Menu): + w.destroy() + self._build_menu() + self._build_toolbar() + def _build_menu(self): mb = tk.Menu(self.root, bg="#2b2d30", fg="#cccccc") - - fm = tk.Menu(mb, tearoff=0, bg="#3c3f41", fg="#cccccc") - fm.add_command(label="New File", accelerator="Ctrl+N", command=self._new_file) - fm.add_command(label="Open File", accelerator="Ctrl+O", command=self._open_file) - fm.add_command(label="Open Folder", command=self._open_folder) - fm.add_separator() - fm.add_command(label="Save", accelerator="Ctrl+S", command=self._save_file) - fm.add_command(label="Save As", command=self._save_as) - fm.add_separator() - fm.add_command(label="Exit", command=self._on_close) - mb.add_cascade(label="File", menu=fm) - - em = tk.Menu(mb, tearoff=0, bg="#3c3f41", fg="#cccccc") - em.add_command(label="Run Game", accelerator="F5", command=self._run_game) - em.add_command(label="Run Current File", accelerator="Ctrl+F5", command=self._run_current) - em.add_separator() - em.add_command(label="Build Executable", accelerator="F7", command=self._build_exe) - em.add_command(label="Build Engine", command=self._build_engine) - em.add_separator() - em.add_command(label="Kill Process", accelerator="Shift+F5", command=self._kill_process) - mb.add_cascade(label="Build", menu=em) - - sm = tk.Menu(mb, tearoff=0, bg="#3c3f41", fg="#cccccc") - sm.add_command(label="New Scene", command=self._new_scene) - sm.add_command(label="Open Scene", command=self._open_scene) - sm.add_command(label="Save Scene", command=self._save_scene) - sm.add_separator() - sm.add_command(label="Run Scene", command=self._run_scene) - mb.add_cascade(label="Scene", menu=sm) - - hm = tk.Menu(mb, tearoff=0, bg="#3c3f41", fg="#cccccc") - hm.add_command(label="About Lity3D", command=lambda: messagebox.showinfo("Lity3D", "Lity3D Engine v1.0\n自研 C+MASM 游戏引擎\nPython 脚本 + D3D11 渲染")) - mb.add_cascade(label="Help", menu=hm) - self.root.config(menu=mb) + + fm = tk.Menu(mb, tearoff=0, bg="#3c3f41", fg="#cccccc") + fm.add_command(label=self._t("new_file"), accelerator="Ctrl+N", command=self._new_file) + fm.add_command(label=self._t("open_file"), accelerator="Ctrl+O", command=self._open_file) + fm.add_command(label=self._t("open_folder"), command=self._open_folder) + fm.add_separator() + fm.add_command(label=self._t("save"), accelerator="Ctrl+S", command=self._save_file) + fm.add_command(label=self._t("save_as"), command=self._save_as) + fm.add_separator() + fm.add_command(label=self._t("exit"), command=self._on_close) + mb.add_cascade(label=self._t("menu_file"), menu=fm) + + em = tk.Menu(mb, tearoff=0, bg="#3c3f41", fg="#cccccc") + em.add_command(label=self._t("undo"), accelerator="Ctrl+Z", command=lambda: self.code_text.edit_undo()) + em.add_command(label=self._t("redo"), accelerator="Ctrl+Y", command=lambda: self.code_text.edit_redo()) + em.add_separator() + em.add_command(label=self._t("cut"), accelerator="Ctrl+X", command=lambda: self.root.focus_get().event_generate("<>")) + em.add_command(label=self._t("copy"), accelerator="Ctrl+C", command=lambda: self.root.focus_get().event_generate("<>")) + em.add_command(label=self._t("paste"), accelerator="Ctrl+V", command=lambda: self.root.focus_get().event_generate("<>")) + em.add_command(label=self._t("select_all"), accelerator="Ctrl+A", command=lambda: self.code_text.tag_add("sel", "1.0", "end")) + mb.add_cascade(label=self._t("menu_edit"), menu=em) + + bm = tk.Menu(mb, tearoff=0, bg="#3c3f41", fg="#cccccc") + bm.add_command(label=self._t("run_game"), accelerator="F5", command=self._run_game) + bm.add_command(label=self._t("run_file"), accelerator="Ctrl+F5", command=self._run_current) + bm.add_separator() + bm.add_command(label=self._t("build_engine"), command=self._build_engine) + bm.add_command(label=self._t("build_exe"), accelerator="F7", command=self._build_exe) + bm.add_separator() + bm.add_command(label=self._t("kill_process"), accelerator="Shift+F5", command=self._kill_process) + mb.add_cascade(label=self._t("menu_build"), menu=bm) + + sm = tk.Menu(mb, tearoff=0, bg="#3c3f41", fg="#cccccc") + sm.add_command(label=self._t("new_scene"), command=self._new_scene) + sm.add_command(label=self._t("open_scene"), command=self._open_scene) + sm.add_command(label=self._t("save_scene"), command=self._save_scene) + sm.add_separator() + sm.add_command(label=self._t("run_scene"), command=self._run_scene) + mb.add_cascade(label=self._t("menu_scene"), menu=sm) + + lm = tk.Menu(mb, tearoff=0, bg="#3c3f41", fg="#cccccc") + for code, name in i18n.get_all_languages(): + marker = " ✓" if code == i18n.get_language() else "" + lm.add_command(label=f"{name}{marker}", command=lambda c=code: self._switch_lang(c)) + mb.add_cascade(label=self._t("menu_language"), menu=lm) + + hm = tk.Menu(mb, tearoff=0, bg="#3c3f41", fg="#cccccc") + hm.add_command(label=self._t("docs"), command=self._open_docs) + hm.add_command(label=self._t("about"), command=lambda: messagebox.showinfo( + self._t("about"), "Lity3D Engine v1.0\nC+MASM Game Engine\nPython Scripting + D3D11\n11 Languages")) + mb.add_cascade(label=self._t("menu_help"), menu=hm) + self.root.bind("", lambda e: self._new_file()) self.root.bind("", lambda e: self._open_file()) self.root.bind("", lambda e: self._save_file()) self.root.bind("", lambda e: self._run_game()) self.root.bind("", lambda e: self._build_exe()) self.root.bind("", lambda e: self._kill_process()) - + + def _switch_lang(self, code): + i18n.set_language(code) + i18n.save_language(code) + self._refresh_ui() + def _build_toolbar(self): - tb = ttk.Frame(self.root, style="Dark.TFrame") - tb.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2) - - ttk.Button(tb, text="New", style="Tool.TButton", command=self._new_file).pack(side=tk.LEFT, padx=1) - ttk.Button(tb, text="Open", style="Tool.TButton", command=self._open_file).pack(side=tk.LEFT, padx=1) - ttk.Button(tb, text="Save", style="Tool.TButton", command=self._save_file).pack(side=tk.LEFT, padx=1) - - ttk.Separator(tb, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=4) - - run_btn = ttk.Button(tb, text="Run (F5)", style="Run.TButton", command=self._run_game) - run_btn.pack(side=tk.LEFT, padx=2) - ttk.Button(tb, text="Stop", style="Tool.TButton", command=self._kill_process).pack(side=tk.LEFT, padx=1) - ttk.Button(tb, text="Build (F7)", style="Tool.TButton", command=self._build_exe).pack(side=tk.LEFT, padx=2) - - ttk.Separator(tb, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=4) - - ttk.Button(tb, text="New Scene", style="Tool.TButton", command=self._new_scene).pack(side=tk.LEFT, padx=1) - ttk.Button(tb, text="Save Scene", style="Tool.TButton", command=self._save_scene).pack(side=tk.LEFT, padx=1) - - ttk.Separator(tb, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=4) - - ttk.Button(tb, text="LityPy Docs", style="Tool.TButton", command=self._open_docs).pack(side=tk.LEFT, padx=1) - - def _build_main_layout(self): + if hasattr(self, '_toolbar') and self._toolbar: + self._toolbar.destroy() + self._toolbar = ttk.Frame(self.root, style="Dark.TFrame") + self._toolbar.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2) + + ttk.Button(self._toolbar, text=self._t("new_file"), command=self._new_file).pack(side=tk.LEFT, padx=1) + ttk.Button(self._toolbar, text=self._t("open_file"), command=self._open_file).pack(side=tk.LEFT, padx=1) + ttk.Button(self._toolbar, text=self._t("save"), command=self._save_file).pack(side=tk.LEFT, padx=1) + ttk.Separator(self._toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=4) + ttk.Button(self._toolbar, text=self._t("run") + " (F5)", command=self._run_game).pack(side=tk.LEFT, padx=2) + ttk.Button(self._toolbar, text=self._t("stop"), command=self._kill_process).pack(side=tk.LEFT, padx=1) + ttk.Button(self._toolbar, text=self._t("build_exe") + " (F7)", command=self._build_exe).pack(side=tk.LEFT, padx=2) + ttk.Separator(self._toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=4) + ttk.Button(self._toolbar, text=self._t("new_scene"), command=self._new_scene).pack(side=tk.LEFT, padx=1) + ttk.Button(self._toolbar, text=self._t("save_scene"), command=self._save_scene).pack(side=tk.LEFT, padx=1) + + def _build_main(self): main = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL) main.pack(fill=tk.BOTH, expand=True, padx=2, pady=2) - + left = ttk.Frame(main, style="Dark.TFrame") main.add(left, weight=1) - - ttk.Label(left, text="EXPLORER", style="Dark.TLabel", font=("Segoe UI", 9, "bold")).pack(anchor=tk.W, padx=5, pady=3) - + ttk.Label(left, text=self._t("explorer").upper(), style="Dark.TLabel", font=("Segoe UI", 9, "bold")).pack(anchor=tk.W, padx=5, pady=3) self.file_tree = ttk.Treeview(left, show="tree", height=20) self.file_tree.pack(fill=tk.BOTH, expand=True, padx=3, pady=3) self.file_tree.bind("", self._on_tree_double) - + self._refresh_tree() + right = ttk.Frame(main, style="Dark.TFrame") main.add(right, weight=3) - self.notebook = ttk.Notebook(right) self.notebook.pack(fill=tk.BOTH, expand=True) - self.editor_frame = ttk.Frame(self.notebook) - self.notebook.add(self.editor_frame, text="Editor") - - self.code_text = scrolledtext.ScrolledText( - self.editor_frame, bg="#1e1e1e", fg="#d4d4d4", - insertbackground="#d4d4d4", selectbackground="#264f78", - font=("Consolas", 12), wrap=tk.NONE, - undo=True, maxundo=-1 - ) + self.notebook.add(self.editor_frame, text="editor.py") + self.code_text = scrolledtext.ScrolledText(self.editor_frame, bg="#1e1e1e", fg="#d4d4d4", insertbackground="#d4d4d4", font=("Consolas", 12), wrap=tk.NONE, undo=True) self.code_text.pack(fill=tk.BOTH, expand=True) - + bottom = ttk.Frame(right, style="Dark.TFrame") bottom.pack(fill=tk.BOTH, expand=False) - - bottom_pw = ttk.PanedWindow(bottom, orient=tk.VERTICAL) - bottom_pw.pack(fill=tk.BOTH, expand=True) - - console_frame = ttk.Frame(bottom_pw) - bottom_pw.add(console_frame, weight=1) - - ttk.Label(console_frame, text="OUTPUT", style="Dark.TLabel", font=("Segoe UI", 9, "bold")).pack(anchor=tk.W, padx=5) - - self.console = scrolledtext.ScrolledText( - console_frame, bg="#1e1e1e", fg="#cccccc", - font=("Consolas", 10), height=8, state=tk.DISABLED - ) - self.console.pack(fill=tk.BOTH, expand=True) - - self._refresh_tree() - + self.console = scrolledtext.ScrolledText(bottom, bg="#1e1e1e", fg="#cccccc", font=("Consolas", 10), height=8, state=tk.DISABLED) + self.console.pack(fill=tk.BOTH, expand=True, padx=3, pady=3) + def _build_statusbar(self): self.statusbar = ttk.Frame(self.root, style="Dark.TFrame") self.statusbar.pack(side=tk.BOTTOM, fill=tk.X) - - self.status_label = ttk.Label(self.statusbar, text="Ready", style="Dark.TLabel") + self.status_label = ttk.Label(self.statusbar, text="", style="Dark.TLabel") self.status_label.pack(side=tk.LEFT, padx=5) - self.process_label = ttk.Label(self.statusbar, text="", style="Dark.TLabel", foreground="#6a9955") self.process_label.pack(side=tk.RIGHT, padx=5) - + def _log(self, msg, tag="info"): self.console.config(state=tk.NORMAL) ts = time.strftime("%H:%M:%S") - prefix = {"info": " ", "error": "!!", "success": "OK"}[tag] - self.console.insert(tk.END, f"[{ts}] {prefix} {msg}\n") + pfx = {"info": " ", "error": "!!", "success": "OK"}[tag] + self.console.insert(tk.END, f"[{ts}] {pfx} {msg}\n") self.console.see(tk.END) self.console.config(state=tk.DISABLED) - + def _set_status(self, msg): self.status_label.config(text=msg) - + def _refresh_tree(self): + if not hasattr(self, 'file_tree'): return self.file_tree.delete(*self.file_tree.get_children()) - root_node = self.file_tree.insert("", "end", text="lity3d", open=True) + root = self.file_tree.insert("", "end", text="lity3d", open=True) + skip = {".git", ".gitignore", "build", "__pycache__", ".lang", ".vs", ".vscode", "obj"} for item in sorted(os.listdir(ENGINE_DIR)): + if item.startswith(".") or item in skip: continue full = os.path.join(ENGINE_DIR, item) - if item.startswith(".") or item in ("build", "__pycache__", ".git", "obj"): continue if os.path.isdir(full): - dir_node = self.file_tree.insert(root_node, "end", text=item, open=False) + dn = self.file_tree.insert(root, "end", text=item) try: for f in sorted(os.listdir(full)): - if f.startswith(".") or f.endswith((".pyc", ".exe")): continue - self.file_tree.insert(dir_node, "end", text=f, values=(os.path.join(item, f),)) + if f.startswith(".") or f.endswith((".pyc", ".exe", ".o", ".a")): continue + self.file_tree.insert(dn, "end", text=f, values=(os.path.join(item, f),)) except: pass else: - self.file_tree.insert(root_node, "end", text=item, values=(item,)) - + self.file_tree.insert(root, "end", text=item, values=(item,)) + def _on_tree_double(self, event): sel = self.file_tree.selection() if not sel: return @@ -196,194 +202,136 @@ class LityEditor: vals = item.get("values", ()) path = vals[0] if vals else item["text"] full = os.path.join(ENGINE_DIR, path) if not os.path.isabs(path) else path - if os.path.isfile(full): - self._open_file_at(full) - + if os.path.isfile(full): self._open_file_at(full) + def _new_file(self): self.code_text.delete("1.0", tk.END) self.current_file = None - self._set_status("New file") self.notebook.tab(self.editor_frame, text="untitled*") - + self._set_status(self._t("new_file_msg")) + def _open_file(self): - path = filedialog.askopenfilename( - initialdir=ENGINE_DIR, - filetypes=[("Python", "*.py"), ("C Source", "*.c *.h"), ("All", "*.*")] - ) - if path: self._open_file_at(path) - + p = filedialog.askopenfilename(initialdir=ENGINE_DIR, filetypes=[("Python", "*.py"), ("C", "*.c *.h"), ("All", "*.*")]) + if p: self._open_file_at(p) + def _open_file_at(self, path): try: with open(path, "r", encoding="utf-8", errors="replace") as f: - content = f.read() - self.code_text.delete("1.0", tk.END) - self.code_text.insert("1.0", content) + self.code_text.delete("1.0", tk.END) + self.code_text.insert("1.0", f.read()) self.current_file = path name = os.path.basename(path) self.notebook.tab(self.editor_frame, text=name) - self._set_status(f"Opened: {name}") - self._log(f"Opened {path}") + self._log(f"{self._t('opened')} {name}", "success") except Exception as e: - self._log(f"Error: {e}", "error") - + self._log(str(e), "error") + def _open_folder(self): - path = filedialog.askdirectory(initialdir=ENGINE_DIR) - if path: self._log(f"Folder: {path}") - + filedialog.askdirectory(initialdir=ENGINE_DIR) + def _save_file(self): - if not self.current_file: - self._save_as() - return + if not self.current_file: self._save_as(); return try: - content = self.code_text.get("1.0", tk.END) with open(self.current_file, "w", encoding="utf-8") as f: - f.write(content) + f.write(self.code_text.get("1.0", tk.END)) name = os.path.basename(self.current_file) self.notebook.tab(self.editor_frame, text=name) - self._log(f"Saved {name}", "success") - self._set_status(f"Saved: {name}") + self._log(f"{self._t('saved')} {name}", "success") except Exception as e: - self._log(f"Save error: {e}", "error") - + self._log(str(e), "error") + def _save_as(self): - path = filedialog.asksaveasfilename( - initialdir=ENGINE_DIR, - defaultextension=".py", - filetypes=[("Python", "*.py"), ("C Source", "*.c"), ("All", "*.*")] - ) - if path: - self.current_file = path - self._save_file() - + p = filedialog.asksaveasfilename(initialdir=ENGINE_DIR, defaultextension=".py", filetypes=[("Python", "*.py"), ("All", "*.*")]) + if p: self.current_file = p; self._save_file() + def _run_game(self): exe = os.path.join(ENGINE_DIR, "build", "lity.exe") - if os.path.exists(exe): - self._run_process([exe]) - else: - self._log("lity.exe not found. Build first (F7).", "error") - + if os.path.exists(exe): self._run_process([exe]) + else: self._log(self._t("build_first"), "error") + def _run_current(self): - if not self.current_file: - self._log("No file open", "error"); return + if not self.current_file: self._log(self._t("no_file"), "error"); return ext = os.path.splitext(self.current_file)[1] - if ext == ".py": - self._run_process([sys.executable, self.current_file]) - elif ext in (".ly", ".lity"): - interp = os.path.join(ENGINE_DIR, "build", "ly_interpreter.exe") - if os.path.exists(interp): - self._run_process([interp, self.current_file]) - else: - self._log("ly_interpreter not built", "error") - else: - self._log(f"Cannot run {ext} files", "error") - + if ext == ".py": self._run_process([sys.executable, self.current_file]) + else: self._log(self._t("cant_run"), "error") + def _run_scene(self): - scene = os.path.join(ENGINE_DIR, "scene.txt") - if not os.path.exists(scene): - self._log("No scene.txt found", "error"); return exe = os.path.join(ENGINE_DIR, "build", "lity.exe") - if os.path.exists(exe): - self._run_process([exe]) - else: - self._log("Build engine first (F7)", "error") - + if os.path.exists(exe): self._run_process([exe]) + else: self._log(self._t("build_first"), "error") + def _run_process(self, cmd): self._kill_process() - self._log(f"Running: {' '.join(cmd)}") - self.process_label.config(text="Running...") + self._log(f"> {' '.join(os.path.basename(c) for c in cmd)}") + self.process_label.config(text=self._t("running")) try: - self.process = subprocess.Popen( - cmd, cwd=ENGINE_DIR, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - creationflags=subprocess.CREATE_NO_WINDOW - ) - self._poll_process() + self.process = subprocess.Popen(cmd, cwd=ENGINE_DIR, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, creationflags=subprocess.CREATE_NO_WINDOW) + self._poll() except Exception as e: - self._log(f"Launch error: {e}", "error") + self._log(str(e), "error") self.process_label.config(text="") - - def _poll_process(self): + + def _poll(self): if self.process and self.process.poll() is None: line = self.process.stdout.readline() if line: self._log(line.decode(errors="replace").rstrip()) - self.root.after(50, self._poll_process) + self.root.after(50, self._poll) elif self.process: - code = self.process.returncode - tag = "success" if code == 0 else "error" - self._log(f"Process exited with code {code}", tag) + self._log(f"{self._t('process_exit')} {self.process.returncode}", "success" if self.process.returncode == 0 else "error") self.process_label.config(text="") self.process = None - + def _kill_process(self): if self.process: self.process.kill() - self._log("Process killed", "error") + self._log(self._t("kill_done"), "error") self.process_label.config(text="") self.process = None - + def _build_engine(self): - self._log("Building engine with CMake...") - cmake_dir = os.path.join(ENGINE_DIR, "build") - os.makedirs(cmake_dir, exist_ok=True) - self._run_process(["cmake", "--build", cmake_dir]) - + d = os.path.join(ENGINE_DIR, "build") + os.makedirs(d, exist_ok=True) + self._run_process(["cmake", "--build", d]) + def _build_exe(self): self._save_file() exe = os.path.join(ENGINE_DIR, "build", "lity.exe") if os.path.exists(exe): - out = filedialog.asksaveasfilename( - initialdir=ENGINE_DIR, - defaultextension=".exe", - filetypes=[("Executable", "*.exe")] - ) + out = filedialog.asksaveasfilename(initialdir=ENGINE_DIR, defaultextension=".exe", filetypes=[("EXE", "*.exe")]) if out: import shutil shutil.copy2(exe, out) - self._log(f"Packaged to {out}", "success") + self._log(f"{self._t('packaged')} {out}", "success") else: - self._log("Build engine first", "error") - + self._log(self._t("build_first"), "error") + def _new_scene(self): - scene = {"entities": [], "camera": {"pos": [0,5,-10], "target": [0,0,0]}} - path = filedialog.asksaveasfilename( - initialdir=ENGINE_DIR, defaultextension=".json", - filetypes=[("Scene", "*.json")] - ) + scene = {"entities": [], "camera": {"pos": [0, 5, -10], "target": [0, 0, 0]}} + path = filedialog.asksaveasfilename(initialdir=ENGINE_DIR, defaultextension=".json", filetypes=[("Scene", "*.json")]) if path: with open(path, "w") as f: json.dump(scene, f, indent=2) self.current_file = path self._open_file_at(path) - self._log(f"New scene: {os.path.basename(path)}", "success") - + self._log(self._t("new_scene_ok"), "success") + def _open_scene(self): - path = filedialog.askopenfilename( - initialdir=ENGINE_DIR, - filetypes=[("Scene", "*.json *.txt")] - ) - if path: self._open_file_at(path) - + p = filedialog.askopenfilename(initialdir=ENGINE_DIR, filetypes=[("Scene", "*.json *.txt")]) + if p: self._open_file_at(p) + def _save_scene(self): self._save_file() - + def _open_docs(self): - docs = os.path.join(ENGINE_DIR, "Engine", "Scripting", "lity.py") - if os.path.exists(docs): - self._open_file_at(docs) - else: - self._log("LityPy docs not found", "error") - - def _load_recent(self): - self._set_status("Lity3D Editor v1.0 | F5=Run | F7=Build | Ctrl+S=Save") - + d = os.path.join(ENGINE_DIR, "Engine", "Scripting", "lity.py") + if os.path.exists(d): self._open_file_at(d) + def _on_close(self): - if self.process: - self.process.kill() + if self.process: self.process.kill() self.root.destroy() - + def run(self): self.root.mainloop() if __name__ == "__main__": - editor = LityEditor() - editor.run() + LityEditor().run()