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

Source Code for Module SimPy.SimGUI

  1  #!/usr / bin / env python 
  2  # coding=utf-8 
  3  # $Revision: 504 $ $Date: 2010-05-11 08:29:10 +0200 (Tue, 11 May 2010) $ kgm 
  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   
40 -class SimGUI(object):
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
51 - def mainloop(self):
52 self.root.mainloop()
53
54 - def makeMenu(self):
55 self.top = Menu(self.win) #win = top - level window 56 self.win.config(menu = self.top) 57 self.makeFileMenu() 58 self.makeEditMenu() 59 self.makeRunMenu() 60 self.makeViewMenu() 61 self.makeHelpMenu()
62 - def makeFileMenu(self):
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)
69 - def makeEditMenu(self):
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)
77 - def makeRunMenu(self):
78 self.run = Menu(self.top) 79 self.top.add_cascade(label = 'Run', 80 menu = self.run, underline = 0)
81 - def makeViewMenu(self):
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)
87 - def makeHelpMenu(self):
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
99 - def makeConsole(self, height):
100 scrollbar = Scrollbar(self.root) 101 scrollbar.pack(side = RIGHT, fill = Y) 102 textOutput = Frame(self.root) 103 # the status - line 104 self.topconsole = Label(textOutput, text = '') 105 self.topconsole.pack() 106 # the console 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
112 - def writeConsole(self, text = ' '):
113 self.console.insert(END, '%s\n'%text) 114 self.root.update()
115
116 - def writeStatusLine(self, text = ''):
117 self.topconsole.config(text = text) 118 self.root.update()
119
120 - def saveConsole(self):
121 from tkFileDialog import asksaveasfilename 122 #get the Console content 123 content = self.console.get('1.0', END + ' - 1c') 124 #get a file name to save to 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
132 - def clearConsole(self):
133 self.console.delete('1.0', END)
134
135 - def showcode(self):
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
151 - def about(self):
152 self.showTextBox(width = 80, height = 30, text = self.doc, 153 title = self.title + ' - Model information')
154
155 - def _aboutSimGUI(self):
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
165 - def notdone(self):
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
176 - def findMonitors(self):
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
183 - def showMonitors(self):
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
207 - def findParameters(self):
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
216 - def changeParameters(self):
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
239 - def commit(self):
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
275 - def makeInterpreter(self):
276 i = Toplevel(self.root) 277 interpreter = tkcons.Console(parent = i) 278 interpreter.dict['SimPy'] = self 279 interpreter.pack(fill = BOTH, expand = 1)
280
281 -class Parameters:
282 - def __init__(self,**kwds):
283 self.__dict__.update(kwds)
284 - def __repr__(self):
285 return str(self.__dict__)
286 - def __str__(self):
287 return str(self.__dict__)
288 - def show(self):
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
300 - class Source(Process):
301 """ Source generates customers randomly"""
302 - def __init__(self, seed = 333):
303 Process.__init__(self) 304 self.SEED = seed
305
306 - def generate(self, number, interval):
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
314 - def NoInSystem(R):
315 """ The number of customers in the resource R 316 in waitQ and active Q""" 317 return (len(R.waitQ) + len(R.activeQ))
318
319 - class Customer(Process):
320 """ Customer arrives, is served and leaves """
321 - def __init__(self, name):
322 Process.__init__(self) 323 self.name = name
324
325 - def visit(self, timeInBank = 0):
326 arrive = now() 327 Qlength = [NoInSystem(counter[i]) for i in range(Nc)] 328 ##print '%7.4f %s: Here I am. %s '%(now(),self.name, Qlength) 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 ##print '%7.4f %s: Waited %6.3f' % (now(),self.name, wait) 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
342 - def model():
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
371 - def statistics():
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
388 - def showAuthors():
389 gui.showTextBox(text = 'Tony Vignaux\nKlaus Muller', title = 'Author information')
390 - class MyGUI(SimGUI):
391 - def __init__(self, win,**p):
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