Package SimPy :: Module SimGUI
[hide private]
[frames] | no frames]

Source Code for Module SimPy.SimGUI

  1  #!/usr / bin / env python 
  2  # $Revision: 163 $ $Date: 2008-12-15 12:47:44 +0100 (Mo, 15 Dez 2008) $ kgm 
  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   
51 -class SimGUI(object):
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
62 - def mainloop(self):
63 self.root.mainloop()
64
65 - def makeMenu(self):
66 self.top = Menu(self.win) #win = top - level window 67 self.win.config(menu = self.top) 68 self.makeFileMenu() 69 self.makeEditMenu() 70 self.makeRunMenu() 71 self.makeViewMenu() 72 self.makeHelpMenu()
73 - def makeFileMenu(self):
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)
80 - def makeEditMenu(self):
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)
88 - def makeRunMenu(self):
89 self.run = Menu(self.top) 90 self.top.add_cascade(label = 'Run', 91 menu = self.run, underline = 0)
92 - def makeViewMenu(self):
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)
98 - def makeHelpMenu(self):
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
110 - def makeConsole(self, height):
111 scrollbar = Scrollbar(self.root) 112 scrollbar.pack(side = RIGHT, fill = Y) 113 textOutput = Frame(self.root) 114 # the status - line 115 self.topconsole = Label(textOutput, text = '') 116 self.topconsole.pack() 117 # the console 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
123 - def writeConsole(self, text = ' '):
124 self.console.insert(END, '%s\n'%text) 125 self.root.update()
126
127 - def writeStatusLine(self, text = ''):
128 self.topconsole.config(text = text) 129 self.root.update()
130
131 - def saveConsole(self):
132 from tkFileDialog import asksaveasfilename 133 #get the Console content 134 content = self.console.get('1.0', END + ' - 1c') 135 #get a file name to save to 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
143 - def clearConsole(self):
144 self.console.delete('1.0', END)
145
146 - def showcode(self):
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
162 - def about(self):
163 self.showTextBox(width = 80, height = 30, text = self.doc, 164 title = self.title + ' - Model information')
165
166 - def _aboutSimGUI(self):
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
176 - def notdone(self):
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
187 - def findMonitors(self):
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
194 - def showMonitors(self):
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
218 - def findParameters(self):
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
227 - def changeParameters(self):
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
250 - def commit(self):
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
286 - def makeInterpreter(self):
287 i = Toplevel(self.root) 288 interpreter = tkcons.Console(parent = i) 289 interpreter.dict['SimPy'] = self 290 interpreter.pack(fill = BOTH, expand = 1)
291
292 -class Parameters:
293 - def __init__(self,**kwds):
294 self.__dict__.update(kwds)
295 - def __repr__(self):
296 return str(self.__dict__)
297 - def __str__(self):
298 return str(self.__dict__)
299 - def show(self):
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
311 - class Source(Process):
312 """ Source generates customers randomly"""
313 - def __init__(self, seed = 333):
314 Process.__init__(self) 315 self.SEED = seed
316
317 - def generate(self, number, interval):
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
325 - def NoInSystem(R):
326 """ The number of customers in the resource R 327 in waitQ and active Q""" 328 return (len(R.waitQ) + len(R.activeQ))
329
330 - class Customer(Process):
331 """ Customer arrives, is served and leaves """
332 - def __init__(self, name):
333 Process.__init__(self) 334 self.name = name
335
336 - def visit(self, timeInBank = 0):
337 arrive = now() 338 Qlength = [NoInSystem(counter[i]) for i in range(Nc)] 339 ##print '%7.4f %s: Here I am. %s '%(now(),self.name, Qlength) 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 ##print '%7.4f %s: Waited %6.3f' % (now(),self.name, wait) 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
353 - def model():
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
382 - def statistics():
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
399 - def showAuthors():
400 gui.showTextBox(text = 'Tony Vignaux\nKlaus Muller', title = 'Author information')
401 - class MyGUI(SimGUI):
402 - def __init__(self, win,**p):
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