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

Source Code for Module SimPy.SimGUI

  1  #!/usr/bin/env python 
  2  # $Revision: 1.1.1.9 $ $Date: 2007/01/08 14:46:36 $ kgm 
  3  """SimGUI 1.8  Provides a Tk/Tkinter-based framework for SimPy simulation 
  4  models. 
  5   
  6  LICENSE: 
  7  Copyright (C) 2002,2003,2004,2005,2006,2007  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 __future__ import generators 
 45  from Tkinter import * 
 46  from tkMessageBox import * 
 47  from Canvas import Line, CanvasText, Rectangle 
 48  import tkconsole as tkcons 
 49   
 50  __version__ = '1.8 $Revision: 1.1.1.9 $ $Date: 2007/01/08 14:46:36 $' 
 51   
52 -class SimGUI(object):
53 - def __init__(self,win,title="SimGUI",doc="No doc string found",consoleHeight=50):
54 self.root=win 55 self.doc=doc 56 self.title=title 57 win.title(title) 58 self.win=self.root 59 self.noRunYet=True 60 self.makeMenu() 61 self.makeConsole(consoleHeight)
62
63 - def mainloop(self):
64 self.root.mainloop()
65
66 - def makeMenu(self):
67 self.top = Menu(self.win) #win = top-level window 68 self.win.config(menu=self.top) 69 self.makeFileMenu() 70 self.makeEditMenu() 71 self.makeRunMenu() 72 self.makeViewMenu() 73 self.makeHelpMenu()
74 - def makeFileMenu(self):
75 self.file = Menu(self.top) 76 self.file.add_command(label='Save console content', 77 command=self.saveConsole, underline=0) 78 self.file.add_command(label='Quit', 79 command=self.win.quit,underline=0) 80 self.top.add_cascade(label='File',menu=self.file,underline=0)
81 - def makeEditMenu(self):
82 self.edit = Menu(self.top) 83 self.edit.add_command(label='Change parameters', 84 command=self.changeParameters, underline=0) 85 self.edit.add_command(label='Clear console', 86 command=self.clearConsole, underline=1) 87 self.top.add_cascade(label='Edit', 88 menu=self.edit, underline=0)
89 - def makeRunMenu(self):
90 self.run = Menu(self.top) 91 self.top.add_cascade(label='Run', 92 menu=self.run, underline=0)
93 - def makeViewMenu(self):
94 self.view = Menu(self.top) 95 self.view.add_command(label="Collected data", 96 command=self.showMonitors, underline=0) 97 self.top.add_cascade(label='View', 98 menu=self.view, underline=0)
99 - def makeHelpMenu(self):
100 self.help = Menu(self.top) 101 self.help.add_command(label='About SimGUI', 102 command=self._aboutSimGUI,underline=6) 103 self.help.add_command(label='Model description', 104 command=self.about,underline=6) 105 self.help.add_command(label='Model code', 106 command=self.showcode,underline=6) 107 self.help.add_command(label='Python interpreter', 108 command=self.makeInterpreter,underline=0) 109 self.top.add_cascade(label='Help',menu=self.help,underline=0)
110
111 - def makeConsole(self,height):
112 scrollbar=Scrollbar(self.root) 113 scrollbar.pack(side=RIGHT,fill=Y) 114 textOutput=Frame(self.root) 115 # the status-line 116 self.topconsole=Label(textOutput,text="") 117 self.topconsole.pack() 118 # the console 119 self.console=Text(textOutput,height=height,wrap=WORD,yscrollcommand=scrollbar.set) 120 self.console.pack() 121 scrollbar.config(command=self.console.yview) 122 textOutput.pack()
123
124 - def writeConsole(self,text=' '):
125 self.console.insert(END,"%s\n"%text) 126 self.root.update()
127
128 - def writeStatusLine(self,text=''):
129 self.topconsole.config(text=text) 130 self.root.update()
131
132 - def saveConsole(self):
133 from tkFileDialog import asksaveasfilename 134 #get the Console content 135 content=self.console.get('1.0',END+'-1c') 136 #get a file name to save to 137 filename=asksaveasfilename() 138 if not filename[-4:] == '.txt': 139 filename+=".txt" 140 fi=open(filename,'wb') 141 fi.write(content) 142 fi.close()
143
144 - def clearConsole(self):
145 self.console.delete('1.0',END)
146
147 - def showcode(self):
148 "Show SimPy/Python code of this program" 149 import sys 150 tl=Toplevel() 151 tl.title(self.title+" - Code") 152 t=Text(tl,width=80) 153 scroll=Scrollbar(tl,command=t.yview) 154 t.configure(yscrollcommand=scroll.set) 155 sourcefile= sys.argv[0] 156 source="" 157 for i in open(sourcefile).readlines(): 158 source=source+i 159 t.insert(END,source) 160 t.pack(side=LEFT) 161 scroll.pack(side=RIGHT,fill=Y)
162
163 - def about(self):
164 self.showTextBox(width=80,height=30,text=self.doc, 165 title=self.title+" - Model information")
166
167 - def _aboutSimGUI(self):
168 t=Toplevel() 169 t.title("About SimGUI") 170 tx=Text(t,width=60,height=7) 171 txt="SimGUI version %s\n\nSimGUI is a framework for SimPy-based simulations. "%__version__+\ 172 "It has been developed by Klaus Muller, Simon Frost and Tony Vignaux. \n"+\ 173 "\n\nHomepage and download: simpy.sourceforge.net\n" 174 tx.insert(END,txt) 175 tx.pack()
176
177 - def notdone(self):
178 showerror('Not implemented','Not yet available')
179
180 - def showTextBox(self,width=60,height=10,text=" ",title=" "):
181 tl=Toplevel() 182 tl.title(title) 183 txt=text 184 t=Text(tl,width=width,height=height,wrap=WORD) 185 t.insert(END,txt) 186 t.pack()
187
188 - def findMonitors(self):
189 self._monitors=[] 190 for k in self.__dict__.keys(): 191 a =self.__dict__[k] 192 if isinstance(a,list) and hasattr(a,'tseries') and hasattr(a,'yseries'): 193 self._monitors.append(a)
194
195 - def showMonitors(self):
196 if self.noRunYet: 197 showwarning('SimGUI warning','Run simulation first!') 198 return 199 self.findMonitors() 200 if not self._monitors: 201 showwarning("SimGUI warning","No Monitor instances found") 202 for m in self._monitors: 203 self.writeConsole("\nMonitor '%s':\n"%m.name) 204 dat=m 205 try: 206 xlab=m.tlab 207 except: 208 xlab='x' 209 try: 210 ylab=m.ylab 211 except: 212 ylab='y' 213 sep=",\t" 214 self.writeConsole("%s%s%s"%(xlab,sep,ylab)) 215 for this in dat: 216 self.writeConsole("%s%s%s"%(this[0],sep,this[1])) 217 self.writeConsole()
218
219 - def findParameters(self):
220 """Finds the instance of Parameters (there may only be one) 221 and associates it with self._parameters""" 222 self._parameters=None 223 for k in self.__dict__.keys(): 224 a=self.__dict__[k] 225 if isinstance(a,Parameters): 226 self._parameters=a
227
228 - def changeParameters(self):
229 """Offers entry fields for parameter change""" 230 231 self.findParameters() 232 if not self._parameters: 233 showwarning("SimGUI warning","No Parameters instance found.") 234 return 235 t1=Toplevel(self.root) 236 top=Frame(t1) 237 self.lbl={} 238 self.ent={} 239 i=1 240 for p in self._parameters.__dict__.keys(): 241 self.lbl[p]=Label(top,text=p) 242 self.lbl[p].grid(row=i,column=0) 243 self.ent[p]=Entry(top) 244 self.ent[p].grid(row=i,column=1) 245 self.ent[p].insert(0,self._parameters.__dict__[p]) 246 i+=1 247 top.pack(side=TOP,fill=BOTH,expand=YES) 248 commitBut=Button(top,text='Change parameters',command=self.commit) 249 commitBut.grid(row=i,column=1)
250
251 - def commit(self):
252 """Commits parameter changes, i.e. updates self._parameters""" 253 for p in self._parameters.__dict__.keys(): 254 this=self._parameters.__dict__ 255 tipo=type(this[p]) 256 if tipo==type(1): 257 try: 258 this[p]=int(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(1.1): 263 try: 264 this[p]=float(self.ent[p].get()) 265 except: 266 showerror(title="Input error", 267 message="Type Error; correct parameter '%s' to %s"%(p,tipo)) 268 elif this==type("abc"): 269 try: 270 this[p]=self.ent[p].get() 271 except: 272 showerror(title="Input error", 273 message="Type Error; correct parameter '%s' to %s"%(p,tipo)) 274 elif tipo==type([]): 275 try: 276 a=eval(self.ent[p].get()) 277 if type(a)==type([]): 278 this[p]=a 279 except: 280 showerror(title="Input error", 281 message="Type Error; correct parameter '%s' to %s"%(p,tipo)) 282 else: 283 showerror(title="Application program error", 284 message="Parameter %s has unsupported type"%p) 285 self.noRunYet=True
286
287 - def makeInterpreter(self):
288 i=Toplevel(self.root) 289 interpreter=tkcons.Console(parent=i) 290 interpreter.dict['SimPy']=self 291 interpreter.pack(fill=BOTH,expand=1)
292
293 -class Parameters:
294 - def __init__(self,**kwds):
295 self.__dict__.update(kwds)
296 - def __repr__(self):
297 return str(self.__dict__)
298 - def __str__(self):
299 return str(self.__dict__)
300 - def show(self):
301 res=[] 302 for i in self.__dict__.keys(): 303 res.append("%s : %s\n"%(i,self.__dict__[i])) 304 return "".join(res)
305 306 if __name__ == '__main__': 307 print "SimGUI.py %s"%__version__ 308 from SimPy.Simulation import * 309 from SimPy.Monitor import * 310 from random import Random 311
312 - class Source(Process):
313 """ Source generates customers randomly"""
314 - def __init__(self,seed=333):
315 Process.__init__(self) 316 self.SEED = seed
317
318 - def generate(self,number,interval):
319 rv = Random(self.SEED) 320 for i in range(number): 321 c = Customer(name = "Customer%02d"%(i,)) 322 activate(c,c.visit(timeInBank=12.0)) 323 t = rv.expovariate(1.0/interval) 324 yield hold,self,t
325
326 - def NoInSystem(R):
327 """ The number of customers in the resource R 328 in waitQ and active Q""" 329 return (len(R.waitQ)+len(R.activeQ))
330
331 - class Customer(Process):
332 """ Customer arrives, is served and leaves """
333 - def __init__(self,name):
334 Process.__init__(self) 335 self.name = name
336
337 - def visit(self,timeInBank=0):
338 arrive=now() 339 Qlength = [NoInSystem(counter[i]) for i in range(Nc)] 340 ##print "%7.4f %s: Here I am. %s "%(now(),self.name,Qlength) 341 for i in range(Nc): 342 if Qlength[i] ==0 or Qlength[i]==min(Qlength): join =i ; break 343 yield request,self,counter[join] 344 wait=now()-arrive 345 waitMonitor.observe(wait,t=now()) 346 ##print "%7.4f %s: Waited %6.3f"%(now(),self.name,wait) 347 tib = counterRV.expovariate(1.0/timeInBank) 348 yield hold,self,tib 349 yield release,self,counter[join] 350 serviceMonitor.observe(now()-arrive,t=now()) 351 if trace: 352 gui.writeConsole("Customer leaves at %.1d"%now())
353
354 - def model():
355 global Nc,counter,counterRV,waitMonitor,serviceMonitor,trace,lastLeave,noRunYet,initialized 356 counterRV = Random(gui.params.counterseed) 357 sourceseed = gui.params.sourceseed 358 nrRuns=gui.params.nrRuns 359 lastLeave=0 360 gui.noRunYet=True 361 for runNr in range(nrRuns): 362 gui.noRunYet=False 363 trace=gui.params.trace 364 if trace: 365 gui.writeConsole(text='\n** Run %s'%(runNr+1)) 366 Nc = 2 367 counter = [Resource(name="Clerk0"),Resource(name="Clerk1")] 368 gui.waitMon=waitMonitor = Monitor(name='Waiting Times') 369 waitMonitor.tlab='Time' 370 waitMonitor.ylab='Customer waiting time' 371 gui.serviceMon=serviceMonitor = Monitor(name='Service Times') 372 serviceMonitor.xlab='Time' 373 serviceMonitor.ylab='Total service time = wait+service' 374 initialize() 375 source = Source(seed = sourceseed) 376 activate(source,source.generate(gui.params.numberCustomers,gui.params.interval),0.0) 377 result=simulate(until=gui.params.endtime) 378 lastLeave+=now() 379 gui.writeConsole("%s simulation run(s) completed\n"%nrRuns) 380 gui.writeConsole("Parameters:\n%s"%gui.params.show()) 381 gui.writeStatusLine("Time: %.2f "%now())
382
383 - def statistics():
384 if gui.noRunYet: 385 showwarning(title='Model warning', 386 message="Run simulation first -- no data available.") 387 return 388 aver=lastLeave/gui.params.nrRuns 389 gui.writeConsole(text="Average time for %s customers to get through bank: %.1f\n(%s runs)\n"\ 390 %(gui.params.numberCustomers,aver,gui.params.nrRuns))
391 392 __doc__=""" 393 Modified bank11.py (from Bank Tutorial) with GUI. 394 395 Model: Simulate customers arriving at random, using a Source, requesting service 396 from two counters each with their own queue with random servicetime. 397 398 Uses Monitor objects to record waiting times and total service times.""" 399
400 - def showAuthors():
401 gui.showTextBox(text="Tony Vignaux\nKlaus Muller",title="Author information")
402 - class MyGUI(SimGUI):
403 - def __init__(self,win,**p):
404 SimGUI.__init__(self,win,**p) 405 self.help.add_command(label="Author(s)", 406 command=showAuthors,underline=0) 407 self.view.add_command(label="Statistics", 408 command=statistics,underline=0) 409 self.run.add_command(label="Run", 410 command=model,underline=0)
411 412 413 414 root=Tk() 415 gui=MyGUI(root,title="SimPy GUI example",doc=__doc__,consoleHeight=40) 416 gui.params=Parameters(endtime=2000, 417 sourceseed=1133, 418 counterseed=3939393, 419 numberCustomers=50, 420 interval=10.0, 421 trace=0, 422 nrRuns=1) 423 gui.mainloop() 424