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