#!/usr/bin/env python2.3
################################################################################
#
# This file is part of Gato (Graph Animation Toolbox)
#
# file: Gato.py
# author: Alexander Schliep (schliep@molgen.mpg.de)
#
# Copyright (C) 1998-2005, Alexander Schliep, Winfried Hochstaettler and
# Copyright 1998-2001 ZAIK/ZPR, Universitaet zu Koeln
#
# Contact: schliep@molgen.mpg.de, wh@zpr.uni-koeln.de
#
# Information: http://gato.sf.net
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the Free
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#
#
#
# This file is version $Revision: 1.1 $
# from $Date: 2007/10/04 14:36:39 $
# last change by $Author: edream $.
#
################################################################################
import sys
import tempfile
import traceback
import os
import bdb
import random
import re
import string
import StringIO
import tokenize
import tkFont
import copy
import webbrowser
import Gred
from Tkinter import *
from tkFileDialog import askopenfilename,asksaveasfilename
from tkMessageBox import askokcancel,showerror,askyesno
from ScrolledText import ScrolledText
from GatoConfiguration import GatoConfiguration
from Graph import Graph
from GraphUtil import *
from GraphDisplay import GraphDisplayToplevel
from GatoUtil import *
from GatoGlobals import *
from GatoDialogs import AboutBox,SplashScreen,HTMLViewer
import GatoIcons
import GatoSystemConfiguration
from AnimationHistory import AnimationHistory
# put someplace else
def WMExtrasGeometry(window):
""" Returns (top,else) where
- top is the amount of extra pixels the WM puts on top
of the window
- else is the amount of extra pixels the WM puts everywhere
else around the window
NOTE: Does not work with tk8.0 style menus, since those are
handled by WM (according to Tk8.1 docs)
NOTE: Some window managers return bad geometry definition
Handle in caller
"""
try:
window.geometry() # XXX Sometimes first produced wrong results ...
g = string.split(window.geometry(),"+")
except TclError:
# bad geometry specifier: e.g. ... "-1949x260+1871+1"
return (32,32)
trueRootx = string.atoi(g[1])
trueRooty = string.atoi(g[2])
rootx = window.winfo_rootx() # top left of our window
rooty = window.winfo_rooty() # *WITHOUT* WM extras
topWMExtra = abs(rooty - trueRooty) # WM adds that on top
WMExtra = abs(rootx - trueRootx) # and that on all other sides
# XXX KLUDGE topWMExtra,WMExtra should always be in 0...32 pixels, or?
topWMExtra = min(32,topWMExtra)
WMExtra = min(32, WMExtra)
return (topWMExtra,WMExtra)
################################################################################
#
#
# Public Methods of class AlgoWin
#
# ShowActive(lineNo) Display line lineNo as activated
#
# ShowBreakpoint(lineNo) Show breakpoint at line lineNo
#
# HideBreakpoint(lineNo) Hide breakpoint at line lineNo
#
# WaitNextEvent() Wait for some GUI event
#
# WaitTime(delay) Wait for delay (in ms)
#
class AlgoWin(Frame):
""" Provide GUI with main menubar for displaying and controlling
algorithms and the algorithm text widget """
def __init__(self, parent=None):
Frame.__init__(self,parent)
#XXX import tkoptions
#tkoptions.tkoptions(self)
Splash = SplashScreen(self.master)
# Need to change things a bit for Tk running on MacOS X
# using the native drawing environment (TkAqua)
self.windowingsystem = self.tk.call("tk", "windowingsystem")
self.algoFont = "Courier"
self.algoFontSize = 10
self.keywordsList = [
"del", "from", "lambda", "return",
"and", "elif", "global", "not", "try",
"break", "else", "if", "or", "while",
"class", "except", "import", "pass",
"continue", "finally", "in", "print",
"def", "for", "is", "raise"]
GatoIcons.Init()
self.config = GatoConfiguration(self)
self.gatoInstaller=GatoSystemConfiguration.GatoInstaller()
# Create widgets
self.pack()
self.pack(expand=1,fill=BOTH) # Makes menuBar and toolBar sizeable
self.makeMenuBar()
self.makeAlgoTextWidget()
self.makeToolBar()
self.master.title("Gato 0.99 - Algorithm")
self.master.iconname("Gato 0.99")
self.algorithm = Algorithm()
self.algorithm.SetGUI(self) # So that algorithm can call us
self.graphDisplay = GraphDisplayToplevel()
self.secondaryGraphDisplay = None
self.AboutAlgorithmDialog = None
self.AboutGraphDialog = None
self.lastActiveLine = 0
self.algorithmIsRunning = 0 # state
self.commandAfterStop = None # command to call after forced Stop
self.goOn = IntVar() # lock variable to avoid busy idling
self.master.protocol('WM_DELETE_WINDOW',self.Quit) # Handle WM Kills
Splash.Destroy()
# Fix focus and stacking
if os.name == 'nt' or os.name == 'dos':
self.graphDisplay.tkraise()
self.master.tkraise()
self.master.focus_force()
else:
self.tkraise()
# Make AlgoWins requested size its minimal size to keep
# toolbar from vanishing when changing window size
# Packer has been running due to splash screen
wmExtras = WMExtrasGeometry(self.graphDisplay)
width = self.master.winfo_reqwidth()
height = self.master.winfo_reqheight()
# XXX Some WM + packer combinatios ocassionally produce absurd requested sizes
log.debug(os.name + str(wmExtras) + " width = %f height = %f " % (width, height))
width = min(600, self.master.winfo_reqwidth())
height = min(750, self.master.winfo_reqheight())
if os.name == 'nt' or os.name == 'dos':
self.master.minsize(width, height + wmExtras[1])
else: # Unix & Mac
self.master.minsize(width, height + wmExtras[0] + wmExtras[1])
self.BindKeys(self.master)
self.BindKeys(self.graphDisplay)
self.SetFromConfig() # Set values read in config
############################################################
#
# Create GUI
#
def makeMenuBar(self):
""" *Internal* """
self.menubar = Menu(self, tearoff=0)
# Cross-plattform accelerators
if self.windowingsystem == "aqua":
accMod = "command"
else:
accMod = "Ctrl"
# --- FILE menu ----------------------------------------
self.fileMenu = Menu(self.menubar, tearoff=0)
self.fileMenu.add_command(label='Open Algorithm...',
command=self.OpenAlgorithm)
self.fileMenu.add_command(label='Open Graph...',
command=self.OpenGraph)
if self.windowingsystem != 'aqua':
self.fileMenu.add_command(label='New Graph...',
command=self.NewGraph)
# Obsolete. Only used for TRIAL-SOLUTION Gato version
#self.fileMenu.add_command(label='Open GatoFile...',
# command=self.OpenGatoFile)
#self.fileMenu.add_command(label='Save GatoFile...',
# command=self.SaveGatoFile)
self.fileMenu.add_command(label='Reload Algorithm & Graph',
command=self.ReloadAlgorithmGraph)
self.fileMenu.add_command(label='Export Graph as EPS...',
command=self.ExportEPSF)
if self.windowingsystem != 'aqua':
self.fileMenu.add_separator()
self.fileMenu.add_command(label='Preferences...',
command=self.Preferences,
accelerator='%s-,' % accMod)
#self.gatoInstaller.addMenuEntry(self.fileMenu)
self.fileMenu.add_separator()
self.fileMenu.add_command(label='Quit',
command=self.Quit,
accelerator='%s-Q' % accMod)
self.menubar.add_cascade(label="File", menu=self.fileMenu,
underline=0)
# --- WINDOW menu ----------------------------------------
self.windowMenu=Menu(self.menubar, tearoff=0)
self.windowMenu.add_command(label='One graph window',
accelerator='%s-1' % accMod,
command=self.OneGraphWindow)
self.windowMenu.add_command(label='Two graph windows',
accelerator='%s-2' % accMod,
command=self.TwoGraphWindow)
self.menubar.add_cascade(label="Window Layout", menu=self.windowMenu,
underline=0)
# --- HELP menu ----------------------------------------
self.helpMenu=Menu(self.menubar, tearoff=0, name='help')
if self.windowingsystem != 'aqua':
self.helpMenu.add_command(label='About Gato',
command=self.AboutBox)
self.helpMenu.add_command(label='Help',
accelerator='%s-?' % accMod,
command=self.HelpBox)
self.helpMenu.add_separator()
self.helpMenu.add_command(label='Go to Gato website',
command=self.GoToGatoWebsite)
self.helpMenu.add_command(label='Go to CATBox website',
command=self.GoToCATBoxWebsite)
self.helpMenu.add_separator()
self.helpMenu.add_command(label='About Algorithm',
command=self.AboutAlgorithm)
self.helpMenu.add_command(label='About Graph',
command=self.AboutGraph)
self.menubar.add_cascade(label="Help", menu=self.helpMenu,
underline=0)
# --- MacOS X application menu --------------------------
# On a Mac we put our about box under the Apple menu ...
if self.windowingsystem == 'aqua':
self.apple=Menu(self.menubar, tearoff=0, name='apple')
self.apple.add_command(label='About Gato',
command=self.AboutBox)
self.apple.add_separator()
self.apple.add_command(label='Preferences...',
accelerator='command-,',
command=self.Preferences)
self.menubar.add_cascade(menu=self.apple)
self.master.configure(menu=self.menubar)
def makeToolBar(self):
""" *Internal* Creates Start/Stop/COntinue ... toolbar """
toolbar = Frame(self, cursor='hand2', relief=FLAT)
toolbar.pack(side=BOTTOM, fill=X) # Allows horizontal growth
toolbar.columnconfigure(5,weight=1)
if os.name == 'nt' or os.name == 'dos':
px = 0
py = 0
else: # Unix
px = 0
py = 3
if self.windowingsystem == 'aqua':
bWidth = 10
else:
bWidth = 8
self.buttonStart = Button(toolbar, width=bWidth, padx=px, pady=py,
text='Start', command=self.CmdStart,
highlightbackground='#DDDDDD')
self.buttonStep = Button(toolbar, width=bWidth, padx=px, pady=py,
text='Step', command=self.CmdStep,
highlightbackground='#DDDDDD')
self.buttonTrace = Button(toolbar, width=bWidth, padx=px, pady=py,
text='Trace', command=self.CmdTrace,
highlightbackground='#DDDDDD')
self.buttonContinue = Button(toolbar, width=bWidth, padx=px, pady=py,
text='Continue', command=self.CmdContinue,
highlightbackground='#DDDDDD')
self.buttonStop = Button(toolbar, width=bWidth, padx=px, pady=py,
text='Stop', command=self.CmdStop,
highlightbackground='#DDDDDD')
self.buttonStart.grid(row=0, column=0, padx=2, pady=2)
self.buttonStep.grid(row=0, column=1, padx=2, pady=2)
self.buttonTrace.grid(row=0, column=2, padx=2, pady=2)
self.buttonContinue.grid(row=0, column=3, padx=2, pady=2)
self.buttonStop.grid(row=0, column=4, padx=2, pady=2)
self.buttonStart['state'] = DISABLED
self.buttonStep['state'] = DISABLED
self.buttonTrace['state'] = DISABLED
self.buttonContinue['state'] = DISABLED
self.buttonStop['state'] = DISABLED
if self.windowingsystem == 'aqua':
dummy = Frame(toolbar, relief=FLAT, bd=2)
dummy.grid(row=0, column=5, padx=6, pady=2)
def makeAlgoTextWidget(self):
""" *Internal* Here we also define appearance of
- interactive lines
- breakpoints
- the active line """
if self.windowingsystem == 'aqua':
borderFrame = Frame(self, relief=FLAT, bd=1, background='#666666') # Extra Frame
else:
borderFrame = Frame(self, relief=SUNKEN, bd=2) # Extra Frame
# around widget needed for more Windows-like appearance
self.algoText = ScrolledText(borderFrame, relief=FLAT,
padx=3, pady=3,
background="white", wrap='none',
width=43, height=30,
)
self.SetAlgorithmFont(self.algoFont, self.algoFontSize)
self.algoText.pack(expand=1, fill=BOTH)
borderFrame.pack(side=TOP, expand=1, fill=BOTH)
# GUI-related tags
self.algoText.tag_config('Interactive', foreground='#009900',background="#E5E5E5")
self.algoText.tag_config('Break', foreground='#ff0000',background="#E5E5E5")
self.algoText.tag_config('Active', background='#bbbbff')
self.algoText.bind("<ButtonRelease-1>", self.handleMouse)
self.algoText['state'] = DISABLED
def SetAlgorithmFont(self, font, size):
self.algoFont = font
self.algoFontSize = size
f = tkFont.Font(self, (font, size, tkFont.NORMAL))
bf = tkFont.Font(self, (font, size, tkFont.BOLD))
itf = tkFont.Font(self, (font, size, tkFont.ITALIC))
self.algoText.config(font=f)
# syntax highlighting tags
self.algoText.tag_config('keyword', font=bf)
self.algoText.tag_config('string', font=itf)
self.algoText.tag_config('comment', font=itf)
self.algoText.tag_config('identifier', font=bf)
def SetFromConfig(self):
c = self.config.get # Shortcut to accessor
self.SetAlgorithmFont(c('algofont'), int(c('algofontsize')))
self.algoText.config(fg=c('algofg'), bg=c('algobg'))
self.algoText.tag_config('Interactive',
foreground=c('interactivefg'),
background=c('interactivebg'))
self.algoText.tag_config('Break',
foreground=c('breakpointfg'),
background=c('breakpointbg'))
self.algoText.tag_config('Active',
foreground=c('activefg'),
background=c('activebg'))
globals()['gBlinkRate'] = int(c('blinkrate'))
globals()['gBlinkRepeat'] = int(c('blinkrepeat'))
def OpenSecondaryGraphDisplay(self):
""" Pops up a second graph window """
if self.secondaryGraphDisplay == None:
self.secondaryGraphDisplay = GraphDisplayToplevel()
self.BindKeys(self.secondaryGraphDisplay)
else:
self.secondaryGraphDisplay.Show()
def WithdrawSecondaryGraphDisplay(self):
""" Hide window containing second graph """
if self.secondaryGraphDisplay != None:
self.secondaryGraphDisplay.Withdraw()
############################################################
#
# GUI Helpers
#
# Lock
def touchLock(self):
""" *Internal* The lock (self.goOn) is a variable which
is used to control the flow of the programm and to
allow GUI interactions without busy idling.
The following methods wait for the lock to be touched:
- WaitNextEvent
- WaitTime
The following methods touch it:
- CmdStop
- CmdStep
- CmdContinue """
self.goOn.set(self.goOn.get() + 1) #XXX possible overflow
def activateMenu(self):
""" Make the menu active (i.e., after stopping an algo) """
self.menubar.entryconfigure(0, state = NORMAL)
def deactivateMenu(self):
""" Make the menu inactive (i.e., before running an algo) """
self.menubar.entryconfigure(0, state = DISABLED)
def tagLine(self, lineNo, tag):
""" Add tag 'tag' to line lineNo """
self.algoText.tag_add(tag,'%d.0' % lineNo,'%d.0' % (lineNo + 1))
def unTagLine(self, lineNo, tag):
""" Remove tag 'tag' from line lineNo """
self.algoText.tag_remove(tag,'%d.0' % lineNo,'%d.0' % (lineNo + 1))
def tagLines(self, lines, tag):
""" Tag every line in list lines with specified tag """
for l in lines:
self.tagLine(l, tag)
def tokenEater(self, type, token, (srow, scol), (erow, ecol), line):
#log.debug("%d,%d-%d,%d:\t%s\t%s" % \
# (srow, scol, erow, ecol, type, repr(token)))
if type == 1: # Name
if token in self.keywordsList:
self.algoText.tag_add('keyword','%d.%d' % (srow, scol),
'%d.%d' % (erow, ecol))
elif type == 3: # String
self.algoText.tag_add('string','%d.%d' % (srow, scol),
'%d.%d' % (erow, ecol))
elif type == 39: # Comment
self.algoText.tag_add('comment','%d.%d' % (srow, scol),
'%d.%d' % (erow, ecol))
############################################################
#
# Menu Commands
#
# The menu commands are passed as call back parameters to
# the menu items.
#
def OpenAlgorithm(self,file=""):
""" GUI to allow selection of algorithm to open
file parameter for testing purposes """
if self.algorithmIsRunning:
self.CmdStop()
self.commandAfterStop = self.OpenAlgorithm
return
if file == "": # caller did not specify file
file = askopenfilename(title="Open Algorithm",
defaultextension=".py",
filetypes = [ ("Gato Algorithm", ".alg")
,("Python Code", ".py")
]
)
if file is not "" and file is not ():
try:
self.algorithm.Open(file)
except (EOFError, IOError):
self.HandleFileIOError("Algorithm",file)
return
self.algoText['state'] = NORMAL
self.algoText.delete('0.0', END)
self.algoText.insert('0.0', self.algorithm.GetSource())
self.algoText['state'] = DISABLED
self.tagLines(self.algorithm.GetInteractiveLines(), 'Interactive')
self.tagLines(self.algorithm.GetBreakpointLines(), 'Break')
# Syntax highlighting
tokenize.tokenize(StringIO.StringIO(self.algorithm.GetSource()).readline,
self.tokenEater)
if self.algorithm.ReadyToStart():
self.buttonStart['state'] = NORMAL
self.master.title("Gato 0.99 - " + stripPath(file))
if self.AboutAlgorithmDialog:
self.AboutAlgorithmDialog.Update(self.algorithm.About(),"About Algorithm")
def NewGraph(self):
Gred.Start()
def OpenGraph(self,file=""):
""" GUI to allow selection of graph to open
file parameter for testing purposes """
if self.algorithmIsRunning:
self.CmdStop()
self.commandAfterStop = self.OpenGraph
return
if file == "": # caller did not specify file
file = askopenfilename(title="Open Graph",
defaultextension=".gato",
filetypes = [ ("Gred", ".cat")
#,("Gato Plus", ".cat")
#,("LEDA", ".gph")
#,("Graphlet", ".let")
#,("Gato",".gato")
]
)
if file is not "" and file is not ():
try:
self.algorithm.OpenGraph(file)
except (EOFError, IOError):
self.HandleFileIOError("Graph",file)
return
if self.algorithm.ReadyToStart():
self.buttonStart['state'] = NORMAL
if self.AboutGraphDialog:
self.AboutGraphDialog.Update(self.graphDisplay.About(), "About Graph")
def SaveGatoFile(self,filename=""):
"""
under Construction...
"""
import GatoFile
# ToDo
if not askyesno("Ooops...",
"...this feature is under developement.\nDo you want to proceed?"):
return
if self.algorithmIsRunning:
# variable file is lost here!
self.CmdStop()
self.commandAfterStop = self.SaveGatoFile
return
if filename == "": # caller did not specify file
filename = asksaveasfilename(title="Save Graph and Algorithm",
defaultextension=".gato",
filetypes = [ ("Gato",".gato")
#,("xml",".xml")
]
)
def OpenGatoFile(self,filename=""):
"""
menu command
"""
import GatoFile
if self.algorithmIsRunning:
# variable file is lost here!
self.CmdStop()
self.commandAfterStop = self.OpenGatoFile
return
if filename == "": # caller did not specify file
filename = askopenfilename(title="Open Graph and Algorithm",
defaultextension=".gato",
filetypes = [ ("Gato",".gato")
#,("xml",".xml")
]
)
if filename is not "":
select={}
try:
# open xml file
f=GatoFile.GatoFile(filename)
select=f.getDefaultSelection()
if not select:
# select the graph
select=f.displaySelectionDialog(self)
except GatoFile.FileException, e:
self.HandleFileIOError("GatoFile: %s"%e.reason,filename)
return
# nothing selected
if select is None:
return
# a graph is selected
if select.get("graph"):
try:
# open graph
graphStream=select["graph"].getGraphAsStringIO()
self.algorithm.OpenGraph(graphStream,
fileName="%s::%s"%(filename,
select["graph"].getName()))
except (EOFError, IOError):
self.HandleFileIOError("Gato",filename)
return
if self.algorithm.ReadyToStart():
self.buttonStart['state'] = NORMAL
if self.AboutGraphDialog:
self.AboutGraphDialog.Update(self.graphDisplay.About(), "About Graph")
# great shit! create files to get old gato running
if select.get("algorithm"):
xmlAlgorithm=select.get("algorithm")
# save last algorithm tmp_name
lastAlgoFileName=None
if hasattr(self,"tmpAlgoFileName"):
lastAlgoFileName=self.tmpAlgoFileName
lastAlgoDispalyName=None
if hasattr(self,"algoDisplayFileName"):
lastAlgoDispalyName=self.algoDisplayFileName
# provide a temporary files for algortihm and prologue
tmpFileName=tempfile.mktemp()
self.tmpAlgoFileName="%s.alg"%tmpFileName
self.algoDisplayFileName="%s::%s"%(filename,xmlAlgorithm.getName())
tmp=file(self.tmpAlgoFileName,"w")
tmp.write(xmlAlgorithm.getText())
tmp.close()
proFileName="%s.pro"%tmpFileName
tmp=file(proFileName,"w")
tmp.write(xmlAlgorithm.getProlog())
tmp.close()
# open it!
# text copied from AlgoWin.OpenAlgorithm
try:
self.algorithm.Open(self.tmpAlgoFileName)
except (EOFError, IOError):
os.remove(self.tmpAlgoFileName)
os.remove(proFileName)
self.HandleFileIOError("Algorithm",self.tmpAlgoFileName)
self.algoDisplayFileName=lastAlgoDispalyName
self.tmpAlgoFileName=lastAlgoFileName
return
# handle old tempfile
if lastAlgoFileName:
os.remove(lastAlgoFileName)
os.remove(lastAlgoFileName[:-3]+'pro')
# prepare algorithm text widget
self.algoText['state'] = NORMAL
self.algoText.delete('0.0', END)
self.algoText.insert('0.0', self.algorithm.GetSource())
self.algoText['state'] = DISABLED
self.tagLines(self.algorithm.GetInteractiveLines(), 'Interactive')
self.tagLines(self.algorithm.GetBreakpointLines(), 'Break')
# Syntax highlighting
tokenize.tokenize(StringIO.StringIO(self.algorithm.GetSource()).readline,
self.tokenEater)
# set the state
if self.algorithm.ReadyToStart():
self.buttonStart['state'] = NORMAL
self.master.title("Gato 0.99 - " + stripPath(self.algoDisplayFileName))
if self.AboutAlgorithmDialog:
# to do ... alright for xml about ?!
self.AboutAlgorithmDialog.Update(self.algorithm.About(),
"About Algorithm")
def CleanUp(self):
"""
removes the temporary files...
"""
if hasattr(self,"tmpAlgoFileName") and self.tmpAlgoFileName:
os.remove(self.tmpAlgoFileName)
os.remove(self.tmpAlgoFileName[:-3]+'pro')
def ReloadAlgorithmGraph(self):
if self.algorithmIsRunning:
self.CmdStop()
self.commandAfterStop = self.ReloadAlgorithmGraph
return
if self.algorithm.algoFileName is not "":
self.OpenAlgorithm(self.algorithm.algoFileName)
if self.algorithm.graphFileName is not "":
self.OpenGraph(self.algorithm.graphFileName)
def Preferences(self,event=None):
""" Handle editing preferences """
self.config.edit()
def ExportEPSF(self):
""" GUI to control export of EPSF file """
file = asksaveasfilename(title="Export EPSF",
defaultextension=".eps",
filetypes = [ ("Encapsulated PS", ".eps")
,("Postscript", ".ps")
]
)
if file is not "":
self.graphDisplay.PrintToPSFile(file)
def Quit(self,event=None):
if self.algorithmIsRunning:
self.commandAfterStop = self.Quit
self.CmdStop()
return
if askokcancel("Quit","Do you really want to quit?"):
Frame.quit(self)
self.CleanUp()
def OneGraphWindow(self,event=None):
""" Align windows nicely for one graph window """
self.WithdrawSecondaryGraphDisplay()
self.master.update()
if self.windowingsystem == 'aqua':
screenTop = 22 # Take care of menubar
else:
screenTop = 0
# Keep the AlgoWin fixed in size but move it to 0,0
(topWMExtra,WMExtra) = WMExtrasGeometry(self.graphDisplay)
pad = 1 # Some optional extra space
trueWidth = self.master.winfo_width() + 2 * WMExtra + pad
# Move AlgoWin so that the WM extras will be at 0,0
# Silly enough one hast to specify the true coordinate at which
# the window will appear
try:
self.master.geometry("+%d+%d" % (pad, screenTop + pad))
except TclError:
log.debug("OneGraphWindow: self.master.geometry failed for +%d+%d" % (pad, screenTop + pad))
log.debug("OneGraphWindow: screen= (%d * %d), extras = (%d %d)" % (
self.master.winfo_screenwidth(),
self.master.winfo_screenheight(),
WMExtra,
topWMExtra)
)
# Move graph win to take up the rest of the screen
screenwidth = self.master.winfo_screenwidth()
screenheight = self.master.winfo_screenheight() - screenTop
self.graphDisplay.geometry("%dx%d+%d+%d" % (
screenwidth - trueWidth - 2 * WMExtra - pad - 1,# see 1 below
screenheight - WMExtra - topWMExtra - pad,
trueWidth + 1 + pad,
screenTop + pad))
self.graphDisplay.update()
self.master.update()
def TwoGraphWindow(self,event=None):
""" Align windows nicely for two graph windows """
self.OpenSecondaryGraphDisplay()
self.master.update()
if self.windowingsystem == 'aqua':
screenTop = 22 # Take care of menubar
else:
screenTop = 0
# Keep the AlgoWin fixed in size but move it to 0,0
(topWMExtra,WMExtra) = WMExtrasGeometry(self.graphDisplay)
pad = 1 # Some optional extra space
trueWidth = self.master.winfo_width() + 2 * WMExtra + pad
# Move AlgoWin so that the WM extras will be at 0,0
# Silly enough one hast to specify the true coordinate at which
# the window will appear
self.master.geometry("+%d+%d" % (pad, screenTop + pad))
# Move GraphWins so that the are stacked dividing vertical
# space evenly and taking up as much as possible horizontally
screenwidth = self.master.winfo_screenwidth()
screenheight = self.master.winfo_screenheight() - screenTop
reqGDWidth = screenwidth - trueWidth - 2 * WMExtra - pad - 1
reqGDHeight = screenheight/2 - WMExtra - topWMExtra - pad
self.graphDisplay.geometry("%dx%d+%d+%d" % (
reqGDWidth,
reqGDHeight,
trueWidth + 1 + pad,
screenTop + pad))
self.secondaryGraphDisplay.geometry("%dx%d+%d+%d" % (
reqGDWidth,
reqGDHeight,
trueWidth + 1 + pad,
screenTop + reqGDHeight + WMExtra + topWMExtra + 2 * pad))
self.master.update()
def AboutBox(self):
d = AboutBox(self.master)
def HelpBox(self,event=None):
d = HTMLViewer(gGatoHelp, "Help", self.master)
def GoToGatoWebsite(self):
webbrowser.open('http://gato.sf.net', new=1, autoraise=1)
def GoToCATBoxWebsite(self):
webbrowser.open('http://algorithmics.molgen.mpg.de/CATBox', new=1, autoraise=1)
def AboutAlgorithm(self):
d = HTMLViewer(self.algorithm.About(), "About Algorithm", self.master)
self.AboutAlgorithmDialog = d
def AboutGraph(self):
d = HTMLViewer(self.graphDisplay.About(), "About Graph", self.master)
self.AboutGraphDialog = d
############################################################
# # Tool bar Commands
#
# The tool bar commands are passed as call back parameters to
# the tool bar buttons.
#
def CmdStart(self):
""" Command linked to toolbar 'Start' """
# self.deactivateMenu()
self.buttonStart['state'] = DISABLED
self.buttonStep['state'] = NORMAL
self.buttonTrace['state'] = NORMAL
self.buttonContinue['state'] = NORMAL
self.buttonStop['state'] = NORMAL
self.algorithmIsRunning = 1
self.algorithm.Start()
def CmdStop(self):
""" Command linked to toolbar 'Stop' """
self.algorithm.Stop()
self.clickResult = ('abort',None) # for aborting interactive
# selection of vertices/edges
self.touchLock()
def CommitStop(self):
""" Commit a stop for the GUI """
self.buttonStart['state'] = NORMAL
self.buttonStep['state'] = DISABLED
self.buttonTrace['state'] = DISABLED
self.buttonContinue['state'] = DISABLED
self.buttonStop['state'] = DISABLED
# Un-activate last line
if self.lastActiveLine != 0:
self.unTagLine(self.lastActiveLine,'Active')
self.update() # Forcing redraw
self.algorithmIsRunning = 0
if self.commandAfterStop != None:
self.commandAfterStop()
self.commandAfterStop = None
# self.activateMenu()
def CmdStep(self):
""" Command linked to toolbar 'Step' """
self.algorithm.Step()
self.clickResult = ('auto',None) # for stepping over interactive
# selection of vertices/edges
self.touchLock()
def CmdContinue(self):
""" Command linked to toolbar 'Continue' """
# Should we disable continue buton here ?
self.algorithm.Continue()
self.clickResult = ('auto',None) # for stepping over interactive
# selection of vertices/edges
self.touchLock()
def CmdTrace(self):
""" Command linked to toolbar 'Trace' """
self.algorithm.Trace()
self.touchLock()
############################################################
#
# Key commands for Tool bar Commands
#
def BindKeys(self, widget):
#widget.bind('<DESTROY>',self.OnQuitMenu)
# self.master.bind_all screws up EPSF save dialog
widget.bind('s', self.KeyStart)
widget.bind('x', self.KeyStop)
widget.bind('<space>', self.KeyStep)
widget.bind('c', self.KeyContinue)
widget.bind('t', self.KeyTrace)
widget.bind('b', self.KeyBreak)
widget.bind('r', self.KeyReplay)
widget.bind('u', self.KeyUndo)
widget.bind('d', self.KeyDo)
# Cross-plattform accelerators
if self.windowingsystem == 'aqua':
accMod = "Command"
else:
accMod = "Control"
widget.bind('<%s-q>' % accMod, self.Quit)
widget.bind('<%s-comma>' % accMod, self.Preferences)
widget.bind('<%s-KeyPress-1>' % accMod, self.OneGraphWindow)
widget.bind('<%s-KeyPress-2>' % accMod, self.TwoGraphWindow)
widget.bind('<%s-question>' % accMod, self.HelpBox)
def KeyStart(self, event):
""" Command linked to toolbar 'Start' """
if self.buttonStart['state'] != DISABLED:
self.CmdStart()
def KeyStop(self, event):
if self.buttonStop['state'] != DISABLED:
self.CmdStop()
def KeyStep(self, event):
""" Command linked to toolbar 'Step' """
if self.buttonStep['state'] != DISABLED:
self.CmdStep()
else:
self.KeyStart(event)
def KeyContinue(self, event):
""" Command linked to toolbar 'Continue' """
if self.buttonContinue['state'] != DISABLED:
self.CmdContinue()
def KeyTrace(self, event):
""" Command linked to toolbar 'Trace' """
if self.buttonTrace['state'] != DISABLED:
self.CmdTrace()
def KeyBreak(self, event):
""" Command for toggling breakpoints """
self.algorithm.ToggleBreakpoint()
def KeyReplay(self, event):
""" Command for Replaying last animation """
self.algorithm.Replay()
def KeyUndo(self, event):
""" Command for Replaying last animation """
self.algorithm.Undo()
def KeyDo(self, event):
""" Command for Replaying last animation """
self.algorithm.Do()
############################################################
#
# Mouse Commands
#
#
# handleMouse
def handleMouse(self, event):
""" Callback for canvas to allow toggeling of breakpoints """
currLine = string.splitfields(self.algoText.index(CURRENT),'.')[0]
self.algorithm.ToggleBreakpoint(string.atoi(currLine))
############################################################
#
# Public methods (for callbacks from algorithm)
#
def ShowActive(self, lineNo):
""" Show lineNo as active line """
if self.lastActiveLine != 0:
self.unTagLine(self.lastActiveLine,'Active')
self.lastActiveLine = lineNo
self.tagLine(lineNo,'Active')
self.algoText.yview_pickplace('%d.0' % lineNo)
self.update() # Forcing redraw
def ShowBreakpoint(self, lineNo):
""" Show lineNo as breakpoint """
self.tagLine(lineNo,'Break')
def HideBreakpoint(self, lineNo):
""" Show lineNo w/o breakpoint """
self.unTagLine(lineNo,'Break')
def WaitNextEvent(self):
""" Stop Execution until user does something. This avoids
busy idling. See touchLock() """
self.wait_variable(self.goOn)
def WaitTime(self, delay):
""" Stop Execution until delay is passed. This avoids
busy idling. See touchLock() """
self.after(delay,self.touchLock)
self.wait_variable(self.goOn)
def ClickHandler(self,type,t):
""" *Internal* Callback for GraphDisplay """
self.clickResult = (type,t)
self.touchLock()
def PickInteractive(self, type, filterChoice=None, default=None):
""" Pick a vertex or an edge (specified by 'type') interactively
GUI blocks until
- a fitting object is clicked
- the algorithm is stopped
- 'Step' is clicked which will randomly select a vertex or an
edge
filterChoice is an optional method (only argument: the vertex or edge).
It returns true if the choice is acceptable
NOTE: To avoid fatal blocks randomly selected objects are not
subjected to filterChoice
"""
self.graphDisplay.RegisterClickhandler(self.ClickHandler)
if default == "None":
self.graphDisplay.UpdateInfo("Select a " + type +
" or click 'Step' or 'Continue' for no selection")
elif default == None:
self.graphDisplay.UpdateInfo("Select a " + type +
" or click 'Step' or 'Continue' for random selection")
else:
self.graphDisplay.UpdateInfo("Select a " + type +
" or click 'Step' or 'Continue' for default selection")
self.clickResult = (None,None)
goOn = 1
while goOn == 1:
self.wait_variable(self.goOn)
if self.clickResult[0] == type:
if filterChoice != None:
if filterChoice(self.clickResult[1]):
goOn = 0
else:
goOn = 0
if self.clickResult[0] in ['abort','auto']:
goOn = 0
self.graphDisplay.UnregisterClickhandler()
self.graphDisplay.DefaultInfo()
if self.clickResult[0] == 'auto':
return None
else:
return self.clickResult[1]
def HandleFileIOError(self, fileDescription, fileName):
log.error("%s file named %s produced an error" % (fileDescription, fileName))
# Endof: AlgoWin ---------------------------------------------------------------
class AlgorithmDebugger(bdb.Bdb):
"""*Internal* Bdb subclass to allow debugging of algorithms
REALLY UGLY CODE: Written before I understood the Debugger.
Probably should use sys.settrace() directly with the different
modes of debugging encoded in appropriate methods"""
def __init__(self,dbgGUI):
""" *Internal* dbgGUI is the GUI for the debugger """
self.GUI = dbgGUI
bdb.Bdb.__init__(self)
self.doTrace = 0
self.lastLine = -1
def dispatch_line(self, frame):
""" *Internal* Only dispatch if we are in the algorithm file """
fn = frame.f_code.co_filename
if fn != self.GUI.algoFileName:
return None
line = self.currentLine(frame)
if line == self.lastLine:
return self.trace_dispatch
self.lastLine = line
self.user_line(frame)
if self.quitting:
raise bdb.BdbQuit
return self.trace_dispatch
def dispatch_call(self, frame, arg):
fn = frame.f_code.co_filename
line = self.currentLine(frame)
doTrace = self.doTrace # value of self.doTrace might change
# No tracing of functions defined outside of our algorithmfile
if fn != self.GUI.algoFileName:
return None
#import inspect
#log.debug("dispatch_call %s %s %s %s %s %s" % (fn, line, frame, self.stop_here(frame), self.break_anywhere(frame), self.break_here(frame)))
#log.debug("%s" % inspect.getframeinfo(frame))
frame.f_locals['__args__'] = arg
if self.botframe is None:
# First call of dispatch since reset()
self.botframe = frame
return self.trace_dispatch
#if self.stop_here(frame) or self.break_anywhere(frame):
# return self.trace_dispatch
self.user_call(frame, arg)
if self.quitting: raise bdb.BdbQuit
if doTrace == 1:
self.doTrace = 0
return self.trace_dispatch
if self.break_anywhere(frame):
self.doTrace = 0
return self.trace_nofeedback_dispatch
return None
def trace_nofeedback_dispatch(self, frame, event, arg):
if self.quitting:
return # None
if event == 'line':
line = self.currentLine(frame)
if line in self.GUI.breakpoints:
self.GUI.mode = 2
return self.dispatch_line(frame)
else:
return None
if event == 'call':
return self.dispatch_call(frame, arg)
if event == 'return':
return self.dispatch_return(frame, arg)
if event == 'exception':
return self.dispatch_exception(frame, arg)
log.debug("bdb.Bdb.dispatch: unknown debugging event: %s" % event)
def reset(self):
""" *Internal* Put debugger into initial state, calls forget() """
bdb.Bdb.reset(self)
self.forget()
def forget(self):
self.lineno = None
self.stack = []
self.curindex = 0
self.curframe = None
def setup(self, f, t):
#self.forget()
self.stack, self.curindex = self.get_stack(f, t)
self.curframe = self.stack[self.curindex][0]
def user_call(self, frame, argument_list):
""" *Internal* This function is called when we stop or break
at this line """
line = self.currentLine(frame)
# log.debug("*user_call* %s %s" % (line, argument_list))
if self.doTrace == 1:
line = self.currentLine(frame)
if line in self.GUI.breakpoints:
self.GUI.mode = 2
self.GUI.GUI.ShowActive(line)
# TO Avoid multiple steps in def line of called fun
#self.interaction(frame, None)
self.doTrace = 0
else:
pass
def user_line(self, frame):
""" *Internal* This function is called when we stop or break at this line """
self.doTrace = 0 # XXX
line = self.currentLine(frame)
# log.debug("*user_line* %s" % line)
if line in self.GUI.breakpoints:
self.GUI.mode = 2
self.GUI.GUI.ShowActive(line)
self.interaction(frame, None)
def user_return(self, frame, return_value):
""" *Internal* This function is called when a return trap is set here """
frame.f_locals['__return__'] = return_value
#log.debug('--Return--')
#self.doTrace = 0 #YYY
# TO Avoid multiple steps in return line of called fun
#self.interaction(frame, None)
def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
""" *Internal* This function is called if an exception occurs,
but only if we are to stop at or just below this level """
frame.f_locals['__exception__'] = exc_type, exc_value
if type(exc_type) == type(''):
exc_type_name = exc_type
else: exc_type_name = exc_type.__name__
#log.debug("exc_type_name: %s" repr.repr(exc_value))
self.interaction(frame, exc_traceback)
def interaction(self, frame, traceback):
""" *Internal* This function does all the interaction with the user
depending on self.GUI.mode
- Step (self.GUI.mode == 2)
- Quit (self.GUI.mode == 0)
- Auto-run w/timer (self.GUI.mode == 1)"""
self.setup(frame, traceback)
#
#line = self.currentLine(frame)
if self.GUI.mode == 2:
old = self.GUI.mode
self.GUI.GUI.WaitNextEvent() # user event -- might change self.GUI.mode
#log.debug("self.GUI.mode: %s -> %s " % (old, self.GUI.mode))
#if self.GUI.mode == 2:
#self.do_next()
if self.GUI.mode == 0:
self.do_quit()
return # Changed
if self.GUI.mode == 1:
self.GUI.GUI.WaitTime(10) # timer event was 100
#self.do_next()
self.forget()
def do_next(self):
self.set_next(self.curframe)
def do_quit(self):
self.set_quit()
def currentLine(self, frame):
""" *Internal* returns the current line number """
return frame.f_lineno
# Endof: AlgorithmDebugger ----------------------------------------------------
class Algorithm:
""" Provides all services necessary to load an algorithm, run it
and provide facilities for visualization """
def __init__(self):
self.DB = AlgorithmDebugger(self)
self.source = "" # Source as a big string
self.interactive = []
self.breakpoints = [] # Doesnt debugger take care of it ?
self.algoFileName = ""
self.graphFileName = ""
self.mode = 0
# mode = 0 Stop
# mode = 1 Running
# mode = 2 Stepping
self.graph = None # graph for the algorithm
self.cleanGraphCopy = None # this is the backup of the graph
self.graphIsDirty = 0 # If graph was changed by running
self.algoGlobals = {} # Sandbox for Algorithm
self.logAnimator = 1
self.about = None
self.commentPattern = re.compile('[ \t]*#')
self.blankLinePattern = re.compile('[ \t]*\n')
def SetGUI(self, itsGUI):
""" Set the connection to its GUI """
self.GUI = itsGUI
def Open(self,file):
""" Read in an algorithm from file. """
self.ClearBreakpoints()
self.algoFileName = file
input=open(file, 'r')
self.source = input.read()
input.close()
# Now read in the prolog as a module to get access to the following data
# Maybe should obfuscate the names ala xxx_<bla>, have one dict ?
try:
input = open(os.path.splitext(self.algoFileName)[0] + ".pro", 'r')
options = self.ReadPrologOptions(input)
input.close()
except EOFError, IOError:
self.GUI.HandleFileIOError("Prolog",file)
return
try:
self.breakpoints = options['breakpoints']
except:
self.breakpoints = []
try:
self.interactive = options['interactive']
except:
self.interactive = []
try:
self.graphDisplays = options['graphDisplays']
except:
self.graphDisplays = None
try:
self.about = options['about']
except:
self.about = None
if self.graphDisplays != None:
if self.graphDisplays == 1 and hasattr(self,"GUI"):
self.GUI.WithdrawSecondaryGraphDisplay()
def ReadPrologOptions(self, file):
""" Prolog files should contain the following variables:
- breakpoints = [] a list of line numbers which are choosen as default
breakpoints
- interactive = [] a list of line numbers which contain interactive commands
(e.g., PickVertex)
- graphDisplays = 1 | 2 the number of graphDisplays needed by the algorithm
- about = \"\"\"<HTML-code>\"\"\" information about the algorithm
Parameter: filelike object
"""
import re
import sys
text = file.read()
options = {}
optionPattern = {'breakpoints':'breakpoints[ \t]*=[ \t]*(\[[^\]]+\])',
'interactive':'interactive[ \t]*=[ \t]*(\[[^\]]+\])',
'graphDisplays':'graphDisplays[ \t]*=[ \t]*([1-2])'}
# about is more complicated
for patternName in optionPattern.keys():
compPattern = re.compile(optionPattern[patternName])
match = compPattern.search(text)
if match != None:
options[patternName] = eval(match.group(1))
# Special case with about (XXX: assuming about = """ ... """)
try:
aboutStartPat = re.compile('about[ \t]*=[ \t]*"""')
aboutEndPat = re.compile('"""')
left = aboutStartPat.search(text).end()
right = aboutEndPat.search(text, left).start()
options['about'] = text[left:right]
except:
pass
return options
def About(self):
""" Return a HTML-page giving information about the algorithm """
if self.about != None:
return self.about
else:
return "<HTML><BODY> <H3>No information available</H3></BODY></HTML>"
def OpenGraph(self,file,fileName=None):
""" Read in a graph from file and open the display """
if type(file) in types.StringTypes:
self.graphFileName = file
elif type(file)==types.FileType or issubclass(file.__class__,StringIO.StringIO):
self.graphFileName = fileName
else:
raise Exception("wrong types in argument list: expected string or file like object")
self.cleanGraphCopy = OpenCATBoxGraph(file)
self.restoreGraph()
self.GUI.graphDisplay.Show() # In case we are hidden
self.GUI.graphDisplay.ShowGraph(self.graph, stripPath(self.graphFileName))
self.GUI.graphDisplay.RegisterGraphInformer(WeightedGraphInformer(self.graph))
def restoreGraph(self):
self.graph=copy.deepcopy(self.cleanGraphCopy)
self.graphIsDirty = 0
def OpenSecondaryGraph(self,G,title,informer=None):
""" Read in graph from file and open the the second display """
self.GUI.OpenSecondaryGraphDisplay()
self.GUI.secondaryGraphDisplay.ShowGraph(G, title)
if informer != None:
self.GUI.secondaryGraphDisplay.RegisterGraphInformer(informer)
def ReadyToStart(self):
""" Return 1 if we are ready to run. That is when we user
has opened both an algorithm and a graph. """
if self.graphFileName != "" and self.algoFileName != "":
return 1
else:
return 0
def Start(self):
""" Start an loaded algorithm. It firsts execs the prolog and
then starts the algorithm in the debugger. The algorithms
globals (i.e., the top-level locals are in a dict we supply
and for which we preload the packages we want to make available)"""
if self.graphIsDirty == 1:
self.restoreGraph()
# Does show
self.GUI.graphDisplay.Show() # In case we are hidden
self.GUI.graphDisplay.ShowGraph(self.graph, stripPath(self.graphFileName))
self.GUI.graphDisplay.RegisterGraphInformer(WeightedGraphInformer(self.graph))
else:
self.GUI.graphDisplay.Show() # In case we are hidden
self.graphIsDirty = 1
self.mode = 1
# Set global vars ...
self.algoGlobals = {}
self.algoGlobals['self'] = self
self.algoGlobals['G'] = self.graph
self.animation_history = None
if self.logAnimator:
self.animation_history = AnimationHistory(self.GUI.graphDisplay)
self.algoGlobals['A'] = self.animation_history
else:
self.algoGlobals['A'] = self.GUI.graphDisplay
# XXX
# explictely loading packages we want to make available to the algorithm
modules = ['DataStructures',
'AnimatedDataStructures',
'AnimatedAlgorithms',
'GraphUtil',
'GatoUtil']
for m in modules:
exec("from %s import *" % m, self.algoGlobals, self.algoGlobals)
# transfer required globals
self.algoGlobals['gInteractive'] = globals()['gInteractive']
# Read in prolog and execute it
try:
execfile(os.path.splitext(self.algoFileName)[0] + ".pro",
self.algoGlobals, self.algoGlobals)
except:
log.exception("Bug in %s.pro" % os.path.splitext(self.algoFileName)[0])
#traceback.print_exc()
# Read in algo and execute it in the debugger
file = self.algoFileName
# Filename must be handed over in a very safe way
# because of \ and ~1 under windows
self.algoGlobals['_tmp_file']=self.algoFileName
# Switch on all shown breakpoints
for line in self.breakpoints:
self.DB.set_break(self.algoFileName,line)
try:
command = "execfile(_tmp_file)"
self.DB.run(command, self.algoGlobals, self.algoGlobals)
except:
log.exception("Bug in %s" % self.algoFileName)
#traceback.print_exc()
self.GUI.CommitStop()
def Stop(self):
self.mode = 0
def Step(self):
if self.animation_history is not None:
self.animation_history.DoAll()
self.DB.doTrace = 0
self.mode = 2
def Continue(self):
if self.animation_history is not None:
self.animation_history.DoAll()
self.DB.doTrace = 0
self.mode = 1
def Trace(self):
if self.animation_history is not None:
self.animation_history.DoAll()
self.mode = 2
self.DB.doTrace = 1
def Replay(self):
#self.GUI.CmdStep()
if self.animation_history is not None:
self.animation_history.DoAll()
self.animation_history.Replay()
def Undo(self):
#self.GUI.CmdStep()
if self.animation_history is not None:
self.animation_history.Undo()
def Do(self):
#self.GUI.CmdStep()
if self.animation_history is not None:
self.animation_history.Do()
def ClearBreakpoints(self):
""" Clear all breakpoints """
for line in self.breakpoints:
self.GUI.HideBreakpoint(line)
self.DB.clear_break(self.algoFileName,line)
self.breakpoints = []
def SetBreakpoints(self, list):
""" SetBreakpoints is depreciated
NOTE: Use 'breakpoint' var in prolog instead.
Set all breakpoints in list: So an algorithm prolog
can set a bunch of pre-assigned breakpoints at once """
log.info("SetBreakpoints() is depreciated. Use 'breakpoint' var in prolog instead. ")
for line in list:
self.GUI.ShowBreakpoint(line)
self.breakpoints.append(line)
self.DB.set_break(self.algoFileName,line)
def ToggleBreakpoint(self,line = None):
""" If we have a breakpoint on line, delete it, else add it.
If no line is passed we ask the DB for it"""
if line == None:
line = self.DB.lastLine
if line in self.breakpoints:
self.GUI.HideBreakpoint(line)
self.breakpoints.remove(line)
self.DB.clear_break(self.algoFileName,line)
else: # New Breakpoint
# check for not breaking in comments nor on empty lines.
import linecache
codeline = linecache.getline(self.algoFileName,line)
if codeline != '' and self.commentPattern.match(codeline) == None and self.blankLinePattern.match(codeline) == None:
self.GUI.ShowBreakpoint(line)
self.breakpoints.append(line)
self.DB.set_break(self.algoFileName,line)
def GetInteractiveLines(self):
""" Return lines on which user interaction (e.g., choosing a
vertex occurrs. """
return self.interactive
def GetBreakpointLines(self):
""" Return lines on which user interaction (e.g., choosing a
vertex occurrs. """
return self.breakpoints
def GetSource(self):
""" Return the algorithms source """
return self.source
def NeededProperties(self, propertyValueDict):
""" Check that graph has that value for each property
specified in the dictionary 'propertyValueDict'
If check fails algorithm is stopped
Proper names for properties are defined in gProperty """
for property in propertyValueDict.keys():
value = self.graph.Property(property)
if value != propertyValueDict[property]:
r = askokcancel("Gato - Error",
"The algorithm you started requires that the graph " +
"it works on has certain properties. The graph does " +
"not have the correct value " +
"for the property '" + property + "'.\n" +
"Do you still want to proceed ?")
if not r:
self.GUI.CmdStop()
def PickVertex(self, default=None, filter=None, visual=None):
""" Pick a vertex interactively.
- default: specifies the vertex returned when user does not
want to select one. If default==None, a random
vertex not subject to filter will be returned.
- filter: a function which should return a non-None value
if the passed vertex is acceptable
- visual is a function which takes the vertex as its
only argument and cause e.g. some visual feedback """
v = None
#log.debug("pickVertex %s" %s globals()['gInteractive'])
if globals()['gInteractive'] == 1:
v = self.GUI.PickInteractive('vertex', filter, default)
if v == None:
if default == None:
v = random.choice(self.graph.vertices)
else:
v = default
if visual is not None:
visual(v)
return v
def PickEdge(self, default=None, filter=None, visual=None):
""" Pick an edge interactively
- default: specifies the edge returned when user does not
want to select one. If default==None, a random
edge not subject to filter will be returned
- filter: a function which should return a non-None value
if the passed edge is acceptable
- visual is a function which takes the edge as its
only argument and cause e.g. some visual feedback """
e = None
if globals()['gInteractive'] == 1:
e = self.GUI.PickInteractive('edge', filter, default)
if e == None:
if default == None:
e = random.choice(self.graph.Edges())
else:
e = default
if visual is not None:
visual(e)
return e
################################################################################
def usage():
print "Usage: Gato.py"
print " Gato.py -v algorithm.alg graph.cat | gato-file"
print " -v or --verbose switches on the debugging/logging information"
if __name__ == '__main__':
import getopt
try:
opts, args = getopt.getopt(sys.argv[1:], "v", ["verbose"])
except getopt.GetoptError:
usage()
sys.exit(2)
if (len(args) < 3):
import logging
log = logging.getLogger("Gato.py")
for o, a in opts:
if o in ("-v", "--verbose"):
logging.verbose = 1
tk = Tk()
# Prevent the Tcl console from popping up in standalone apps on MacOS X
# Checking for hasattr(sys,'frozen') does not work for bundelbuilder
try:
tk.tk.call('console','hide')
except tkinter.TclError:
pass
#tk.option_add('*ActiveBackground','#EEEEEE')
tk.option_add('*background','#DDDDDD')
#XXX Buttons look ugly with white backgrounds on MacOS X, added directly to Button(...)
# The option not working is might be a known bug
# http://aspn.activestate.com/ASPN/Mail/Message/Tcl-bugs/2131881
# Still present in the 8.4.7 that comes with 10.4
tk.option_add('*Highlightbackground','#DDDDDD')
tk.option_add('*Button.highlightbackground','#DDDDDD')
tk.option_add('*Button.background','#DDDDDD')
tk.option_add('Tk*Scrollbar.troughColor','#CACACA')
app = AlgoWin(tk)
# On MacOS X the Quit menu entry otherwise bypasses our Quit Handler
# According to
# http://mail.python.org/pipermail/pythonmac-sig/2006-May/017432.html
# this should work, Maybr
if app.windowingsystem == 'aqua':
tk.tk.createcommand("::tk::mac::Quit",app.Quit)
#======================================================================
# Gato.py <algorithm> <graph>
if len(args) == 2:
algorithm = args[0]
graph = args[1]
app.OpenAlgorithm(algorithm)
app.update_idletasks()
app.update()
app.OpenGraph(graph)
app.update_idletasks()
app.update()
app.after_idle(app.CmdContinue) # after idle needed since CmdStart
app.CmdStart()
app.update_idletasks()
elif len(args)==1:
# expect gato file name or url
fileName=args[0]
app.OpenGatoFile(fileName)
app.update_idletasks()
app.update()
app.mainloop()
else:
usage()
sys.exit(2)
|