Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # tk_step_debug.py
- import tkinter as tk
- from tkinter import scrolledtext
- import sys
- import io
- import copy
- steps = []
- current_step_idx = -1
- stdout_buffer = io.StringIO()
- source_lines = []
- auto_stepping = False
- drag_start_x = 0
- drag_start_y = 0
- BG_DARK = "#181824"
- BG_PANEL = "#1e1e2e"
- BG_INPUTS = "#28283d"
- FG_LIGHT = "#cdd6f4"
- FG_MUTED = "#a6adc8"
- ACCENT_BLUE = "#89b4fa"
- ACCENT_GREEN = "#a6e3a1"
- CHANGED_BG_LINE = "#008800"
- CHANGED_BG_BOX = "#1e2e24"
- CHANGED_BORDER = "#40a02b"
- def trace_calls(frame, event, arg):
- if event not in ('line', 'call', 'return', 'exception'):
- return trace_calls
- stack = []
- f = frame
- while f:
- stack.append(f"{f.f_code.co_name} (line {f.f_lineno})")
- f = f.f_back
- stack.reverse()
- step_data = {
- 'line_no': frame.f_lineno,
- 'event': event,
- 'stdout': stdout_buffer.getvalue(),
- 'locals': {k: copy.deepcopy(v) for k, v in frame.f_locals.items() if not k.startswith('__')},
- 'globals': {k: copy.deepcopy(v) for k, v in frame.f_globals.items() if not k.startswith('__')},
- 'stack_frames': stack
- }
- steps.append(step_data)
- return trace_calls
- def compile_and_start():
- global steps, current_step_idx, stdout_buffer, source_lines, auto_stepping
- auto_stepping = False
- btn_play.config(text="Auto Play")
- code = code_editor.get("1.0", tk.END).strip()
- if not code:
- return
- source_lines = code.splitlines()
- steps = []
- current_step_idx = -1
- stdout_buffer = io.StringIO()
- original_stdout = sys.stdout
- sys.stdout = stdout_buffer
- try:
- compiled = compile(code, '<string>', 'exec')
- sys.settrace(trace_calls)
- exec(compiled, {})
- except Exception as e:
- sys.settrace(None)
- stdout_buffer.write(f"\n{type(e).__name__}: {e}\n")
- finally:
- sys.settrace(None)
- sys.stdout = original_stdout
- if steps:
- current_step_idx = 0
- for btn in (btn_first, btn_back10, btn_back, btn_step, btn_step10, btn_last, btn_play):
- btn.config(state="normal")
- sync_ui_to_step()
- def sync_ui_to_step():
- if not steps or current_step_idx < 0:
- return
- step = steps[current_step_idx]
- step_label.config(text=f"Step {current_step_idx + 1} of {len(steps)} ({step['event']})")
- code_editor.tag_remove("current_line", "1.0", tk.END)
- code_editor.tag_add("current_line", f"{step['line_no']}.0", f"{step['line_no']}.end")
- code_editor.tag_config("current_line", background=CHANGED_BG_LINE, foreground="#ffffff")
- code_editor.see(f"{step['line_no']}.0")
- console_output.config(state="normal")
- console_output.delete("1.0", tk.END)
- console_output.insert("1.0", step['stdout'])
- console_output.config(state="disabled")
- draw_objects_diagram(step)
- def draw_objects_diagram(step):
- canvas.delete("all")
- x_offset = 30
- y_cursor = 40
- changed_vars = set()
- if current_step_idx > 0:
- prev_step = steps[current_step_idx - 1]
- prev_vars = {}
- prev_vars.update(prev_step['globals'])
- prev_vars.update(prev_step['locals'])
- curr_vars = {}
- curr_vars.update(step['globals'])
- curr_vars.update(step['locals'])
- for k, v in curr_vars.items():
- if k not in prev_vars or prev_vars[k] != v:
- changed_vars.add(k)
- canvas.create_text(x_offset, y_cursor, text="Frames & Dynamic Heap Variables (Stack Timeline)", font=("Arial", 12, "bold"), anchor="w", fill=FG_LIGHT)
- y_cursor += 40
- if step['stack_frames']:
- canvas.create_text(x_offset, y_cursor, text="→ Active Call Stack:", font=("Arial", 10, "bold"), anchor="w", fill=ACCENT_BLUE)
- y_cursor += 20
- for frame_name in reversed(step['stack_frames']):
- canvas.create_rectangle(x_offset, y_cursor, x_offset + 350, y_cursor + 28, fill="#242535", outline="#45475a", width=1)
- canvas.create_text(x_offset + 10, y_cursor + 14, text=frame_name, font=("Consolas", 10, "bold"), anchor="w", fill=ACCENT_BLUE)
- y_cursor += 34
- y_cursor += 15
- active_variables = {}
- active_variables.update(step['globals'])
- active_variables.update(step['locals'])
- filtered_vars = {k: v for k, v in active_variables.items() if str(type(v)) not in ("<class 'module'>", "<class 'function'>")}
- if filtered_vars:
- canvas.create_text(x_offset, y_cursor, text="→ Variables Map:", font=("Arial", 10, "bold"), anchor="w", fill=FG_MUTED)
- y_cursor += 25
- for key, val in filtered_vars.items():
- is_changed = key in changed_vars
- var_box_bg = CHANGED_BG_BOX if is_changed else "#242535"
- var_box_outline = CHANGED_BORDER if is_changed else "#45475a"
- var_box_width = 2 if is_changed else 1
- canvas.create_rectangle(x_offset, y_cursor, x_offset + 500, y_cursor + 24, fill=var_box_bg, outline=var_box_outline, width=var_box_width)
- lbl_suffix = " [UPDATED]" if is_changed else ""
- lbl_color = ACCENT_GREEN if is_changed else FG_LIGHT
- canvas.create_text(x_offset + 10, y_cursor + 12, text=f"Variable Name: {key}{lbl_suffix}", font=("Consolas", 10, "bold"), anchor="w", fill=lbl_color)
- y_cursor += 24
- if isinstance(val, (list, dict, set, tuple)):
- val_str = f"[{type(val).__name__}] {str(val)}"
- box_color = CHANGED_BG_BOX if is_changed else "#24202a"
- border_color = CHANGED_BORDER if is_changed else "#6c7086"
- text_color = ACCENT_GREEN if is_changed else "#f5c2e7"
- else:
- val_str = repr(val)
- box_color = CHANGED_BG_BOX if is_changed else "#1e222a"
- border_color = CHANGED_BORDER if is_changed else "#45475a"
- text_color = ACCENT_GREEN if is_changed else "#b4befe"
- canvas.create_rectangle(x_offset, y_cursor, x_offset + 500, y_cursor + 28, fill=box_color, outline=border_color, width=var_box_width)
- canvas.create_text(x_offset + 10, y_cursor + 14, text=f"Value ➔ {val_str}", font=("Consolas", 10), anchor="w", fill=text_color)
- y_cursor += 42
- canvas.config(scrollregion=(0, 0, 700, max(y_cursor + 100, 800)))
- def step_forward():
- global current_step_idx, auto_stepping
- if current_step_idx < len(steps) - 1:
- current_step_idx += 1
- sync_ui_to_step()
- else:
- auto_stepping = False
- btn_play.config(text="Auto Play")
- def step_forward_10():
- global current_step_idx, auto_stepping
- if steps:
- current_step_idx = min(len(steps) - 1, current_step_idx + 10)
- sync_ui_to_step()
- if current_step_idx == len(steps) - 1:
- auto_stepping = False
- btn_play.config(text="Auto Play")
- def step_back():
- global current_step_idx
- if current_step_idx > 0:
- current_step_idx -= 1
- sync_ui_to_step()
- def step_back_10():
- global current_step_idx
- if steps:
- current_step_idx = max(0, current_step_idx - 10)
- sync_ui_to_step()
- def go_to_first():
- global current_step_idx
- if steps:
- current_step_idx = 0
- sync_ui_to_step()
- def go_to_last():
- global current_step_idx
- if steps:
- current_step_idx = len(steps) - 1
- sync_ui_to_step()
- def toggle_play():
- global auto_stepping
- if auto_stepping:
- auto_stepping = False
- btn_play.config(text="Auto Play")
- else:
- auto_stepping = True
- btn_play.config(text="Pause ⏸")
- play_loop()
- def play_loop():
- if not auto_stepping:
- return
- step_forward()
- if current_step_idx < len(steps) - 1:
- interval = int(1100 - (speed_slider.get() * 100))
- root.after(interval, play_loop)
- def start_drag(event):
- global drag_start_x, drag_start_y
- drag_start_x = event.x
- drag_start_y = event.y
- def do_drag(event):
- global drag_start_x, drag_start_y
- dx = event.x - drag_start_x
- dy = event.y - drag_start_y
- canvas.xview_scroll(-dx, "units")
- canvas.yview_scroll(-dy, "units")
- drag_start_x = event.x
- drag_start_y = event.y
- root = tk.Tk()
- root.title("# tk_step_debug.py")
- root.geometry("1400x750+-10+0")
- root.configure(bg=BG_DARK)
- toolbar = tk.Frame(root, bg=BG_PANEL, bd=0)
- toolbar.pack(fill="x", side="top", ipady=6)
- btn_run = tk.Button(toolbar, text="Visualize Execution", bg="#008800", fg="#ffffff", activebackground="#a6e3a1", font=("Arial", 10, "bold"), relief=tk.FLAT, command=compile_and_start)
- btn_run.pack(side="left", padx=12, pady=5)
- btn_first = tk.Button(toolbar, text="<< First", bg=BG_INPUTS, fg=FG_LIGHT, disabledforeground="#585b70", activebackground=CHANGED_BG_LINE, activeforeground=FG_LIGHT, relief=tk.FLAT, state="disabled", command=go_to_first)
- btn_back10 = tk.Button(toolbar, text="-10 Steps", bg=BG_INPUTS, fg=FG_LIGHT, disabledforeground="#585b70", activebackground=CHANGED_BG_LINE, activeforeground=FG_LIGHT, relief=tk.FLAT, state="disabled", command=step_back_10)
- btn_back = tk.Button(toolbar, text="< -1 Step", bg=BG_INPUTS, fg=FG_LIGHT, disabledforeground="#585b70", activebackground=CHANGED_BG_LINE, activeforeground=FG_LIGHT, relief=tk.FLAT, state="disabled", command=step_back)
- btn_step = tk.Button(toolbar, text="+1 Step >", bg=BG_INPUTS, fg=FG_LIGHT, disabledforeground="#585b70", activebackground=CHANGED_BG_LINE, activeforeground=FG_LIGHT, relief=tk.FLAT, state="disabled", command=step_forward)
- btn_step10 = tk.Button(toolbar, text="+10 Steps", bg=BG_INPUTS, fg=FG_LIGHT, disabledforeground="#585b70", activebackground=CHANGED_BG_LINE, activeforeground=FG_LIGHT, relief=tk.FLAT, state="disabled", command=step_forward_10)
- btn_last = tk.Button(toolbar, text="Last >>", bg=BG_INPUTS, fg=FG_LIGHT, disabledforeground="#585b70", activebackground=CHANGED_BG_LINE, activeforeground=FG_LIGHT, relief=tk.FLAT, state="disabled", command=go_to_last)
- for btn in (btn_first, btn_back10, btn_back, btn_step, btn_step10, btn_last):
- btn.pack(side="left", padx=3)
- step_label = tk.Label(toolbar, text="Step 0 of 0", bg=BG_PANEL, fg=FG_MUTED, font=("Consolas", 10))
- step_label.pack(side="left", padx=15)
- tk.Label(toolbar, text="Auto Speed:", bg=BG_PANEL, fg=FG_MUTED).pack(side="left", padx=(15, 0))
- speed_slider = tk.Scale(toolbar, from_=1, to=10, orient=tk.HORIZONTAL, length=150, showvalue=False, bg=BG_PANEL, fg=FG_LIGHT, highlightthickness=0, troughcolor=BG_INPUTS, activebackground=ACCENT_BLUE)
- speed_slider.set(5)
- speed_slider.pack(side="left", padx=5)
- btn_play = tk.Button(toolbar, text="Auto Play", bg=BG_INPUTS, fg=FG_LIGHT, disabledforeground="#585b70", relief=tk.FLAT, state="disabled", command=toggle_play)
- btn_play.pack(side="left", padx=5)
- main_pane = tk.PanedWindow(root, orient=tk.HORIZONTAL, bg=BG_DARK, bd=0, sashwidth=4)
- main_pane.pack(fill="both", expand=True)
- left_frame = tk.Frame(main_pane, bg=BG_PANEL)
- main_pane.add(left_frame, minsize=450)
- tk.Label(left_frame, text=" Python Source Code Editor", anchor="w", font=("Arial", 10, "bold"), bg=BG_DARK, fg=FG_MUTED).pack(fill="x", ipady=4)
- code_editor = scrolledtext.ScrolledText(left_frame, wrap=tk.NONE, font=("Consolas", 11), bg=BG_INPUTS, fg=FG_LIGHT, insertbackground="white", bd=0, highlightthickness=0)
- code_editor.pack(fill="both", expand=True)
- sample_code = (
- "def factorial(n):\n"
- " if n <= 1:\n"
- " return 1\n"
- " return n * factorial(n - 1)\n\n"
- "data = [3, 4]\n"
- "results = []\n"
- "for val in data:\n"
- " res = factorial(val)\n"
- " results.append(res)\n\n"
- "print(f\"Final Results: {results}\")\n"
- )
- code_editor.insert("1.0", sample_code)
- tk.Label(left_frame, text=" Print Output (Console)", anchor="w", font=("Arial", 10, "bold"), bg=BG_DARK, fg=FG_MUTED).pack(fill="x", pady=(5,0), ipady=4)
- console_output = scrolledtext.ScrolledText(left_frame, height=8, font=("Consolas", 10), bg="#11111b", fg=ACCENT_GREEN, bd=0, highlightthickness=0)
- console_output.pack(fill="x")
- right_frame = tk.Frame(main_pane, bg=BG_PANEL)
- main_pane.add(right_frame, minsize=850)
- tk.Label(right_frame, text=" Dynamic Heap Stack Monitor Panel", anchor="w", font=("Arial", 10, "bold"), bg=BG_DARK, fg=FG_MUTED).pack(fill="x", ipady=4)
- canvas = tk.Canvas(right_frame, bg=BG_INPUTS, highlightthickness=0, xscrollincrement=1, yscrollincrement=1)
- canvas.pack(fill="both", expand=True, side="left")
- canvas_vbar = tk.Scrollbar(right_frame, orient="vertical", command=canvas.yview, bg=BG_PANEL)
- canvas_vbar.pack(fill="y", side="right")
- canvas_hbar = tk.Scrollbar(right_frame, orient="horizontal", command=canvas.xview, bg=BG_PANEL)
- canvas_hbar.pack(fill="x", side="bottom")
- canvas.config(yscrollcommand=canvas_vbar.set, xscrollcommand=canvas_hbar.set)
- canvas.bind("<ButtonPress-1>", start_drag)
- canvas.bind("<B1-Motion>", do_drag)
- root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment