1
2
3 """SimGUI 2.0 Provides a Tk / Tkinter - based framework for SimPy simulation
4 models.
5
6 LICENSE:
7 Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Klaus G. Muller, Tony Vignaux
8 mailto: kgmuller@xs4all.nl and Tony.Vignaux@vuw.ac.nz
9
10 This library is free software; you can redistribute it and / or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version.
14
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 - 1307 USA
23 END OF LICENSE
24
25 SimGUI uses a Tkinter - based console for conversing with the Python interpreter,
26 developed by Ka - Ping Yee, <ping@lfw.org>.
27
28
29 **Change history:**
30 October through December 2003:
31 Development of SimGUI, with outstanding support by
32 Prof. Simon Frost of University of California, San Diego
33 as co - designer / co - implementor.
34 Simon also contributed the idea of using Ka - Ping Yee's
35 Python interpreter console.
36
37 December 16, 2003: Completion of 1.4alpha version (fully compatible with
38 SimPy 1.4alpha).
39
40 February 2004: Release as part of SimPy 1.4
41
42 """
43
44 from Tkinter import *
45 from tkMessageBox import *
46 from Canvas import Line, CanvasText, Rectangle
47 import tkconsole as tkcons
48
49 __version__ = '2.0 $Revision: 163 $ $Date: 2008-12-15 12:47:44 +0100 (Mo, 15 Dez 2008) $'
50
52 - def __init__(self, win, title = 'SimGUI', doc = 'No doc string found', consoleHeight = 50):
53 self.root = win
54 self.doc = doc
55 self.title = title
56 win.title(title)
57 self.win = self.root
58 self.noRunYet = True
59 self.makeMenu()
60 self.makeConsole(consoleHeight)
61
64
74 self.file = Menu(self.top)
75 self.file.add_command(label = 'Save console content',
76 command = self.saveConsole, underline = 0)
77 self.file.add_command(label = 'Quit',
78 command = self.win.quit, underline = 0)
79 self.top.add_cascade(label = 'File', menu = self.file, underline = 0)
81 self.edit = Menu(self.top)
82 self.edit.add_command(label = 'Change parameters',
83 command = self.changeParameters, underline = 0)
84 self.edit.add_command(label = 'Clear console',
85 command = self.clearConsole, underline = 1)
86 self.top.add_cascade(label = 'Edit',
87 menu = self.edit, underline = 0)
89 self.run = Menu(self.top)
90 self.top.add_cascade(label = 'Run',
91 menu = self.run, underline = 0)
93 self.view = Menu(self.top)
94 self.view.add_command(label = 'Collected data',
95 command = self.showMonitors, underline = 0)
96 self.top.add_cascade(label = 'View',
97 menu = self.view, underline = 0)
99 self.help = Menu(self.top)
100 self.help.add_command(label = 'About SimGUI',
101 command = self._aboutSimGUI, underline = 6)
102 self.help.add_command(label = 'Model description',
103 command = self.about, underline = 6)
104 self.help.add_command(label = 'Model code',
105 command = self.showcode, underline = 6)
106 self.help.add_command(label = 'Python interpreter',
107 command = self.makeInterpreter, underline = 0)
108 self.top.add_cascade(label = 'Help', menu = self.help, underline = 0)
109
111 scrollbar = Scrollbar(self.root)
112 scrollbar.pack(side = RIGHT, fill = Y)
113 textOutput = Frame(self.root)
114
115 self.topconsole = Label(textOutput, text = '')
116 self.topconsole.pack()
117
118 self.console = Text(textOutput, height = height, wrap = WORD, yscrollcommand = scrollbar.set)
119 self.console.pack()
120 scrollbar.config(command = self.console.yview)
121 textOutput.pack()
122
124 self.console.insert(END, '%s\n'%text)
125 self.root.update()
126
128 self.topconsole.config(text = text)
129 self.root.update()
130
132 from tkFileDialog import asksaveasfilename
133
134 content = self.console.get('1.0', END + ' - 1c')
135
136 filename = asksaveasfilename()
137 if not filename[-4:] == '.txt':
138 filename += '.txt'
139 fi = open(filename, 'wb')
140 fi.write(content)
141 fi.close()
142
144 self.console.delete('1.0', END)
145
147 'Show SimPy / Python code of this program'
148 import sys
149 tl = Toplevel()
150 tl.title(self.title + ' - Code')
151 t = Text(tl, width = 80)
152 scroll = Scrollbar(tl, command = t.yview)
153 t.configure(yscrollcommand = scroll.set)
154 sourcefile = sys.argv[0]
155 source = ''
156 for i in open(sourcefile).readlines():
157 source = source + i
158 t.insert(END, source)
159 t.pack(side = LEFT)
160 scroll.pack(side = RIGHT, fill = Y)
161
163 self.showTextBox(width = 80, height = 30, text = self.doc,
164 title = self.title + ' - Model information')
165
167 t = Toplevel()
168 t.title('About SimGUI')
169 tx = Text(t, width = 60, height = 7)
170 txt = 'SimGUI version %s\n\nSimGUI is a framework for SimPy - based simulations. '%__version__+\
171 'It has been developed by Klaus Muller, Simon Frost and Tony Vignaux. \n'+\
172 '\n\nHomepage and download: simpy.sourceforge.net\n'
173 tx.insert(END, txt)
174 tx.pack()
175
177 showerror('Not implemented', 'Not yet available')
178
179 - def showTextBox(self, width = 60, height = 10, text = ' ', title = ' '):
180 tl = Toplevel()
181 tl.title(title)
182 txt = text
183 t = Text(tl, width = width, height = height, wrap = WORD)
184 t.insert(END, txt)
185 t.pack()
186
188 self._monitors = []
189 for k in self.__dict__.keys():
190 a = self.__dict__[k]
191 if isinstance(a, list) and hasattr(a, 'tseries') and hasattr(a, 'yseries'):
192 self._monitors.append(a)
193
195 if self.noRunYet:
196 showwarning('SimGUI warning', 'Run simulation first!')
197 return
198 self.findMonitors()
199 if not self._monitors:
200 showwarning('SimGUI warning', 'No Monitor instances found')
201 for m in self._monitors:
202 self.writeConsole('\nMonitor \'%s\':\n' % m.name)
203 dat = m
204 try:
205 xlab = m.tlab
206 except:
207 xlab = 'x'
208 try:
209 ylab = m.ylab
210 except:
211 ylab = 'y'
212 sep = ',\t'
213 self.writeConsole('%s%s%s' % (xlab, sep, ylab))
214 for this in dat:
215 self.writeConsole('%s%s%s' % (this[0],sep, this[1]))
216 self.writeConsole()
217
219 """Finds the instance of Parameters (there may only be one)
220 and associates it with self._parameters"""
221 self._parameters = None
222 for k in self.__dict__.keys():
223 a = self.__dict__[k]
224 if isinstance(a, Parameters):
225 self._parameters = a
226
228 """Offers entry fields for parameter change"""
229
230 self.findParameters()
231 if not self._parameters:
232 showwarning('SimGUI warning', 'No Parameters instance found.')
233 return
234 t1 = Toplevel(self.root)
235 top = Frame(t1)
236 self.lbl={}
237 self.ent={}
238 i = 1
239 for p in self._parameters.__dict__.keys():
240 self.lbl[p] = Label(top, text = p)
241 self.lbl[p].grid(row = i, column = 0)
242 self.ent[p] = Entry(top)
243 self.ent[p].grid(row = i, column = 1)
244 self.ent[p].insert(0, self._parameters.__dict__[p])
245 i += 1
246 top.pack(side = TOP, fill = BOTH, expand = YES)
247 commitBut = Button(top, text = 'Change parameters', command = self.commit)
248 commitBut.grid(row = i, column = 1)
249
251 """Commits parameter changes, i.e. updates self._parameters"""
252 for p in self._parameters.__dict__.keys():
253 this = self._parameters.__dict__
254 tipo = type(this[p])
255 if tipo == type(1):
256 try:
257 this[p] = int(self.ent[p].get())
258 except:
259 showerror(title = 'Input error',
260 message = 'Type Error; correct parameter \'%s\' to %s' % (p, tipo))
261 elif tipo == type(1.1):
262 try:
263 this[p] = float(self.ent[p].get())
264 except:
265 showerror(title = 'Input error',
266 message = 'Type Error; correct parameter \'%s\' to %s' % (p, tipo))
267 elif this == type('abc'):
268 try:
269 this[p] = self.ent[p].get()
270 except:
271 showerror(title = 'Input error',
272 message = 'Type Error; correct parameter \'%s\' to %s' % (p, tipo))
273 elif tipo == type([]):
274 try:
275 a = eval(self.ent[p].get())
276 if type(a) == type([]):
277 this[p] = a
278 except:
279 showerror(title = 'Input error',
280 message = 'Type Error; correct parameter \'%s\' to %s' % (p, tipo))
281 else:
282 showerror(title = 'Application program error',
283 message = 'Parameter %s has unsupported type'%p)
284 self.noRunYet = True
285
287 i = Toplevel(self.root)
288 interpreter = tkcons.Console(parent = i)
289 interpreter.dict['SimPy'] = self
290 interpreter.pack(fill = BOTH, expand = 1)
291
294 self.__dict__.update(kwds)
296 return str(self.__dict__)
298 return str(self.__dict__)
300 res = []
301 for i in self.__dict__.keys():
302 res.append('%s : %s\n' % (i, self.__dict__[i]))
303 return "".join(res)
304
305 if __name__ == '__main__':
306 print 'SimGUI.py %s'%__version__
307 from SimPy.Simulation import *
308 from SimPy.Monitor import *
309 from random import Random
310
312 """ Source generates customers randomly"""
316
318 rv = Random(self.SEED)
319 for i in range(number):
320 c = Customer(name = 'Customer%02d' % (i,))
321 activate(c, c.visit(timeInBank = 12.0))
322 t = rv.expovariate(1.0 / interval)
323 yield hold, self, t
324
326 """ The number of customers in the resource R
327 in waitQ and active Q"""
328 return (len(R.waitQ) + len(R.activeQ))
329
331 """ Customer arrives, is served and leaves """
335
336 - def visit(self, timeInBank = 0):
337 arrive = now()
338 Qlength = [NoInSystem(counter[i]) for i in range(Nc)]
339
340 for i in range(Nc):
341 if Qlength[i] == 0 or Qlength[i] == min(Qlength): join = i ; break
342 yield request, self, counter[join]
343 wait = now() - arrive
344 waitMonitor.observe(wait, t = now())
345
346 tib = counterRV.expovariate(1.0 / timeInBank)
347 yield hold, self, tib
348 yield release, self, counter[join]
349 serviceMonitor.observe(now() - arrive, t = now())
350 if trace:
351 gui.writeConsole('Customer leaves at %.1d'%now())
352
354 global Nc, counter, counterRV, waitMonitor, serviceMonitor, trace, lastLeave, noRunYet, initialized
355 counterRV = Random(gui.params.counterseed)
356 sourceseed = gui.params.sourceseed
357 nrRuns = gui.params.nrRuns
358 lastLeave = 0
359 gui.noRunYet = True
360 for runNr in range(nrRuns):
361 gui.noRunYet = False
362 trace = gui.params.trace
363 if trace:
364 gui.writeConsole(text = '\n ** Run %s' % (runNr + 1))
365 Nc = 2
366 counter = [Resource(name = 'Clerk0'),Resource(name = 'Clerk1')]
367 gui.waitMon = waitMonitor = Monitor(name = 'Waiting Times')
368 waitMonitor.tlab = 'Time'
369 waitMonitor.ylab = 'Customer waiting time'
370 gui.serviceMon = serviceMonitor = Monitor(name = 'Service Times')
371 serviceMonitor.xlab = 'Time'
372 serviceMonitor.ylab = 'Total service time = wait + service'
373 initialize()
374 source = Source(seed = sourceseed)
375 activate(source, source.generate(gui.params.numberCustomers, gui.params.interval),0.0)
376 result = simulate(until = gui.params.endtime)
377 lastLeave += now()
378 gui.writeConsole('%s simulation run(s) completed\n'%nrRuns)
379 gui.writeConsole('Parameters:\n%s'%gui.params.show())
380 gui.writeStatusLine('Time: %.2f '%now())
381
383 if gui.noRunYet:
384 showwarning(title = 'Model warning',
385 message = 'Run simulation first -- no data available.')
386 return
387 aver = lastLeave / gui.params.nrRuns
388 gui.writeConsole(text = 'Average time for %s customers to get through bank: %.1f\n(%s runs)\n'\
389 %(gui.params.numberCustomers, aver, gui.params.nrRuns))
390
391 __doc__ = """
392 Modified bank11.py (from Bank Tutorial) with GUI.
393
394 Model: Simulate customers arriving at random, using a Source, requesting service
395 from two counters each with their own queue with random servicetime.
396
397 Uses Monitor objects to record waiting times and total service times."""
398
400 gui.showTextBox(text = 'Tony Vignaux\nKlaus Muller', title = 'Author information')
403 SimGUI.__init__(self, win,**p)
404 self.help.add_command(label = 'Author(s)',
405 command = showAuthors, underline = 0)
406 self.view.add_command(label = 'Statistics',
407 command = statistics, underline = 0)
408 self.run.add_command(label = 'Run',
409 command = model, underline = 0)
410
411
412
413 root = Tk()
414 gui = MyGUI(root, title = 'SimPy GUI example', doc = __doc__, consoleHeight = 40)
415 gui.params = Parameters(endtime = 2000,
416 sourceseed = 1133,
417 counterseed = 3939393,
418 numberCustomers = 50,
419 interval = 10.0,
420 trace = 0,
421 nrRuns = 1)
422 gui.mainloop()
423