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