Finish ggt task
This commit is contained in:
@@ -5,112 +5,92 @@ from MaMa import MaMa
|
||||
|
||||
class MaMaGUI:
|
||||
def __init__(self, mama: MaMa, delay: int = 400) -> None:
|
||||
# Autoplay speed
|
||||
self.delay = delay
|
||||
|
||||
# Rather autoplay is activated
|
||||
self.do_autoplay = False
|
||||
|
||||
# Run MaMa and create journal
|
||||
self.machine = mama
|
||||
self.journal: List[Dict[str, object]] = self.machine.run()
|
||||
self.journal = self.machine.run()
|
||||
if not self.journal:
|
||||
raise ValueError("Journal is empty.")
|
||||
self.journal_index = 0
|
||||
|
||||
# Bootstrap UI
|
||||
# Root window
|
||||
self.root = tk.Tk()
|
||||
self.root.title("MaMa GUI")
|
||||
self.root.geometry("1000x600")
|
||||
self.root.columnconfigure(0, weight=1)
|
||||
self.root.rowconfigure(0, weight=1)
|
||||
self.root.rowconfigure(1, weight=0)
|
||||
self.root.rowconfigure(2, weight=0)
|
||||
|
||||
# Main flex container
|
||||
container = ttk.Frame(self.root, padding=10)
|
||||
container.grid(sticky="nsew")
|
||||
container.columnconfigure((0, 1), weight=1)
|
||||
container.grid(row=0, column=0, sticky="nsew")
|
||||
container.columnconfigure(0, weight=3)
|
||||
container.columnconfigure(1, weight=2)
|
||||
container.rowconfigure(0, weight=1)
|
||||
|
||||
# Program display
|
||||
prog_frame = ttk.LabelFrame(container, text="Program", padding=5)
|
||||
prog_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 10))
|
||||
self.prog_list = tk.Listbox(prog_frame, width=70, height=24, font=("Consolas", 11))
|
||||
self.prog_list.pack(fill="both", expand=True)
|
||||
prog_frame.rowconfigure(0, weight=1)
|
||||
prog_frame.columnconfigure(0, weight=1)
|
||||
self.prog_list = tk.Listbox(prog_frame, font=("Consolas", 11))
|
||||
self.prog_list.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
# Stack display
|
||||
stack_frame = ttk.LabelFrame(container, text="Stack", padding=5)
|
||||
stack_frame.grid(row=0, column=1, sticky="nsew")
|
||||
stack_frame.rowconfigure(0, weight=1)
|
||||
self.stack_list = tk.Listbox(stack_frame, width=36, height=22, font=("Consolas", 11))
|
||||
stack_frame.columnconfigure(0, weight=1)
|
||||
self.stack_list = tk.Listbox(stack_frame, font=("Consolas", 11))
|
||||
self.stack_list.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
# Configuration display
|
||||
self.conf_label = ttk.Label(stack_frame, text="(s, p, S) = (-, -, [])", font=("Consolas", 11), anchor="e")
|
||||
self.conf_label = ttk.Label(stack_frame, text="(s, p, S) = (-, -, [])",
|
||||
font=("Consolas", 11), anchor="e")
|
||||
self.conf_label.grid(row=1, column=0, sticky="ew", pady=(4, 0))
|
||||
|
||||
# Create and bind controls
|
||||
# Control buttons
|
||||
control = ttk.Frame(self.root, padding=5)
|
||||
control.grid(row=1, column=0, columnspan=2, sticky="ew")
|
||||
btns = [
|
||||
control.grid(row=1, column=0, sticky="ew")
|
||||
control.columnconfigure(0, weight=1)
|
||||
for text, cmd in [
|
||||
("▶ Start", self.__start),
|
||||
("⏸ Stop", self.__stop),
|
||||
("⏭ Next", self.__next_step),
|
||||
("↩ Prev", self.__prev_step),
|
||||
("⟲ Reset", self.__reset)
|
||||
]
|
||||
|
||||
for text, cmd in btns:
|
||||
("⟲ Reset", self.__reset),
|
||||
]:
|
||||
ttk.Button(control, text=text, command=cmd).pack(side="left", padx=5)
|
||||
|
||||
ttk.Label(control, text="Speed:").pack(side="left", padx=5)
|
||||
self.speed_scale = ttk.Scale(control, from_=2000, to=100, orient="horizontal", command=self.__update_speed)
|
||||
self.speed_scale = ttk.Scale(control, from_=2000, to=100,
|
||||
orient="horizontal", command=self.__update_speed)
|
||||
self.speed_scale.set(self.delay)
|
||||
self.speed_scale.pack(side="left", padx=5)
|
||||
self.speed_scale.pack(side="left", padx=5, fill="x", expand=True)
|
||||
|
||||
# Global status bar
|
||||
# Status bar
|
||||
self.status = ttk.Label(self.root, text="Ready", padding=5, anchor="w")
|
||||
self.status.grid(row=2, column=0, columnspan=2, sticky="ew")
|
||||
self.status.grid(row=2, column=0, sticky="ew")
|
||||
|
||||
self.__update()
|
||||
|
||||
# --- UI update ---
|
||||
def __update(self) -> None:
|
||||
# Load current journal entry
|
||||
# Current journal state
|
||||
state = self.journal[self.journal_index]
|
||||
stack = dict(state["stack"])
|
||||
p_prog, p_stack = int(state["p_prog"]), int(state["p_stack"])
|
||||
step, micro = int(state["step"]), str(state["micro"])
|
||||
step, micro = int(state["step"]), str(state["micro"])
|
||||
|
||||
# Load code structure
|
||||
structure = self.machine.structure()
|
||||
# --- Program display ---
|
||||
self.prog_list.delete(0, tk.END)
|
||||
opened_macros: List[str] = []
|
||||
macro_offset = 4
|
||||
|
||||
# Display code lines and unpack macros
|
||||
for addr, info in structure.items():
|
||||
macros = info.get("macros", [])
|
||||
depth = len(macros)
|
||||
|
||||
# Align macro headers at parent's instruction column
|
||||
if len(macros) > len(opened_macros):
|
||||
parent_depth = len(macros) - 1
|
||||
header_indent = " " * parent_depth + " " * macro_offset
|
||||
macro = macros[-1]
|
||||
if isinstance(macro, dict):
|
||||
params = ", ".join(f"{k}={v}" for k, v in macro.get("params", {}).items())
|
||||
header = f"{macro['name']}({params})" if params else macro["name"]
|
||||
else:
|
||||
header = str(macro)
|
||||
self.prog_list.insert(tk.END, f"{header_indent}[{header}]")
|
||||
self.prog_list.itemconfig(tk.END, {"bg": "#e6e6e6"})
|
||||
|
||||
instr_indent = " " * depth
|
||||
for addr, instr in sorted(self.machine.prog.items()):
|
||||
prefix = ">> " if addr == p_prog else " "
|
||||
line = f"{prefix}{instr_indent}{addr:>3}: {info['micro']}"
|
||||
line = f"{prefix}{addr:>3}: {instr}"
|
||||
self.prog_list.insert(tk.END, line)
|
||||
opened_macros = macros
|
||||
|
||||
# Highlight current program line
|
||||
if addr == p_prog:
|
||||
self.prog_list.itemconfig(tk.END, {"bg": "#ffd966"})
|
||||
|
||||
# Visualize current stack
|
||||
# --- Stack display ---
|
||||
self.stack_list.delete(0, tk.END)
|
||||
if stack:
|
||||
for i in range(p_stack, -1, -1):
|
||||
@@ -122,25 +102,25 @@ class MaMaGUI:
|
||||
else:
|
||||
self.stack_list.insert(tk.END, "Empty")
|
||||
|
||||
# Update configuration and global stack
|
||||
# --- Configuration & status ---
|
||||
self.conf_label.config(text=f"({p_stack}, {p_prog}, {list(stack.values())})")
|
||||
self.status.config(text=f"Step {step}/{len(self.journal)-1} | PC={p_prog} | SP={p_stack} | Micro: {micro}")
|
||||
self.status.config(
|
||||
text=f"Step {step}/{len(self.journal)-1} | PC={p_prog} | SP={p_stack} | Micro: {micro}"
|
||||
)
|
||||
|
||||
# Configure auto step speed
|
||||
# --- Speed control ---
|
||||
def __update_speed(self, value: str) -> None:
|
||||
self.delay = int(float(value))
|
||||
|
||||
# Start autoplay
|
||||
# --- Autoplay controls ---
|
||||
def __start(self) -> None:
|
||||
if not self.do_autoplay:
|
||||
self.do_autoplay = True
|
||||
self.__auto_step()
|
||||
|
||||
# Stop autoplay
|
||||
def __stop(self) -> None:
|
||||
self.do_autoplay = False
|
||||
|
||||
# Play an autoplay step
|
||||
def __auto_step(self) -> None:
|
||||
if self.do_autoplay and self.journal_index < len(self.journal) - 1:
|
||||
self.journal_index += 1
|
||||
@@ -149,23 +129,21 @@ class MaMaGUI:
|
||||
else:
|
||||
self.do_autoplay = False
|
||||
|
||||
# Play next step
|
||||
# --- Step navigation ---
|
||||
def __next_step(self) -> None:
|
||||
if self.journal_index < len(self.journal) - 1:
|
||||
self.journal_index += 1
|
||||
self.__update()
|
||||
|
||||
# Play previous step
|
||||
def __prev_step(self) -> None:
|
||||
if self.journal_index > 0:
|
||||
self.journal_index -= 1
|
||||
self.__update()
|
||||
|
||||
# Reset machine
|
||||
def __reset(self) -> None:
|
||||
self.journal_index = 0
|
||||
self.__update()
|
||||
|
||||
# Run main loop
|
||||
# --- Run GUI ---
|
||||
def display(self) -> None:
|
||||
self.root.mainloop()
|
||||
|
||||
Reference in New Issue
Block a user