forked from WmHHooper/aima-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathenvgui.py
More file actions
executable file
·349 lines (295 loc) · 11.4 KB
/
envgui.py
File metadata and controls
executable file
·349 lines (295 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# ______________________________________________________________________________
# GUI - Graphical User Interface for Environments
# If you do not have Tkinter installed, either get a new installation of Python
# (Tkinter is standard in all new releases), or delete the rest of this file
# and muddle through without a GUI.
#
# Excerpted from:
# http://web.media.mit.edu/~havasi/MAS.S60/code/lemmatizer_learning/aima/agents.py
# Catherine Havasi
# 2012-04-10
#
# Revised:
# William H. Hooper
# 2016-07-13
# Python 2 -> 3
import tkinter as tk # pip install tkinter
from tkinter import ttk
from PIL import ImageTk, Image # pip install pillow
import os
class EnvGUI(tk.Tk, object):
def __init__(self, env, title='AIMA GUI', cellsize=200, n=10):
# Initialize window
super(EnvGUI, self).__init__()
self.title(title)
# Create components
w = env.width
h = env.height
self.canvas = EnvCanvas(self, env, cellsize, w, h)
toolbar = EnvToolbar(self, env, self.canvas)
for w in [self.canvas, toolbar]:
w.pack(side="bottom", fill="x", padx="3", pady="3")
def getCanvas(self):
return self.canvas
class EnvToolbar(tk.Frame, object):
def __init__(self, parent, env, canvas):
super(EnvToolbar, self).__init__(parent, relief='raised', bd=2)
# Initialize instance variables
self.env = env
self.canvas = canvas
self.running = False
self.speed = 1.0
# Create buttons and other controls
for txt, cmd in [#('Step >', self.env.step),
('Step >', self.step),
('Run >>', self.run),
('Stop [ ]', self.stop),
('List things', self.list_things),
('List agents', self.list_agents)]:
ttk.Button(self, text=txt, command=cmd).pack(side='left')
tk.Label(self, text='Speed').pack(side='left')
scale = tk.Scale(self, orient='h',
from_=(1.0), to=100.0, resolution=1.0,
command=self.set_speed)
scale.set(self.speed)
scale.pack(side='left')
def step(self):
self.env.step()
self.canvas.update()
def run(self):
print('run')
self.running = True
self.background_run()
def stop(self):
print('stop')
self.running = False
def background_run(self):
if self.running:
self.step()
# ms = int(1000 * max(float(self.speed), 0.5))
# ms = max(int(1000 * float(self.delay)), 1)
delay_sec = 10.0 / max(self.speed, 1.0) # avoid division by zero
ms = int(1000.0 * delay_sec) # seconds to milliseconds
self.after(ms, self.background_run)
def list_things(self):
print("Things in the environment:")
for obj in self.env.things:
print("%s at %s" % (obj, obj.location))
def list_agents(self):
print("Agents in the environment:")
for agt in self.env.agents:
print("%s at %s" % (agt, agt.location))
def set_speed(self, speed):
self.speed = float(speed)
class Empty:
pass
class EnvCanvas(tk.Canvas, object):
def __init__(self, parent, env, cellwidth, w, h):
self.env = env
cellheight = cellwidth
canvwidth = cellwidth * w # (cellwidth + 1 ) * n
canvheight = cellheight * h # (cellwidth + 1) * n
super(EnvCanvas, self).__init__(parent, background="white",)
# Initialize instance variables
self.env = env
self.cellwidth = cellwidth
self.cellheight = cellheight
self.w = w
self.h = h
# print(
# "cellwidth, canvwidth, camvheight = %d, %d, %d" % \
# (self.cellwidth, canvwidth, canvheight))
# Set up image dictionary.
# Ugly hack: we need to keep a reference to each ImageTk.PhotoImage,
# or it will be garbage collected. This dictionary maps image files
# that have been opened to their PhotoImage objects
self.fnMap = { Empty: 'images/default.png'}
self.images = {}
cwd = os.getcwd()
default = self.get_image(self.fnMap[Empty])
self.cells = [[0 for x in range(w)] for y in range(h)]
for x in range(w):
for y in range(h):
cell = ttk.Frame(self)
contents = ttk.Label(cell, image=default)
contents.pack(side="bottom", fill="both", expand="yes")
cell.grid(row=y, column=x)
self.cells[y][x] = cell
# Bind canvas events.
# self.bind('<Button-1>', self.user_left) ## What should this do?
# self.bind('<Button-2>', self.user_edit_objects)
# self.bind('<Button-3>', self.user_add_object)
self.pack()
def mapImageNames(self, fnMap):
self.fnMap.update(fnMap)
def get_image(self, concat):
"""concat = 'filename1[+filename2]'
Try to find the image in the images dictionary.
If it's not there: open each file, create it,
and paste it over the composite.
When all names are processed, stick the composite
image in the dictionary, and return the image in
a form usable by the canvas."""
if concat in self.images:
tk_image = self.images[concat]
else:
filenames = concat.split('+')
fn0 = filenames[0]
pil_image = Image.open(fn0)
for fni in filenames[1:]:
pi = Image.open(fni)
#tki = ImageTk.PhotoImage(pi)
pil_image.paste(pi, mask=pi)
pil_image = pil_image.resize((self.cellwidth, self.cellheight),
Image.ANTIALIAS)
tk_image = ImageTk.PhotoImage(pil_image)
self.images[concat] = tk_image
return tk_image
def update(self):
'''Create a tiled image of the XY Environment,
based on the things in each cell.'''
env = self.env
for x in range(self.w):
for y in range(self.h):
cell = self.cells[y][x]
filenames = ''
tList = env.list_things_at((x, y))
for thing in tList:
tclass = thing.__class__
tname = self.fnMap[tclass]
if filenames == '':
filenames = tname
elif not tname in filenames:
filenames += '+' + tname
if filenames == '':
filenames = self.fnMap[Empty]
bg = self.get_image(filenames)
# contents = ttk.Label(cell, image=bg)
contents = cell.winfo_children()[0]
contents.config(image=bg)
contents.pack(side="bottom", fill="both", expand="yes")
def user_left(self, event):
print('left at %d, %d' % self.event_cell(event))
def user_edit_objects(self, event):
"""Choose an object within radius and edit its fields."""
pass
def user_add_object(self, event):
"""Pops up a menu of Object classes; you choose the
one you want to put in this square."""
cell = self.event_cell(event)
xy = self.cell_topleft_xy(cell)
menu = tk.Menu(self, title='Edit (%d, %d)' % cell)
# Generalize object classes available,
# and why is self.run the command?
# for (txt, cmd) in [('Wumpus', self.run), ('Pit', self.run)]:
# menu.add_command(label=txt, command=cmd)
obj_classes = self.env.object_classes()
def class_cmd(oclass):
def cmd():
obj = oclass()
self.env.add_object(obj, cell)
# what about drawing it on the canvas?
print(
"Drawing object %s at %s %s" % (obj, cell, xy))
tk_image = self.get_image(oclass.image_file)
self.canvas.create_image(xy, anchor="nw", image=tk_image)
return cmd
for oclass in obj_classes:
menu.add_command(label=oclass.__name__, command=class_cmd(oclass))
menu.tk_popup(event.x + self.winfo_rootx(),
event.y + self.winfo_rooty())
def event_cell(self, event):
return self.xy_cell(event.x, event.y)
def xy_cell(self, x, y):
"""Given an (x, y) on the canvas, return the row and column
of the cell containing it."""
w = self.cellwidth
return x / w, y / w
def cell_topleft_xy(self, row, column):
"""Given a (row, column) tuple, return the (x, y) coordinates
of the cell(row, column)'s top left corner."""
w = self.cellwidth
return w * row, w * column
# a Text User Interface for the Agent program
class EnvTUI(object):
def __init__(self, env, title='AIMA GUI', cellsize=200, n=10):
# Initialize window
# Create components
w = env.width
h = env.height
self.env = env
self.grid = [['.' for x in range(w)] for y in range(h)]
self.fnMap = { Empty: '.'}
def mapImageNames(self, fnMap):
self.fnMap.update(fnMap)
def displayString(self):
display = ''
first = True
for y in range(len(self.grid)):
if not first:
display += '\n'
first = False
for x in range(len(self.grid[y])):
tList = self.env.list_things_at((x, y))
tname = self.fnMap[Empty]
for thing in tList:
tclass = thing.__class__
tname = self.fnMap[tclass]
display += tname
return display
def help(self):
for line in [
'Commands are:',
' h: print this help message',
' s n: advance n steps',
' t: list things',
' a: list agents',
' an empty string advances n steps',
]:
print(line)
def list_things(self, MyClass=object):
print(MyClass.__name__ + 's in the environment:')
for obj in self.env.things:
if isinstance(obj, MyClass):
print("%s at %s" % (obj, obj.location))
def list_agents(self):
print("Agents in the environment:")
for agt in self.env.agents:
print("%s at %s" % (agt, agt.location))
def step(self, n=1):
for s in range(n):
self.env.step()
print(str(n) + ' step(s) later:')
print(self.displayString())
def mainloop(self):
print(self.displayString())
reply = ''
n = 1
while reply != 'q':
reply = input('Command (h for help): ').strip()
if reply == '':
self.step(n)
continue
if reply == 'h':
self.help()
continue
if reply == 'q':
continue
if reply == 'a':
self.list_agents()
continue
if reply == 't':
self.list_things()
continue
if reply[0] == 's':
command = reply.split()
try:
arg = command[1]
except:
arg = str(n)
try:
n = int(arg)
self.step(n)
except:
print('"' + arg + '" is not an integer')
continue