Implement MaMa machine
This commit is contained in:
145
Uebung-01/MaMaGUI.py
Normal file
145
Uebung-01/MaMaGUI.py
Normal file
@@ -0,0 +1,145 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from typing import Dict, List
|
||||
from MaMa import MaMa
|
||||
|
||||
class MaMaGUI:
|
||||
def __init__(self, mama: MaMa, delay: int = 400) -> None:
|
||||
self.machine = mama
|
||||
self.delay = delay
|
||||
self.index = 0
|
||||
self.journal: List[Dict[str, object]] = []
|
||||
|
||||
# Tracks automatic running state
|
||||
self.running = False
|
||||
|
||||
# Run machine once and include initial configuration
|
||||
self.journal = self.machine.run()
|
||||
if not self.journal:
|
||||
raise ValueError("Journal is empty. Make sure MaMa executed successfully.")
|
||||
print(self.journal)
|
||||
|
||||
self.root = tk.Tk()
|
||||
self.root.title("MaMa GUI")
|
||||
|
||||
# Prepare rows and columns
|
||||
container = ttk.Frame(self.root, padding=10)
|
||||
container.grid(sticky="nsew")
|
||||
container.columnconfigure(0, weight=1)
|
||||
container.columnconfigure(1, weight=1)
|
||||
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=30, height=22, font=("Consolas", 11))
|
||||
self.prog_list.pack(fill="both", expand=True)
|
||||
|
||||
# Stack and configuration 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)
|
||||
stack_frame.columnconfigure(0, weight=1)
|
||||
self.stack_list = tk.Listbox(stack_frame, width=32, height=20, font=("Consolas", 11))
|
||||
self.stack_list.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
# Configuration label
|
||||
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))
|
||||
|
||||
# Init and bind controls
|
||||
control = ttk.Frame(self.root, padding=5)
|
||||
control.grid(row=1, column=0, columnspan=2, sticky="ew")
|
||||
|
||||
ttk.Button(control, text="▶ Start", command=self.start).pack(side="left", padx=5)
|
||||
ttk.Button(control, text="⏸ Stop", command=self.stop).pack(side="left", padx=5)
|
||||
ttk.Button(control, text="⏭ Next", command=self.next_step).pack(side="left", padx=5)
|
||||
ttk.Button(control, text="↩ Prev", command=self.prev_step).pack(side="left", padx=5)
|
||||
ttk.Button(control, text="⟲ Reset", command=self.reset).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.set(self.delay)
|
||||
self.speed_scale.pack(side="left", padx=5)
|
||||
|
||||
# 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")
|
||||
|
||||
# Initial update
|
||||
self.__update()
|
||||
|
||||
def __update(self) -> None:
|
||||
state = self.journal[self.index]
|
||||
prog = self.machine.prog
|
||||
p_prog = state["p_prog"]
|
||||
p_stack = state["p_stack"]
|
||||
stack = state["stack"]
|
||||
step = state["step"]
|
||||
call = state["call"]
|
||||
|
||||
# Program display
|
||||
self.prog_list.delete(0, tk.END)
|
||||
for addr, instr in prog.items():
|
||||
prefix = ">> " if addr == p_prog else " "
|
||||
self.prog_list.insert(tk.END, f"{prefix}{addr:>3}: {instr}")
|
||||
if addr == p_prog:
|
||||
self.prog_list.itemconfig(tk.END, {"bg": "#ffd966"})
|
||||
|
||||
# Stack display
|
||||
self.stack_list.delete(0, tk.END)
|
||||
if stack:
|
||||
for i in range(p_stack, -1, -1):
|
||||
val = stack.get(i, 0)
|
||||
mark = "<-- top" if i == p_stack else ""
|
||||
self.stack_list.insert(tk.END, f"[{i:>2}] {val:>6} {mark}")
|
||||
if i == p_stack:
|
||||
self.stack_list.itemconfig(tk.END, {"bg": "#cfe2f3"})
|
||||
else:
|
||||
self.stack_list.insert(tk.END, "(empty)")
|
||||
|
||||
# Configuration line
|
||||
conf_str = f"({p_stack}, {p_prog}, {list(stack.values())})"
|
||||
self.conf_label.config(text=conf_str)
|
||||
|
||||
# Status update
|
||||
status = f"Step {step}/{len(self.journal)-1} | PC={p_prog} | SP={p_stack} | Last call: {call}"
|
||||
self.status.config(text=status)
|
||||
|
||||
def __update_speed(self, value: str) -> None:
|
||||
self.delay = int(float(value))
|
||||
|
||||
def start(self) -> None:
|
||||
if not self.running:
|
||||
self.running = True
|
||||
self.__auto_step()
|
||||
|
||||
def stop(self) -> None:
|
||||
self.running = False
|
||||
|
||||
def __auto_step(self) -> None:
|
||||
if self.running and self.index < len(self.journal) - 1:
|
||||
self.index += 1
|
||||
self.__update()
|
||||
self.root.after(self.delay, self.__auto_step)
|
||||
else:
|
||||
self.running = False
|
||||
|
||||
def next_step(self) -> None:
|
||||
if self.index < len(self.journal) - 1:
|
||||
self.index += 1
|
||||
self.__update()
|
||||
|
||||
def prev_step(self) -> None:
|
||||
if self.index > 0:
|
||||
self.index -= 1
|
||||
self.__update()
|
||||
|
||||
def reset(self) -> None:
|
||||
self.machine.reload()
|
||||
self.journal = self.machine.run()
|
||||
self.index = 0
|
||||
self.__update()
|
||||
|
||||
def display(self) -> None:
|
||||
self.root.mainloop()
|
||||
Reference in New Issue
Block a user