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