Skip to content

Commit 61b480f

Browse files
committed
added files to be tracked
1 parent ceb8c09 commit 61b480f

33 files changed

+1780
-0
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

HospitalKarel_S313/karel/Karel.py

+428
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
"""
2+
This file defines the GUI for running Karel programs.
3+
4+
Original Author: Nicholas Bowman
5+
Credits: Kylie Jue
6+
License: MIT
7+
Version: 1.0.0
8+
Email: nbowman@stanford.edu
9+
Date of Creation: 10/1/2019
10+
Last Modified: 3/31/2020
11+
"""
12+
13+
import tkinter as tk
14+
from karel.kareldefinitions import *
15+
from karel.KarelCanvas import KarelCanvas
16+
from time import sleep
17+
from tkinter.filedialog import askopenfilename
18+
from tkinter.messagebox import showerror, showwarning
19+
import os
20+
import traceback as tb
21+
import inspect
22+
import importlib.util
23+
import sys
24+
25+
26+
class KarelApplication(tk.Frame):
27+
def __init__(self, karel, world, code_file, master=None, window_width=800, window_height=600, canvas_width=600, canvas_height=400):
28+
# set window background to contrast white Karel canvas
29+
master.configure(background=LIGHT_GREY)
30+
31+
# configure location of canvas to expand to fit window resizing
32+
master.rowconfigure(0, weight=1)
33+
master.columnconfigure(1, weight=1)
34+
35+
# set master geometry
36+
master.geometry(str(window_width) + "x" + str(window_height))
37+
38+
super().__init__(master, background=LIGHT_GREY)
39+
40+
self.karel = karel
41+
self.world = world
42+
self.code_file = code_file
43+
if not self.load_student_module():
44+
master.destroy()
45+
return
46+
self.icon = DEFAULT_ICON
47+
self.window_width = window_width
48+
self.window_height = window_height
49+
self.canvas_width = canvas_width
50+
self.canvas_height = canvas_height
51+
self.master = master
52+
self.master.title(self.module_name)
53+
self.set_dock_icon()
54+
self.inject_namespace()
55+
self.grid(row=0, column=0)
56+
self.create_menubar()
57+
self.create_canvas()
58+
self.create_buttons()
59+
self.create_slider()
60+
self.create_status_label()
61+
62+
def set_dock_icon(self):
63+
# make Karel dock icon image
64+
img = tk.Image("photo", file="./karel/icon.png")
65+
self.master.tk.call('wm', 'iconphoto', self.master._w, img)
66+
67+
def load_student_module(self):
68+
# This process is used to extract a module from an arbitarily located
69+
# file that contains student code
70+
# Adapted from https://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path
71+
self.base_filename = os.path.basename(self.code_file)
72+
self.module_name = os.path.splitext(self.base_filename)[0]
73+
spec = importlib.util.spec_from_file_location(self.module_name, os.path.abspath(self.code_file))
74+
try:
75+
self.mod = importlib.util.module_from_spec(spec)
76+
spec.loader.exec_module(self.mod)
77+
except Exception as e:
78+
# Handle syntax errors and only print location of error
79+
print("here")
80+
print("\n".join(tb.format_exc(limit=0).split("\n")[1:]))
81+
return False
82+
83+
# Do not proceed if the student has not defined a main function
84+
if not hasattr(self.mod, "main"):
85+
print("Couldn't find the main() function. Are you sure you have one?")
86+
return False
87+
88+
return True
89+
90+
def create_menubar(self):
91+
menubar = tk.Menu(self.master)
92+
93+
fileMenu = tk.Menu(menubar, tearoff=False)
94+
menubar.add_cascade(label="File", menu=fileMenu)
95+
fileMenu.add_command(label="Exit", underline=1,
96+
command=self.master.quit, accelerator="Cmd+W")
97+
98+
iconmenu = tk.Menu(menubar, tearoff=0)
99+
menubar.add_cascade(label="Select Icon", menu=iconmenu)
100+
101+
iconmenu.add_command(label="Karel", command=lambda: self.set_icon("karel"))
102+
iconmenu.add_command(label="Simple", command=lambda: self.set_icon("simple"))
103+
104+
self.bind_all("<Command-w>", self.quit)
105+
106+
self.master.config(menu=menubar)
107+
108+
def quit(self, event):
109+
sys.exit(0)
110+
111+
def set_icon(self, icon):
112+
self.canvas.set_icon(icon)
113+
self.canvas.redraw_karel()
114+
115+
def create_slider(self):
116+
"""
117+
This method creates a frame containing three widgets:
118+
two labels on either side of a scale slider to control
119+
Karel execution speed.
120+
"""
121+
self.slider_frame = tk.Frame(self, bg=LIGHT_GREY)
122+
self.slider_frame.grid(row=3, column=0, padx=PAD_X, pady=PAD_Y, sticky="ew")
123+
124+
self.fast_label = tk.Label(self.slider_frame, text="Fast", bg=LIGHT_GREY)
125+
self.fast_label.pack(side="right")
126+
127+
self.slow_label = tk.Label(self.slider_frame, text="Slow", bg=LIGHT_GREY)
128+
self.slow_label.pack(side="left")
129+
130+
self.speed = tk.IntVar()
131+
132+
self.scale = tk.Scale(self.slider_frame, orient=tk.HORIZONTAL, variable=self.speed, showvalue=0)
133+
self.scale.set(self.world.init_speed)
134+
self.scale.pack()
135+
136+
def create_canvas(self):
137+
"""
138+
This method creates the canvas on which Karel and Karel's
139+
world are drawn.
140+
"""
141+
self.canvas = KarelCanvas(self.canvas_width, self.canvas_height, self.master, world=self.world, karel=self.karel)
142+
self.canvas.grid(column=1, row=0, sticky="NESW")
143+
self.canvas.bind("<Configure>", lambda t: self.canvas.redraw_all())
144+
145+
def create_buttons(self):
146+
"""
147+
This method creates the three buttons that appear on the left
148+
side of the screen. These buttons control the start of Karel
149+
execution, resetting Karel's state, and loading new worlds.
150+
"""
151+
self.program_control_button = tk.Button(self, highlightthickness=0, highlightbackground='white')
152+
self.program_control_button["text"] = "Run Program"
153+
self.program_control_button["command"] = self.run_program
154+
self.program_control_button.grid(column=0, row=0, padx=PAD_X, pady=PAD_Y, sticky="ew")
155+
156+
self.load_world_button = tk.Button(self, highlightthickness=0, text="Load World", command=self.load_world)
157+
self.load_world_button.grid(column=0, row=2, padx=PAD_X, pady=PAD_Y, sticky="ew")
158+
159+
def create_status_label(self):
160+
"""
161+
This function creates the status label at the bottom of the window.
162+
"""
163+
self.status_label = tk.Label(self.master, text="Welcome to Karel!", bg=LIGHT_GREY)
164+
self.status_label.grid(row=1, column=0, columnspan=2)
165+
166+
def karel_action_decorator(self, karel_fn):
167+
def wrapper():
168+
# execute Karel function
169+
karel_fn()
170+
# redraw canavs with updated state of the world
171+
self.canvas.redraw_karel()
172+
# delay by specified amount
173+
sleep(1 - self.speed.get() / 100)
174+
return wrapper
175+
176+
def beeper_action_decorator(self, karel_fn):
177+
def wrapper():
178+
# execute Karel function
179+
karel_fn()
180+
# redraw canavs with updated state of the world
181+
self.canvas.redraw_beepers()
182+
self.canvas.redraw_karel()
183+
# delay by specified amount
184+
sleep(1 - self.speed.get() / 100)
185+
return wrapper
186+
187+
def corner_action_decorator(self, karel_fn):
188+
def wrapper(color):
189+
# execute Karel function
190+
karel_fn(color)
191+
# redraw canvas with updated state of the world
192+
self.canvas.redraw_corners()
193+
self.canvas.redraw_beepers()
194+
self.canvas.redraw_karel()
195+
# delay by specified amount
196+
sleep(1 - self.speed.get() / 100)
197+
return wrapper
198+
199+
def inject_namespace(self):
200+
"""
201+
This function is responsible for doing some Python hackery
202+
that associates the generic commands the student wrote in their
203+
file with specific commands relating to the Karel object that exists
204+
in the world.
205+
"""
206+
207+
self.mod.turn_left = self.karel_action_decorator(self.karel.turn_left)
208+
self.mod.move = self.karel_action_decorator(self.karel.move)
209+
self.mod.pick_beeper = self.beeper_action_decorator(self.karel.pick_beeper)
210+
self.mod.put_beeper = self.beeper_action_decorator(self.karel.put_beeper)
211+
self.mod.facing_north = self.karel.facing_north
212+
self.mod.facing_south = self.karel.facing_south
213+
self.mod.facing_east = self.karel.facing_east
214+
self.mod.facing_west = self.karel.facing_west
215+
self.mod.not_facing_north = self.karel.not_facing_north
216+
self.mod.not_facing_south = self.karel.not_facing_south
217+
self.mod.not_facing_east = self.karel.not_facing_east
218+
self.mod.not_facing_west = self.karel.not_facing_west
219+
self.mod.front_is_clear = self.karel.front_is_clear
220+
self.mod.beepers_present = self.karel.beepers_present
221+
self.mod.no_beepers_present = self.karel.no_beepers_present
222+
self.mod.beepers_in_bag = self.karel.beepers_in_bag
223+
self.mod.no_beepers_in_bag = self.karel.no_beepers_in_bag
224+
self.mod.front_is_blocked = self.karel.front_is_blocked
225+
self.mod.left_is_clear = self.karel.left_is_clear
226+
self.mod.left_is_blocked = self.karel.left_is_blocked
227+
self.mod.right_is_clear = self.karel.right_is_clear
228+
self.mod.right_is_blocked = self.karel.right_is_blocked
229+
self.mod.paint_corner = self.corner_action_decorator(self.karel.paint_corner)
230+
self.mod.corner_color_is = self.karel.corner_color_is
231+
232+
def disable_buttons(self):
233+
self.program_control_button.configure(state="disabled")
234+
self.load_world_button.configure(state="disabled")
235+
236+
def enable_buttons(self):
237+
self.program_control_button.configure(state="normal")
238+
self.load_world_button.configure(state="normal")
239+
240+
def display_error_traceback(self, e):
241+
print("Traceback (most recent call last):")
242+
display_frames = []
243+
# walk through all the frames in stack trace at time of failure
244+
for frame, lineno in tb.walk_tb(e.__traceback__):
245+
frame_info = inspect.getframeinfo(frame)
246+
# get the name of the file corresponding to the current frame
247+
filename = frame_info.filename
248+
# Only display frames generated within the student's code
249+
if self.base_filename in filename:
250+
display_frames.append((frame, lineno))
251+
252+
print(("".join(tb.format_list(tb.StackSummary.extract(display_frames)))).strip())
253+
print(f"{type(e).__name__}: {str(e)}")
254+
255+
def run_program(self):
256+
# Error checking for existence of main function completed in prior file
257+
try:
258+
self.status_label.configure(text="Running...", fg="brown")
259+
self.disable_buttons()
260+
self.mod.main()
261+
self.status_label.configure(text="Finished running.", fg="green")
262+
263+
except (KarelException, NameError) as e:
264+
# Generate popup window to let the user know their program crashed
265+
self.status_label.configure(text="Program crashed, check console for details.", fg="red")
266+
self.display_error_traceback(e)
267+
self.update()
268+
showwarning("Karel Error", "Karel Crashed!\nCheck the terminal for more details.")
269+
270+
finally:
271+
# Update program control button to force user to reset world before running program again
272+
self.program_control_button["text"] = "Reset World"
273+
self.program_control_button["command"] = self.reset_world
274+
self.enable_buttons()
275+
276+
def reset_world(self):
277+
self.karel.reset_state()
278+
self.world.reset_world()
279+
self.canvas.redraw_all()
280+
self.status_label.configure(text="Reset to initial state.", fg="black")
281+
# Once world has been reset, program control button resets to "run" mode
282+
self.program_control_button["text"] = "Run Program"
283+
self.program_control_button["command"] = self.run_program
284+
self.update()
285+
286+
def load_world(self):
287+
filename = askopenfilename(initialdir="../worlds", title="Select Karel World", filetypes=[("Karel Worlds", "*.w")], parent=self.master)
288+
# User hit cancel and did not select file, so leave world as-is
289+
if filename == "": return
290+
self.world.reload_world(filename=filename)
291+
self.karel.reset_state()
292+
self.canvas.redraw_all()
293+
# Reset speed slider
294+
self.scale.set(self.world.init_speed)
295+
self.status_label.configure(text=f"Loaded world from {os.path.basename(filename)}.", fg="black")
296+
297+
# Make sure program control button is set to 'run' mode
298+
self.program_control_button["text"] = "Run Program"
299+
self.program_control_button["command"] = self.run_program

0 commit comments

Comments
 (0)