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