Ejemplo n.º 1
0
def main(args):
    """Load two lists of strokes and return a score for them"""
    if len(args) < 3:
        print "Usage: %s <strokes1.dat> <strokes2.dat>" % (args[0])
        exit(1)
    strokeLoader = StrokeStorage(args[1])
    strokeList1 = list(strokeLoader.loadStrokes())
    strokeLoader = StrokeStorage(args[2])
    strokeList2 = list(strokeLoader.loadStrokes())
    score = compareStrokeLists(strokeList1, strokeList2)
    print "Similarity score: %s" % (score)
Ejemplo n.º 2
0
    def __init__(self, master = None, **kargs):
        "Set up the Tkinter GUI stuff as well as the board logic"
        global HEIGHT, WIDTH

        Frame.__init__(self, master, **kargs)
        self.pack()
        #***********************
        #Set up the underlying board stuff
        #***********************

        self.Board = None
        self.TMVisualizer = None
        self.ResetBoard()

        #***********************
        #Set up the GUI stuff
        #***********************

        self.drawMenuOptions = {}
        
        self.BoardCanvas= Canvas(self, width=WIDTH, height = HEIGHT, bg="white", bd=2)
        self.BoardCanvas.pack(side=BOTTOM)
        #Left click bindings
        self.BoardCanvas.bind("<ButtonPress-1>", self.CanvasMouseDown)
        self.BoardCanvas.bind("<B1-Motion>", self.CanvasMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-1>", self.CanvasMouseUp)      

        #Right click bindings
        self.BoardCanvas.bind("<ButtonPress-3>", self.CanvasRightMouseDown)
        self.BoardCanvas.bind("<B3-Motion>", self.CanvasRightMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-3>", self.CanvasRightMouseUp)      

        #self.BoardCanvas.bind("<Enter>", self.CanvasMouseEnter)
        #self.BoardCanvas.bind("<Leave>", self.CanvasMouseLeave)

        self.StrokeLoader = StrokeStorage()
        self.MakeMenu()

        stringLabel = Label(self, text="Turing String")
        stringLabel.pack(side=LEFT)
        self.StringText = Entry(self, width=20)
        self.StringText.pack(side=LEFT)

        self.SimButton = Button(self, text="Step", command = (lambda: self.StepMachines() or self.Redraw()) )
        self.SimButton.pack(side=LEFT)
        self.SimButton = Button(self, text="Restart", command = (lambda: self.RestartMachines() or self.Redraw()))
        self.SimButton.pack(side=LEFT)

        self.CurrentPointList = []
        self.StrokeList = []

        self.shouldDrawAnnos = True
        self.shouldDrawStrokes = True

       
        #print "Redraw from Init"
        self.Redraw()
Ejemplo n.º 3
0
    def __init__(self):
        "Set up the Tkinter GUI stuff as well as the board logic"
        global HEIGHT, WIDTH

        #Set up all the logical board stuff before displaying anything
        self.running = False
        self.isFullScreen = False
        self.OpQueue = Queue.Queue()
        self.StrokeQueue = Queue.Queue()
        self.Board = None
        self.CurrentPointList = []
        self._tempLines = []
        self.StrokeList = []
        self.AnimatorDrawtimes = {} #A dictionary of Animator subclasses to the
                                    #deadline for the next frame draw
        self.StrokeLoader = StrokeStorage()
        #self.SetupImageServer()
        self.ResetBoard()

        root = self.root = Tk()
        #capture = Tk() #Used exclusively to grab keyboard events
        #capture.focus_set()
        #sw = root.winfo_screenwidth()
        #sh = root.winfo_screenheight()

        self.root.title("Sketchy/Scratch")
        #root.overrideredirect(True) # Get rid of the menu bars
        #root.geometry("%dx%d+1024+1" % (WIDTH, HEIGHT)) #Set to full screen
        #root.focus_set() #Make sure we can grab keyboard
        Frame.__init__(self, self.root)
        self.pack()
        #Set up the GUI stuff

        self.drawMenuOptions = {}

        self.BoardCanvas= Canvas(self,
                    width=WIDTH, height = HEIGHT,
                    bg="black", bd=2)
        self.BoardCanvas.pack(side=BOTTOM)
        self.root.bind("<Alt-Return>", lambda e: self.toggleFullscreen() )
        self.root.bind("<Escape>", lambda e: self.toggleFullscreen() )
        #Left click bindings
        self.BoardCanvas.bind("<ButtonPress-1>", self.CanvasMouseDown)
        self.BoardCanvas.bind("<B1-Motion>", self.CanvasMouseDown)
        self.BoardCanvas.bind("<ButtonRelease-1>", self.CanvasMouseUp)

        #Right click bindings
        self.BoardCanvas.bind("<ButtonPress-3>", self.CanvasRightMouseDown)
        self.BoardCanvas.bind("<B3-Motion>", self.CanvasRightMouseDown)
        self.BoardCanvas.bind("<ButtonRelease-3>", self.CanvasRightMouseUp)

        #Middle click bindings
        self.BoardCanvas.bind("<ButtonPress-2>", self.CanvasMiddleMouseDown)
        self.BoardCanvas.bind("<B2-Motion>", self.CanvasMiddleMouseDown)
        self.BoardCanvas.bind("<ButtonRelease-2>", self.CanvasMiddleMouseUp)
        self.SetCommandBindings(self.root, makeMenu=False)
        self.Redraw()
Ejemplo n.º 4
0
    def __init__(self):
        "Set up the Tkinter GUI stuff as well as the board logic"
        global HEIGHT, WIDTH


        self.root = Tk()
        self.root.title("Sketchy/Scratch")
        Frame.__init__(self, self.root)
        self.pack()
        #Set up the GUI stuff

        self.drawMenuOptions = {}
        
        self.BoardCanvas= Canvas(self, width=WIDTH, height = HEIGHT, bg="white", bd=2)
        self.BoardCanvas.pack(side=BOTTOM)
        self.ClassNameEntry = Entry(self)
        self.ClassNameEntry['width'] = 30
        self.ClassNameEntry.pack(side=RIGHT)

        #Left click bindings
        self.BoardCanvas.bind("<ButtonPress-1>", self.CanvasMouseDown)
        self.BoardCanvas.bind("<B1-Motion>", self.CanvasMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-1>", self.CanvasMouseUp)      

        #Right click bindings
        self.BoardCanvas.bind("<ButtonPress-3>", self.CanvasRightMouseDown)
        self.BoardCanvas.bind("<B3-Motion>", self.CanvasRightMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-3>", self.CanvasRightMouseUp)      

        #Middle click bindings
        self.BoardCanvas.bind("<ButtonPress-2>", self.CanvasMiddleMouseDown)
        self.BoardCanvas.bind("<B2-Motion>", self.CanvasMiddleMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-2>", self.CanvasMiddleMouseUp)      
        self.MakeMenu()

        #Set up all the logical board stuff before displaying anything
        self.StrokeQueue = Queue.Queue()
        self.CurrentPointList = []
        self.StrokeList = []
        self.StrokeLoader = StrokeStorage()
        self.ResetBoard()
        self._strokeTrainer = Rubine.RubineClassifier(featureSet = FEATURESET(), debug = True)
        #self.NewTrainingClass()

        self.Redraw()

        self.run()
Ejemplo n.º 5
0
    def __init__(self, dims = (WIDTH, HEIGHT) ):
        # Create a new window
        gtk.DrawingArea.__init__(self)
        self.resize(*dims)# BREAKS when X forwarding

        #Semantic board data
        self.board = None # set by resetBoard()
        self.boardProcThread = BoardThread(self.board) # set by resetBoard()

        #GUI data variables
        self.shouldDrawStrokes = True
        self.currentPoints = None # set by resetBoard()
        self.strokeList = None # set by resetBoard()
        self.opQueue = None # set by resetBoard()

        self.isMouseDown1 = False
        self.isMouseDown3 = False
        self.keyCallbacks = {}
        self.strokeQueue = ProcQueue()
        self.strokeLoader = StrokeStorage()
        self.screenImage = None
        self._pixbuf = None
        self._isFullscreen = False
        #Cairo drawing data
        self.renderBuffer = None
        self.context = None 



        #Event hooks
        gobject.idle_add(self.processOps) #Idle event
        gobject.idle_add(self.processQueuedStrokes) #Async stroke processing
        self.set_property("can-focus", True) #So we can capture keyboard events
        self.connect("button_press_event", self.onMouseDown)
        self.connect("motion_notify_event", self.onMouseMove)
        self.connect("button_release_event", self.onMouseUp)
        self.connect("key_press_event", self.onKeyPress)
        self.connect("expose_event", self.onExpose)
        self.set_events(gtk.gdk.BUTTON_RELEASE_MASK | 
                        gtk.gdk.BUTTON_PRESS_MASK |
                        gtk.gdk.KEY_PRESS_MASK |
                        gtk.gdk.EXPOSURE_MASK |
                        gtk.gdk.POINTER_MOTION_MASK )

        #Enable the board processing
        self.resetBoard()
Ejemplo n.º 6
0
    def __init__(self):
        "Set up the Tkinter GUI stuff as well as the board logic"
        global HEIGHT, WIDTH

        #Set up all the logical board stuff before displaying anything
        self.StrokeQueue = Queue.Queue()
        self.Board = None
        self.CurrentPointList = []
        self.StrokeList = []
        self.AnimatorDrawtimes = {} #A dictionary of Animator subclasses to the deadline for the next frame draw 
        self.StrokeLoader = StrokeStorage()
        #self.SetupImageServer()
        self.ResetBoard()

        self.root = Tk()
        self.root.title("Sketchy/Scratch")
        Frame.__init__(self, self.root)
        self.pack()
        #Set up the GUI stuff

        self.drawMenuOptions = {}
        
        self.BoardCanvas= Canvas(self, width=WIDTH, height = HEIGHT, bg="white", bd=2)
        self.BoardCanvas.pack(side=BOTTOM)
        #Left click bindings
        self.BoardCanvas.bind("<ButtonPress-1>", self.CanvasMouseDown)
        self.BoardCanvas.bind("<B1-Motion>", self.CanvasMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-1>", self.CanvasMouseUp)      

        #Right click bindings
        self.BoardCanvas.bind("<ButtonPress-3>", self.CanvasRightMouseDown)
        self.BoardCanvas.bind("<B3-Motion>", self.CanvasRightMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-3>", self.CanvasRightMouseUp)      

        #Middle click bindings
        self.BoardCanvas.bind("<ButtonPress-2>", self.CanvasMiddleMouseDown)
        self.BoardCanvas.bind("<B2-Motion>", self.CanvasMiddleMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-2>", self.CanvasMiddleMouseUp)      
        self.MakeMenu()
        self.Redraw()

        self.run()
Ejemplo n.º 7
0
    def __init__(self, master = None, **kargs):
        "Set up the Tkinter GUI stuff as well as the board logic"
        global HEIGHT, WIDTH

        Frame.__init__(self, master, **kargs)
        self.pack()
        #Set up the GUI stuff

        self.drawMenuOptions = {}
        
        self.BoardCanvas= Canvas(self, width=WIDTH, height = HEIGHT, bg="white", bd=2)
        self.BoardCanvas.pack(side=BOTTOM)
        #Left click bindings
        self.BoardCanvas.bind("<ButtonPress-1>", self.CanvasMouseDown)
        self.BoardCanvas.bind("<B1-Motion>", self.CanvasMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-1>", self.CanvasMouseUp)      

        #Right click bindings
        self.BoardCanvas.bind("<ButtonPress-3>", self.CanvasRightMouseDown)
        self.BoardCanvas.bind("<B3-Motion>", self.CanvasRightMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-3>", self.CanvasRightMouseUp)      

        self.StrokeQueue = Queue.Queue()
        self.Board = None
        self.CurrentPointList = []
        self.StrokeList = []

        self.AnimatorDrawtimes = {} #A dictionary of Animator subclasses to the deadline for the next frame draw 

        self.StrokeLoader = StrokeStorage()
        #self.SetupImageServer()

        self.ResetBoard()
        self.MakeMenu()
       
        self.Redraw()
Ejemplo n.º 8
0
class TkSketchFrame(Frame):
    """The base GUI class. 
    Class must implement drawText, drawLine and drawCircle. X-Y origin is bottom-left corner.
    Aside from these restrictions, interface options (reset board, etc) are up to the GUI programmer."""
    Singleton = None
    def __init__(self, master = None, **kargs):
        "Set up the Tkinter GUI stuff as well as the board logic"
        global HEIGHT, WIDTH

        Frame.__init__(self, master, **kargs)
        self.pack()
        #***********************
        #Set up the underlying board stuff
        #***********************

        self.Board = None
        self.TMVisualizer = None
        self.ResetBoard()

        #***********************
        #Set up the GUI stuff
        #***********************

        self.drawMenuOptions = {}
        
        self.BoardCanvas= Canvas(self, width=WIDTH, height = HEIGHT, bg="white", bd=2)
        self.BoardCanvas.pack(side=BOTTOM)
        #Left click bindings
        self.BoardCanvas.bind("<ButtonPress-1>", self.CanvasMouseDown)
        self.BoardCanvas.bind("<B1-Motion>", self.CanvasMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-1>", self.CanvasMouseUp)      

        #Right click bindings
        self.BoardCanvas.bind("<ButtonPress-3>", self.CanvasRightMouseDown)
        self.BoardCanvas.bind("<B3-Motion>", self.CanvasRightMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-3>", self.CanvasRightMouseUp)      

        #self.BoardCanvas.bind("<Enter>", self.CanvasMouseEnter)
        #self.BoardCanvas.bind("<Leave>", self.CanvasMouseLeave)

        self.StrokeLoader = StrokeStorage()
        self.MakeMenu()

        stringLabel = Label(self, text="Turing String")
        stringLabel.pack(side=LEFT)
        self.StringText = Entry(self, width=20)
        self.StringText.pack(side=LEFT)

        self.SimButton = Button(self, text="Step", command = (lambda: self.StepMachines() or self.Redraw()) )
        self.SimButton.pack(side=LEFT)
        self.SimButton = Button(self, text="Restart", command = (lambda: self.RestartMachines() or self.Redraw()))
        self.SimButton.pack(side=LEFT)

        self.CurrentPointList = []
        self.StrokeList = []

        self.shouldDrawAnnos = True
        self.shouldDrawStrokes = True

       
        #print "Redraw from Init"
        self.Redraw()


      
    def SwitchTuringView(self, showTuring = None):
        if showTuring == None:
            self.shouldDrawStrokes = not self.shouldDrawStrokes
            self.shouldDrawAnnos = not self.shouldDrawAnnos
        else:
            self.shouldDrawStrokes = not showTuring
            self.shouldDrawAnnos = not showTuring
        self.Redraw()
        
    def CanvasMouseEnter(self, event):
        self.shouldDrawStrokes = True
        self.shouldDrawAnnos = True
        #print "Redraw from Mouse Enter"
        self.Redraw()
    def CanvasMouseLeave(self, event):
        self.shouldDrawStrokes = False
        self.shouldDrawAnnos = False
        #print "Redraw from Mouse Leave"
        self.Redraw()

    def SetTapeString(self):
        text = self.StringText.get()
        print "Setting text to %s" % (text)
        for tm_anno in BoardSingleton().FindAnnotations( anno_type = TuringMachineObserver.TuringMachineAnnotation):
            tm_anno.setTapeString(text)
        

    def StepMachines(self):
        for tm_anno in BoardSingleton().FindAnnotations( anno_type = TuringMachineObserver.TuringMachineAnnotation):
            tm_anno.simulateStep()

    def RestartMachines(self):
        self.SetTapeString()
        for tm_anno in BoardSingleton().FindAnnotations( anno_type = TuringMachineObserver.TuringMachineAnnotation):
            tm_anno.restartSimulation()

        
    def MakeMenu(self):
        "Reserve places in the menu for fun actions!"
        win = self.master 
        top_menu = Menu(win)
        win.config(menu=top_menu)
        
        self.object_menu = Menu(top_menu)
        top_menu.bind("<ButtonPress-1>",(lambda e: self.RebuildObjectMenu()))
        self.RebuildObjectMenu()
        top_menu.add_cascade(label="ObjectMenu", menu=self.object_menu)

        top_menu.add_command(label="Reset Board", command = (lambda :self.ResetBoard() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Load stks.txt", command = (lambda : self.LoadStrokes() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Save stks.txt", command = (lambda : self.SaveStrokes()), underline=1 )
        top_menu.add_command(label="Undo Stroke", command = (lambda :self.RemoveLatestStroke() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Strokes From Image", command = (lambda :self.LoadStrokesFromImage() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Toggle View", command = lambda: self.SwitchTuringView(), underline=1 )




    def LoadStrokes(self):
      for stroke in self.StrokeLoader.loadStrokes():
         self.Board.AddStroke(stroke)
         self.StrokeList.append(stroke)

    def SaveStrokes(self):
      self.StrokeLoader.saveStrokes(self.StrokeList)
        
    def LoadStrokesFromImage(self):
        fname = askopenfilename(initialdir='/home/jbrowne/src/sketchvision/images/')
        if fname == "":
           return

        try:
           print "Loading strokes..."
           strokes = imageToStrokes(fname)
        except Exception as e:
           print "Error importing strokes from image '%s':\n %s" % (fname, e)
           return
        print "Loaded %s strokes from '%s'" % (len(strokes), fname)

        for s in strokes:
           newStroke = Stroke()
           for x,y in s.points:
              scale = WIDTH / float(1280)
              newPoint = Point(scale * x,HEIGHT - scale * y)
              newStroke.addPoint(newPoint)
           self.Board.AddStroke(newStroke)
           self.StrokeList.append(newStroke)

    def RemoveLatestStroke(self):
        if len (self.StrokeList) > 0:
            stroke = self.StrokeList.pop()
            self.Board.RemoveStroke(stroke)

    def RebuildObjectMenu(self):
        "Search the board for existing objects, and add a menu entry to manipulate it (drawAll)"
        observers = BoardSingleton().GetBoardObservers()
        draw_vars = {}
        for obs in observers:
            key = obs.__class__
            if key not in self.drawMenuOptions and hasattr(obs, "DrawAll"):
                draw_vars[key] = key.DrawAll
        
        for key, var in draw_vars.items():
            self.drawMenuOptions[key] = self.object_menu.add_command(label=key.__name__,command=(lambda class_=key: self.InvertDraw(class_)), underline = 0)

    def InvertDraw(self, class_):
        "Essentially checkbox behavior for BoardObserver.DrawAll variable"
        if hasattr(class_, "DrawAll"):
            class_.DrawAll = not class_.DrawAll
            self.Redraw()


    def ResetBoard(self):
        "Clear all strokes and board observers from the board (logically and visually)"
        self.p_x = self.p_y = None

        self.Board = BoardSingleton(reset = True)
        initializeBoard(self.Board)
        self.TMVisualizer = TuringMachineObserver.TuringMachineVisualizer()
        self.CurrentPointList = []
        self.StrokeList = []

                
    def CanvasRightMouseDown(self, event):
        x = event.x
        y = event.y
        #self.BoardCanvas.create_oval(x,y,x,y,activewidth="1", fill="black", outline = "black")
        
        if self.p_x != None and self.p_y != None:
            p_x = self.p_x
            p_y = self.p_y
            self.BoardCanvas.create_line(p_x, p_y, x ,y, fill = "gray", width=2)

        x = event.x
        y = HEIGHT - event.y
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))

        self.p_x = x
        self.p_y = HEIGHT - y

    def CanvasRightMouseUp(self, event):
        delStrokes = set([])
        if len(self.CurrentPointList) > 0:
            stroke = Stroke( self.CurrentPointList )#, smoothing=True )
            self.CurrentPointList = []
            for stk in list(self.StrokeList):
                if len(getStrokesIntersection(stroke, stk) ) > 0:
                    print "Removing Stroke"
                    self.Board.RemoveStroke(stk)
                    self.StrokeList.remove(stk)
        self.p_x = self.p_y = None
        #print "Redraw from RightMouseUp"
        self.Redraw()

    def CanvasMouseDown(self, event):
        "Draw a line connecting the points of a stroke as it is being drawn"
        
        x = event.x
        y = event.y
        #self.BoardCanvas.create_oval(x,y,x,y,activewidth="1", fill="black", outline = "black")
        
        if self.p_x != None and self.p_y != None:
            p_x = self.p_x
            p_y = self.p_y
            self.BoardCanvas.create_line(p_x, p_y, x ,y, fill = "black", width=2)

        x = event.x
        y = HEIGHT - event.y
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))

        self.p_x = x
        self.p_y = HEIGHT - y

    def AddCurrentStroke(self):
        if len(self.CurrentPointList) > 0:
            stroke = Stroke( self.CurrentPointList )#, smoothing=True )
            
            self.Board.AddStroke(stroke)
            self.StrokeList.append(stroke)
            self.CurrentPointList = []
            
        
    def CanvasMouseUp(self, event):
        "Finish the stroke and add it to the board"
        #start a new stroke
        self.AddCurrentStroke()
        self.p_x = self.p_y = None
        #print "Redraw from MouseUp"
        self.Redraw()

    def Redraw(self):
        "Find all the strokes on the board, draw them, then iterate through every object and have it draw itself"
        global HEIGHT, WIDTH
        #print "> Redraw Start"
        sys.stdout.flush()
        self.BoardCanvas.delete(ALL)
        strokes = self.Board.Strokes
        observers = self.Board.BoardObservers
        if self.shouldDrawStrokes:
            for s in strokes:
               #print "Drawing stroke %s" % (s.id)
               s.drawMyself()
            #print ">   Strokes drawn"
            sys.stdout.flush()

        if self.shouldDrawAnnos:
            for obs in observers:
               #print "Drawing", obj.__class__.__name__
               if type(obs) != TuringMachineObserver.TuringMachineVisualizer:
                   obs.drawMyself()
            #print ">   Annos drawn"
            sys.stdout.flush()
        else:
            for s in strokes:
               if len(s.findAnnotations(TuringMachineObserver.TuringMachineAnnotation)) == 0:
                   s.drawMyself(color="#cccccc")
            self.TMVisualizer.drawMyself()
        #print "< Redraw Done"
        sys.stdout.flush()

    def drawCircle(self, x, y, radius=1, color="#000000", fill="", width=1.0):
         "Draw a circle on the canvas at (x,y) with radius rad. Color should be 24 bit RGB string #RRGGBB. Empty string is transparent"
         y = HEIGHT - y
         self.BoardCanvas.create_oval(x-radius,y-radius,x+radius,y+radius,width=width, fill=fill, outline = color)
         
    def drawLine(self, x1, y1, x2, y2, width=2, color="#000000"):
         "Draw a line on the canvas from (x1,y1) to (x2,y2). Color should be 24 bit RGB string #RRGGBB"
         y1 = HEIGHT - y1
         y2 = HEIGHT - y2
         self.BoardCanvas.create_line(x1, y1, x2 ,y2, fill=color, width = width)

    def drawText (self, x, y, InText="", size=10, color="#000000"):
        "Draw some text (InText) on the canvas at (x,y). Color as defined by 24 bit RGB string #RRGGBB"
        y = HEIGHT - y
        text_font = ("times", size, "")
        self.BoardCanvas.create_text(x,y,text = InText, fill = color, font = text_font, anchor=NW) 
Ejemplo n.º 9
0
class TkSketchFrame(Frame):
    """The base GUI class. 
    Class must implement drawText, drawLine and drawCircle. X-Y origin is bottom-left corner.
    Aside from these restrictions, interface options (reset board, etc) are up to the GUI programmer."""
    Singleton = None
    def __init__(self, master = None, **kargs):
        "Set up the Tkinter GUI stuff as well as the board logic"
        global HEIGHT, WIDTH

        Frame.__init__(self, master, **kargs)
        self.pack()
        #Set up the GUI stuff

        self.drawMenuOptions = {}
        
        self.BoardCanvas= Canvas(self, width=WIDTH, height = HEIGHT, bg="white", bd=2)
        self.BoardCanvas.pack(side=BOTTOM)
        #Left click bindings
        self.BoardCanvas.bind("<ButtonPress-1>", self.CanvasMouseDown)
        self.BoardCanvas.bind("<B1-Motion>", self.CanvasMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-1>", self.CanvasMouseUp)      

        #Right click bindings
        self.BoardCanvas.bind("<ButtonPress-3>", self.CanvasRightMouseDown)
        self.BoardCanvas.bind("<B3-Motion>", self.CanvasRightMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-3>", self.CanvasRightMouseUp)      

        self.StrokeQueue = Queue.Queue()
        self.Board = None
        self.CurrentPointList = []
        self.StrokeList = []

        self.AnimatorDrawtimes = {} #A dictionary of Animator subclasses to the deadline for the next frame draw 

        self.StrokeLoader = StrokeStorage()
        #self.SetupImageServer()

        self.ResetBoard()
        self.MakeMenu()
       
        self.Redraw()

      
    def MakeMenu(self):
        "Reserve places in the menu for fun actions!"
        win = self.master 
        top_menu = Menu(win)
        win.config(menu=top_menu)
        
        self.object_menu = Menu(top_menu)
        top_menu.bind("<ButtonPress-1>",(lambda e: self.RebuildObjectMenu()))
        self.RebuildObjectMenu()
        top_menu.add_cascade(label="ObjectMenu", menu=self.object_menu)

        top_menu.add_command(label="Reset Board", command = (lambda :self.ResetBoard() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Load strokes.dat", command = (lambda : self.LoadStrokes() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Save strokes.dat", command = (lambda : self.SaveStrokes()), underline=1 )
        top_menu.add_command(label="Undo Stroke", command = (lambda :self.RemoveLatestStroke() or self.Redraw()), underline=1 )
        #top_menu.add_command(label="Strokes From Image", command = (lambda :self.LoadStrokesFromImage() or self.Redraw()), underline=1 )


    def AddQueuedStroke(self):
        #Only process one stroke per round
        if not self.StrokeQueue.empty():
            stk = self.StrokeQueue.get()
            logger.debug("Adding queued stroke %s" % (stk))
            self.Board.AddStroke(stk)
            self.StrokeList.append(stk)
            self.Redraw()
            self.StrokeQueue.task_done()

    def LoadStrokes(self):
      for stroke in self.StrokeLoader.loadStrokes():
         self.Board.AddStroke(stroke)
         self.StrokeList.append(stroke)

    def SaveStrokes(self):
      self.StrokeLoader.saveStrokes(self.StrokeList)
        
    """
    def LoadStrokesFromImage(self):
        fname = askopenfilename(initialdir='/home/jbrowne/src/sketchvision/images/')
        if fname == "":
           return

        try:
           logger.debug( "Loading strokes...")
           strokes = imageToStrokes(fname)
        except Exception as e:
           logger.debug( "Error importing strokes from image '%s':\n %s" % (fname, e))
           return
        logger.debug( "Loaded %s strokes from '%s'" % (len(strokes), fname))

        for s in strokes:
           newStroke = Stroke()
           for x,y in s.points:
              scale = WIDTH / float(1280)
              newPoint = Point(scale * x,HEIGHT - scale * y)
              newStroke.addPoint(newPoint)
           self.Board.AddStroke(newStroke)
           self.StrokeList.append(newStroke)
    """

    def RemoveLatestStroke(self):
        if len (self.StrokeList) > 0:
            stroke = self.StrokeList.pop()
            self.Board.RemoveStroke(stroke)

    def RebuildObjectMenu(self):
        "Search the board for existing objects, and add a menu entry to manipulate it (drawAll)"
        observers = BoardSingleton().GetBoardObservers()
        draw_vars = {}
        for obs in observers:
            key = obs.__class__
            if key not in self.drawMenuOptions and hasattr(obs, "DrawAll"):
                draw_vars[key] = key.DrawAll
        
        for key, var in draw_vars.items():
            self.drawMenuOptions[key] = self.object_menu.add_command(label=key.__name__,command=(lambda class_=key: self.InvertDraw(class_)), underline = 0)

    def InvertDraw(self, class_):
        "Essentially checkbox behavior for BoardObserver.DrawAll variable"
        if hasattr(class_, "DrawAll"):
            class_.DrawAll = not class_.DrawAll
            self.Redraw()


    def ResetBoard(self):
        "Clear all strokes and board observers from the board (logically and visually)"
        self.p_x = self.p_y = None

        self.Board = BoardSingleton(reset = True)
        initialize(self.Board)
        self.RegisterAnimators()
        self.CurrentPointList = []
        self.StrokeList = []


    def RegisterAnimators(self):
        self.AnimatorDrawtimes = {}
        for obs in self.Board.BoardObservers:
            if Animator in type(obs).__mro__: #Check if it inherits from Animator
                logger.debug( "Registering %s as animator" % (obs))
                self.AnimatorDrawtimes[obs] = time.time()
                
                
    def CanvasRightMouseDown(self, event):
        x = event.x
        y = event.y
        #self.BoardCanvas.create_oval(x,y,x,y,activewidth="1", fill="black", outline = "black")
        
        if self.p_x != None and self.p_y != None:
            p_x = self.p_x
            p_y = self.p_y
            self.BoardCanvas.create_line(p_x, p_y, x ,y, fill = "gray", width=2)

        x = event.x
        y = HEIGHT - event.y
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))

        self.p_x = x
        self.p_y = HEIGHT - y

    def CanvasRightMouseUp(self, event):
        delStrokes = set([])
        if len(self.CurrentPointList) > 0:
            stroke = Stroke( self.CurrentPointList )#, smoothing=True )
            self.CurrentPointList = []
            for stk in list(self.StrokeList):
                if len(getStrokesIntersection(stroke, stk) ) > 0:
                    logger.debug( "Removing Stroke")
                    self.Board.RemoveStroke(stk)
                    self.StrokeList.remove(stk)
        self.p_x = self.p_y = None
        self.Redraw()

    def CanvasMouseDown(self, event):
        "Draw a line connecting the points of a stroke as it is being drawn"
        
        x = event.x
        y = event.y
        #self.BoardCanvas.create_oval(x,y,x,y,activewidth="1", fill="black", outline = "black")
        
        if self.p_x != None and self.p_y != None:
            p_x = self.p_x
            p_y = self.p_y
            self.BoardCanvas.create_line(p_x, p_y, x ,y, fill = "black", width=2)

        x = event.x
        y = HEIGHT - event.y
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))

        self.p_x = x
        self.p_y = HEIGHT - y

    def AddCurrentStroke(self):
        if len(self.CurrentPointList) > 0:
            stroke = Stroke( self.CurrentPointList )#, smoothing=True )
            
            self.Board.AddStroke(stroke)
            self.StrokeList.append(stroke)
            self.CurrentPointList = []
            
        
    def CanvasMouseUp(self, event):
        "Finish the stroke and add it to the board"
        #start a new stroke
        self.AddCurrentStroke()
        self.p_x = self.p_y = None
        self.Redraw()

    """
    def SetupImageServer(self):
        self.serverThread = ServerThread(port = 30000)
        self.net_queue = self.serverThread.getResponseQueue()
        self.serverThread.start()
        self.imgProcThread = ImgProcThread(self.net_queue, self.StrokeQueue)
        self.imgProcThread.start()
    """

                
        

        
    def AnimateFrame(self):
        for obs, deadline in self.AnimatorDrawtimes.items():
            if deadline <= 1000 * time.time():
                obs.drawMyself()
                self.AnimatorDrawtimes[obs] = 1000 *( (1 / float(obs.fps)) + time.time() ) #Time the next frame

        
    def Redraw(self):
        "Find all the strokes on the board, draw them, then iterate through every object and have it draw itself"
        global HEIGHT, WIDTH
        self.BoardCanvas.delete(ALL)
        strokes = self.Board.Strokes
        observers = self.Board.BoardObservers
        for s in strokes:
           s.drawMyself()
        for obs in observers:
           obs.drawMyself()

    def drawCircle(self, x, y, radius=1, color="#000000", fill="", width=1.0):
         "Draw a circle on the canvas at (x,y) with radius rad. Color should be 24 bit RGB string #RRGGBB. Empty string is transparent"
         y = HEIGHT - y
         self.BoardCanvas.create_oval(x-radius,y-radius,x+radius,y+radius,width=width, fill=fill, outline = color)
         
    def drawLine(self, x1, y1, x2, y2, width=2, color="#000000"):
         "Draw a line on the canvas from (x1,y1) to (x2,y2). Color should be 24 bit RGB string #RRGGBB"
         y1 = HEIGHT - y1
         y2 = HEIGHT - y2
         self.BoardCanvas.create_line(x1, y1, x2 ,y2, fill=color, width = width)

    def drawText (self, x, y, InText="", size=10, color="#000000"):
        "Draw some text (InText) on the canvas at (x,y). Color as defined by 24 bit RGB string #RRGGBB"
        y = HEIGHT - y
        text_font = ("times", size, "")
        self.BoardCanvas.create_text(x,y,text = InText, fill = color, font = text_font, anchor=NW) 
Ejemplo n.º 10
0
class TkSketchFrame(Frame, _SketchGUI):
    """The base GUI class.
    Class must implement drawText, drawLine and drawCircle. X-Y origin is
    bottom-left corner.
    Aside from these restrictions, interface options (reset board, etc) are up
    to the GUI programmer."""
    def __init__(self):
        "Set up the Tkinter GUI stuff as well as the board logic"
        global HEIGHT, WIDTH

        #Set up all the logical board stuff before displaying anything
        self.running = False
        self.isFullScreen = False
        self.OpQueue = Queue.Queue()
        self.StrokeQueue = Queue.Queue()
        self.Board = None
        self.CurrentPointList = []
        self._tempLines = []
        self.StrokeList = []
        self.AnimatorDrawtimes = {} #A dictionary of Animator subclasses to the
                                    #deadline for the next frame draw
        self.StrokeLoader = StrokeStorage()
        #self.SetupImageServer()
        self.ResetBoard()

        root = self.root = Tk()
        #capture = Tk() #Used exclusively to grab keyboard events
        #capture.focus_set()
        #sw = root.winfo_screenwidth()
        #sh = root.winfo_screenheight()

        self.root.title("Sketchy/Scratch")
        #root.overrideredirect(True) # Get rid of the menu bars
        #root.geometry("%dx%d+1024+1" % (WIDTH, HEIGHT)) #Set to full screen
        #root.focus_set() #Make sure we can grab keyboard
        Frame.__init__(self, self.root)
        self.pack()
        #Set up the GUI stuff

        self.drawMenuOptions = {}

        self.BoardCanvas= Canvas(self,
                    width=WIDTH, height = HEIGHT,
                    bg="black", bd=2)
        self.BoardCanvas.pack(side=BOTTOM)
        self.root.bind("<Alt-Return>", lambda e: self.toggleFullscreen() )
        self.root.bind("<Escape>", lambda e: self.toggleFullscreen() )
        #Left click bindings
        self.BoardCanvas.bind("<ButtonPress-1>", self.CanvasMouseDown)
        self.BoardCanvas.bind("<B1-Motion>", self.CanvasMouseDown)
        self.BoardCanvas.bind("<ButtonRelease-1>", self.CanvasMouseUp)

        #Right click bindings
        self.BoardCanvas.bind("<ButtonPress-3>", self.CanvasRightMouseDown)
        self.BoardCanvas.bind("<B3-Motion>", self.CanvasRightMouseDown)
        self.BoardCanvas.bind("<ButtonRelease-3>", self.CanvasRightMouseUp)

        #Middle click bindings
        self.BoardCanvas.bind("<ButtonPress-2>", self.CanvasMiddleMouseDown)
        self.BoardCanvas.bind("<B2-Motion>", self.CanvasMiddleMouseDown)
        self.BoardCanvas.bind("<ButtonRelease-2>", self.CanvasMiddleMouseUp)
        self.SetCommandBindings(self.root, makeMenu=False)
        self.Redraw()

        #self.run()

    def toggleFullscreen(self):
        if not self.isFullScreen:
            self.root.withdraw()
            #sw = 1024 #self.root.winfo_screenwidth()
            #sh = self.root.winfo_screenheight()
            #sw, sh = 1024, 768
            sw = WIDTH
            sh = HEIGHT
            self.BoardCanvas.config(width = sw, height= sh)
            #self.root.overrideredirect(True) # Get rid of the menu bars
            self.root.deiconify()
            self.root.geometry("%dx%d+1023-50" % (sw, sh)) #Set to full screen
            #self.root.grab_set_global()

            self.capture = Tk() #Used exclusively to grab keyboard events
            self.capture.focus_force()
            self.capture.bind("<Escape>",  
                    lambda e: self.toggleFullscreen())
            self.capture.bind("<Alt-Return>",  
                    lambda e: self.toggleFullscreen())
            self.SetCommandBindings(self.capture, makeMenu=False)

        else:
            self.root.withdraw()
            #self.root.overrideredirect(False)
            self.BoardCanvas.config(width = WIDTH, height = HEIGHT)
            self.root.geometry("%dx%d+0+0" % (WIDTH, HEIGHT))
            self.root.deiconify()
            #self.root.grab_release()
            self.capture.destroy()
            self.capture = None
            self.root.focus_force()
        self.isFullScreen = not self.isFullScreen
            
    def run(self):
       self.running = True
       #self.root.grab_set_global()
       self.root.update()
       try:
           while self.running:
               self.root.update()
               self.root.update_idletasks()
               self.AnimateFrame()
               self.AddQueuedStroke()
               self.runOp()
       except TclError:
           raise
       finally:
          pass
          #self.root.grab_release()

    def stop(self):
        print "Stopping!"
        self.running = False


    def post(self, operation):
        self.OpQueue.put(operation)

    def runOp(self):
        while not self.OpQueue.empty():
            op = self.OpQueue.get()
            op()
            self.OpQueue.task_done()

    def initBoardObservers( observers, debugAnnotations = None ):
        if observers is not None:
            for obs in observers:
                obs(self.Board)

    def SetCommandBindings(self, widget, makeMenu = True):
        "Reserve places in the menu for fun actions!"

        CMD_Reset = (lambda e=1:self.ResetBoard() or 
                     self.Redraw(clear=True))
        CMD_LoadStrokes = (lambda e=1: self.LoadStrokes() or 
                     self.Redraw())
        CMD_SaveStrokes = (lambda e=1: self.SaveStrokes())
        CMD_UndoStroke = (lambda e=1:self.RemoveLatestStroke() or 
                     self.Redraw())
        CMD_ProcessImage = (lambda e=1:self.LoadStrokesFromImage() or 
                     self.Redraw())

        widget.bind_all("<r>", CMD_Reset)
        widget.bind_all("<l>", CMD_LoadStrokes)
        widget.bind("<s>", CMD_SaveStrokes)
        widget.bind("<Control-z>", CMD_UndoStroke)
        widget.bind("<i>", CMD_ProcessImage)
        if makeMenu:
            win = self.master
            self.top_menu = top_menu = Menu(win)
            win.config(menu=top_menu)

            self.object_menu = Menu(top_menu)
            top_menu.bind("<ButtonPress-1>",(lambda e: self.RebuildObjectMenu()))
            self.RebuildObjectMenu()
            top_menu.add_cascade(label="ObjectMenu", menu=self.object_menu)
            top_menu.add_command(label="Reset Board", command = CMD_Reset)
            top_menu.add_command(label="Load strokes.dat",command = CMD_LoadStrokes)
            top_menu.add_command(label="Save strokes.dat",command = CMD_SaveStrokes)
            top_menu.add_command(label="Undo Stroke", command = CMD_UndoStroke)
            top_menu.add_command(label="Strokes From Image", 
                command = CMD_ProcessImage)


    def AddQueuedStroke(self):
        #Only process one stroke per round
        lim = 10
        if not self.StrokeQueue.empty() and lim > 0:
            lim -= 1
            stk = self.StrokeQueue.get()
            logger.debug("Adding queued stroke %s" % (stk))
            self.Board.AddStroke(stk)
            self.StrokeList.append(stk)
            self.Redraw()
            self.StrokeQueue.task_done()

    def LoadStrokes(self):
      for stroke in self.StrokeLoader.loadStrokes():
         self.AddStroke(stroke)
         #self.Board.AddStroke(stroke)
         #self.StrokeList.append(stroke)

    def SaveStrokes(self):
      self.StrokeLoader.saveStrokes(self.StrokeList)

    def LoadStrokesFromImage(self, image = None):
        global WIDTH, HEIGHT
        pruneLen = 10
        if image != None:
            try:
                strokeDict = ImageStrokeConverter.cvimgToStrokes(image)
            except:
                logger.error("Error importing strokes from frame")
                raise
        else:
            fname = askopenfilename(initialdir='/home/jbrowne/src/photos/')
            if fname == "":
               return

            try:
               logger.debug( "Loading strokes...")
               strokeDict = ImageStrokeConverter.imageToStrokes(fname)
               logger.debug( "Loaded %s strokes from '%s'" % 
                   (len(strokeDict['strokes']), fname))
            except Exception as e:
               logger.debug( "Error importing strokes from image '%s':\n %s" % 
                   (fname, e))
               raise

        strokes = strokeDict['strokes']
        w,h = strokeDict['dims']
        scale_x = WIDTH / float(w)
        scale_y = HEIGHT / float(h)
        for s in strokes:
           if len(s.points) > pruneLen:
               pointList = []
               for x,y in s.points:
                  newPoint = Point(scale_x * x, HEIGHT - (scale_y *y))
                  pointList.append(newPoint)
               newStroke = Stroke(pointList)
               self.AddStroke(newStroke)


    def RemoveLatestStroke(self):
        if len (self.StrokeList) > 0:
            stroke = self.StrokeList.pop()
            self.Board.RemoveStroke(stroke)

    def RebuildObjectMenu(self):
        """Search the board for existing objects, and add a menu entry to 
        manipulate it (drawAll)"""
        observers = self.Board.GetBoardObservers()
        draw_vars = {}
        for obs in observers:
            key = obs.__class__
            if key not in self.drawMenuOptions and hasattr(obs, "DrawAll"):
                draw_vars[key] = key.DrawAll

        for key, var in draw_vars.items():
            self.drawMenuOptions[key] = self.object_menu.add_command(
                label=key.__name__,command=(
                    lambda class_=key: self.InvertDraw(class_)
                ), underline = 0
            )

    def InvertDraw(self, class_):
        "Essentially checkbox behavior for BoardObserver.DrawAll variable"
        if hasattr(class_, "DrawAll"):
            class_.DrawAll = not class_.DrawAll
            self.Redraw()


    def InitializeBoard(self):
        """Initialize all of the board observers and register debugable 
        annotations, etc."""
        Config.initializeBoard(self.Board)


    def ResetBoard(self):
        """Clear all strokes and board observers from the board (logically and 
        visually)"""
        self.p_x = self.p_y = None

        self.Board = Board(gui = self)
        Config.initializeBoard(self.Board)
        self.RegisterAnimators()
        self.CurrentPointList = []
        self.StrokeList = []
        self._tempLines = []
        #Clear pending strokes
        while not self.StrokeQueue.empty():
            self.StrokeQueue.get()
            self.StrokeQueue.task_done()


    def RegisterAnimators(self):
        self.AnimatorDrawtimes = {}
        for obs in self.Board.BoardObservers:
            if Animator in type(obs).__mro__: #Check if it inherits from Animator
                logger.debug( "Registering %s as animator" % (obs))
                self.AnimatorDrawtimes[obs] = time.time()


    def CanvasMiddleMouseDown(self, event):
        x = event.x
        y = event.y
        #self.BoardCanvas.create_oval(x,y,x,y,activewidth="1", fill="black", 
        #outline = "black")

        if self.p_x != None and self.p_y != None:
            p_x = self.p_x
            p_y = self.p_y
            l = self.BoardCanvas.create_line(p_x, p_y, x ,y, 
                    fill = "blue", width=2)
            self._tempLines.append(l)

        x = event.x
        y = HEIGHT - event.y
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))

        self.p_x = event.x
        self.p_y = event.y

    def CanvasMiddleMouseUp(self, event):
        suggestStrokes = set()
        if len(self.CurrentPointList) > 0:
            containerStroke = Stroke( self.CurrentPointList )#, smoothing=True )
            for testStroke in self.Board.Strokes:
                if strokeContainsStroke(containerStroke, testStroke):
                    suggestStrokes.add(testStroke)
            if len(suggestStrokes) > 0:
                annoNameMap = dict( [(k.__name__, k) for k in self.Board.AnnoTargeters.keys() ] )
                d = AnnotationDialog(self, annoNameMap.keys())
                self.wait_window(d.top)
                if d.data is not None:
                    self.Board.SuggestAnnotation(annoNameMap[d.data], list(suggestStrokes))


        self.CurrentPointList = []
        self.p_x = self.p_y = None
        self.Redraw()

    def CanvasRightMouseDown(self, event):
        x = event.x
        y = event.y
        #self.BoardCanvas.create_oval(x,y,x,y,activewidth="1", fill="black", outline = "black")

        if self.p_x != None and self.p_y != None:
            p_x = self.p_x
            p_y = self.p_y
            l = self.BoardCanvas.create_line(p_x, p_y, x ,y, fill = "gray", width=2)
            self._tempLines.append(l)

        x = event.x
        y = HEIGHT - event.y
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))

        self.p_x = x
        self.p_y = HEIGHT - y

    def CanvasRightMouseUp(self, event):
        removed = False
        if len(self.CurrentPointList) > 0:
            stroke = Stroke( self.CurrentPointList )#, smoothing=True )
            self.CurrentPointList = []
            for stk in list(self.StrokeList):
                if len(getStrokesIntersection(stroke, stk) ) > 0:
                    logger.debug( "Removing Stroke")
                    removed = True
                    self.Board.RemoveStroke(stk)
                    self.StrokeList.remove(stk)
        self.p_x = self.p_y = None
        self.Redraw(clear=removed)

    def CanvasMouseDown(self, event):
        "Draw a line connecting the points of a stroke as it is being drawn"

        x = event.x
        y = event.y
        #self.BoardCanvas.create_oval(x,y,x,y,activewidth="1", fill="black", outline = "black")

        if self.p_x != None and self.p_y != None:
            p_x = self.p_x
            p_y = self.p_y
            l = self.BoardCanvas.create_line(p_x, p_y, x ,y, fill = "white", width=2)
            self._tempLines.append(l)

        x = event.x
        y = HEIGHT - event.y
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))

        self.p_x = x
        self.p_y = HEIGHT - y

    def AddCurrentStroke(self):
        if len(self.CurrentPointList) > 0:
            stroke = Stroke( self.CurrentPointList )#, smoothing=True )
            self.AddStroke(stroke)
            self.CurrentPointList = []

    def AddStroke(self, stroke):
        self.StrokeQueue.put(stroke)
        #self.Board.AddStroke(stroke)
        #self.StrokeList.append(stroke)

    def CanvasMouseUp(self, event):
        "Finish the stroke and add it to the board"
        #start a new stroke
        self.AddCurrentStroke()
        self.p_x = self.p_y = None
        self.Redraw()

    """
    def SetupImageServer(self):
        self.serverThread = ServerThread(port = 30000)
        self.net_queue = self.serverThread.getResponseQueue()
        self.serverThread.start()
        self.imgProcThread = ImgProcThread(self.net_queue, self.StrokeQueue)
        self.imgProcThread.start()
    """





    def AnimateFrame(self):
        for obs, deadline in self.AnimatorDrawtimes.items():
            if deadline <= 1000 * time.time():
                obs.drawMyself()
                self.AnimatorDrawtimes[obs] = 1000 *( (1 / float(obs.fps)) + time.time() ) #Time the next frame


    def Redraw(self, clear=True):
        "Find all the strokes on the board, draw them, then iterate through every object and have it draw itself"
        global HEIGHT, WIDTH
        if clear:
            self.BoardCanvas.delete(ALL)
        else:
            for l in self._tempLines:
                self.BoardCanvas.delete(l)
        self._tempLines = []

        strokes = self.Board.Strokes
        observers = self.Board.BoardObservers
        for s in strokes:
           s.drawMyself()
        for obs in observers:
           obs.drawMyself()

        #fout = open("standalone.xml", "w")
        #bxml = self.Board.xml(WIDTH, HEIGHT)
        #print >> fout, ET.tostring(bxml)
        #fout.close()



    def do_drawCircle(self, x, y, radius=1, color="#FFFFFF", fill="", width=1.0):
         """Draw a circle on the canvas at (x,y) with radius rad. 
         Color should be 24 bit RGB string #RRGGBB.
         Empty string is transparent"""
         y = HEIGHT - y
         self.BoardCanvas.create_oval(x-radius,y-radius,x+radius,
            y+radius,width=width, fill=fill, outline = color)

    def do_drawLine(self, x1, y1, x2, y2, width=2, color="#FFFFFF"):
         """Draw a line on the canvas from (x1,y1) to (x2,y2). 
            Color should be 24 bit RGB string #RRGGBB"""
         y1 = HEIGHT - y1
         y2 = HEIGHT - y2
         self.BoardCanvas.create_line(x1, y1, x2 ,y2, fill=color, width = width)

    def do_drawText (self, x, y, InText="", size=10, color="#FFFFFF"):
        """Draw some text (InText) on the canvas at (x,y).
        Color as defined by 24 bit RGB string #RRGGBB"""
        y = HEIGHT - y
        text_font = ("times", size, "")
        self.BoardCanvas.create_text(x,y,text = InText,
            fill = color, font = text_font, anchor=NW)
    def do_drawCurve(self, curve, width = 2, color = "#FFFFFF"):
        "Draw a curve on the board with width and color as specified"
        self.drawStroke(curve.toStroke(), width = width, color = color)
        colorwheel = ["#FF0000", "#00FF00", "#0000FF", "#FF00FF"]
        for i, pt in enumerate(curve.getControlPoints()):
            color = colorwheel[i]
            self.drawCircle(pt.X, pt.Y, radius=4-i, 
                 width = width, color = color)
	"""
        for pt in curve.getControlPoints():
            self.drawCircle(pt.X, pt.Y, radius=2, width = width, color = "#0000FF")
	"""

    def do_drawStroke(self, stroke, width= 1, color = "#FFFFFF", erasable= True):
        if len(stroke.Points) >= 2:
            px, py = stroke.Points[0].X, stroke.Points[0].Y
            for pt in stroke.Points[1:]:
                x,y = pt.X, pt.Y
                self.do_drawLine(px, py, x, y, width=width, color=color)
                px, py = x,y
        #_SketchGUI.drawStroke(self, stroke, width = width, color = color, erasable = erasable)


    def drawText(self, *args, **kargs):
        op = partial(TkSketchFrame.do_drawText, self, *args, **kargs)
        self.OpQueue.put(op)
    def drawLine(self, *args, **kargs):
        op = partial(TkSketchFrame.do_drawLine, self, *args, **kargs)
        self.OpQueue.put(op)
    def drawCircle(self, *args, **kargs):
        op = partial(TkSketchFrame.do_drawCircle, self, *args, **kargs)
        self.OpQueue.put(op)
    def drawStroke(self, *args, **kargs):
        op = partial(TkSketchFrame.do_drawStroke, self, *args, **kargs)
        self.OpQueue.put(op)
Ejemplo n.º 11
0
        self.OpQueue.put(op)
    def drawStroke(self, *args, **kargs):
        op = partial(TkSketchFrame.do_drawStroke, self, *args, **kargs)
        self.OpQueue.put(op)




if __name__ == "__main__":
    if len(sys.argv) > 1:
        #Do something with the CLI arguments
        fname = sys.argv[1]
        board = Board()
        _initializeBoard(board)

        stkLoader = StrokeStorage(fname+".dat")
        stkDict = ImageStrokeConverter.imageToStrokes(fname)
        stks = stkDict['strokes']
        WIDTH, HEIGHT = stkDict['dims']
        strokeList = []
        for s in stks:
            pointList = []
            for x,y in s.points:
               newPoint = Point(x, HEIGHT - y)
               pointList.append(newPoint)
            strokeList.append(Stroke(pointList))
            board.AddStroke(Stroke(pointList))

        stkLoader.saveStrokes(strokeList)
        #fout = open("standalone.xml", "w")
        #print >> fout, ET.tostring(board.xml(WIDTH, HEIGHT))
Ejemplo n.º 12
0
class TkSketchFrame(Frame, _SketchGUI):
    """The base GUI class. 
    Class must implement drawText, drawLine and drawCircle. X-Y origin is bottom-left corner.
    Aside from these restrictions, interface options (reset board, etc) are up to the GUI programmer."""
    def __init__(self):
        "Set up the Tkinter GUI stuff as well as the board logic"
        global HEIGHT, WIDTH

        #Set up all the logical board stuff before displaying anything
        self.StrokeQueue = Queue.Queue()
        self.Board = None
        self.CurrentPointList = []
        self.StrokeList = []
        self.AnimatorDrawtimes = {} #A dictionary of Animator subclasses to the deadline for the next frame draw 
        self.StrokeLoader = StrokeStorage()
        #self.SetupImageServer()
        self.ResetBoard()

        self.root = Tk()
        self.root.title("Sketchy/Scratch")
        Frame.__init__(self, self.root)
        self.pack()
        #Set up the GUI stuff

        self.drawMenuOptions = {}
        
        self.BoardCanvas= Canvas(self, width=WIDTH, height = HEIGHT, bg="white", bd=2)
        self.BoardCanvas.pack(side=BOTTOM)
        #Left click bindings
        self.BoardCanvas.bind("<ButtonPress-1>", self.CanvasMouseDown)
        self.BoardCanvas.bind("<B1-Motion>", self.CanvasMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-1>", self.CanvasMouseUp)      

        #Right click bindings
        self.BoardCanvas.bind("<ButtonPress-3>", self.CanvasRightMouseDown)
        self.BoardCanvas.bind("<B3-Motion>", self.CanvasRightMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-3>", self.CanvasRightMouseUp)      

        #Middle click bindings
        self.BoardCanvas.bind("<ButtonPress-2>", self.CanvasMiddleMouseDown)
        self.BoardCanvas.bind("<B2-Motion>", self.CanvasMiddleMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-2>", self.CanvasMiddleMouseUp)      
        self.MakeMenu()
        self.Redraw()

        self.run()

    def run(self):
       try:
           while 1:
               self.root.update()
               self.AnimateFrame()
               self.AddQueuedStroke()
               self.root.update_idletasks()
       except TclError:
           pass

    def initBoardObservers( observers, debugAnnotations = None ):
        if observers is not None:
            for obs in observers:
                obs(self.Board)
      
    def MakeMenu(self):
        "Reserve places in the menu for fun actions!"
        win = self.master 
        top_menu = Menu(win)
        win.config(menu=top_menu)
        
        self.object_menu = Menu(top_menu)
        top_menu.bind("<ButtonPress-1>",(lambda e: self.RebuildObjectMenu()))
        self.RebuildObjectMenu()
        top_menu.add_cascade(label="ObjectMenu", menu=self.object_menu)

        top_menu.add_command(label="Reset Board", command = (lambda :self.ResetBoard() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Load strokes.dat", command = (lambda : self.LoadStrokes() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Save strokes.dat", command = (lambda : self.SaveStrokes()), underline=1 )
        top_menu.add_command(label="Undo Stroke", command = (lambda :self.RemoveLatestStroke() or self.Redraw()), underline=1 )
        #top_menu.add_command(label="Strokes From Image", command = (lambda :self.LoadStrokesFromImage() or self.Redraw()), underline=1 )

        # code that loads the data manager
        self.dataManager_menu = Menu(top_menu)
        top_menu.add_cascade(label="Data Manager", menu=self.dataManager_menu)
        self.dataManager_menu.add_command(label="Load Data Manager", command = (lambda :self.loadDataManaer() or self.Redraw()), underline=1 )
        self.dataManager_menu.add_command(label="Next Participant", command = (lambda :self.nextParticipant() or self.Redraw()), underline=1 )
        self.dataManager_menu.add_command(label="Prev Participant", command = (lambda :self.prevParticipant() or self.Redraw()), underline=1 )
        self.dataManager_menu.add_command(label="Next Diagram", command = (lambda :self.nextDiagram() or self.Redraw()), underline=1 )
        self.dataManager_menu.add_command(label="Prev Diagram", command = (lambda :self.prevDiagram() or self.Redraw()), underline=1 )

    def nextParticipant(self):
        if self.participant + 1 >= len(self.dataset.participants):
            self.participant = 0
        else:
            self.participant += 1
        self.displayDataManager()

    def prevParticipant(self):
        if self.participant - 1 <= -1:
            self.participant = len(self.dataset.participants) - 1
        else:
            self.participant -= 1
        self.displayDataManager()

    def nextDiagram(self):
        p = self.participant
        if self.diagram + 1 >= len(self.dataset.participants[p].diagrams):
            self.diagram = 0
        else:
            self.diagram += 1
        self.displayDataManager()

    def prevDiagram(self):
        p = self.participant
        if self.diagram - 1 <= -1:
            self.diagram = len(self.dataset.participants[p].diagrams) - 1
        else:
            self.diagram -= 1
        self.displayDataManager()
        
    def loadDataManaer(self):

        # the string is the xml file you want to load.
        fname = askopenfilename(initialdir='./')
        if fname == "":
           return

        elif fname.strip().split(".")[-1] == "p":
            try:
                self.dataset = pickle.load(open(fname, "rb"))
            except Exception as e:
               print traceback.format_exc()
               logger.debug( "Error loading data from file '%s':\n %s" % (fname, e))
               return
        else:
            try:
                self.dataset = DataManager.loadDataset(fname)
            except Exception as e:
               print traceback.format_exc()
               logger.debug( "Error loading data from file '%s':\n %s" % (fname, e))
               return
        self.participant = 0
        self.diagram = 0
        self.displayDataManager()
        logger.debug( "Loaded data from '%s'" % (fname))

    def displayDataManager(self):
        """Paint whatever the display manager wants on the board"""
        global HEIGHT, WIDTH, BOARDSCALE
        self.ResetBoard()
        print self.dataset.participants[self.participant].diagrams[self.diagram].type

        xMax = 0
        yMax = 0
        xMin = sys.maxint
        yMin = sys.maxint

        par = self.participant
        dig = self.diagram


        # Finds the min and max points so we can scale the data to fit on the screen
        for stkNum, inkStroke in self.dataset.participants[par].diagrams[dig].InkStrokes.items():
            stroke = traceStroke(inkStroke.stroke)
            ul,br = GeomUtils.strokelistBoundingBox([stroke])
            xMax = max(ul.X, br.X, xMax)
            yMax = max(ul.Y, br.Y, yMax)
            xMin = min(ul.X, br.X, xMin)
            yMin = min(ul.Y, br.Y, yMin)

        # Find the distance that the strokes take up
        # the "+ 20" is so we can have a 10 pixle buffer around the edges

        setBoardScale(xMax, yMax)


        labelStrokeMap = {} #Maps groupLabel : set(strokes)
        for stkNum, inkStroke in self.dataset.participants[par].diagrams[dig].InkStrokes.items():
            #print inkStroke.id
            stroke = inkStroke.stroke
            for groupLabel in self.dataset.participants[par].diagrams[dig].groupLabels:
                if stroke.id in groupLabel.ids:
                    labelStrokeMap.setdefault(groupLabel, set()).add(stroke)
            points = []
            
            """
            # scale each point to it's new position
            for p in stroke.Points:
                x = (p.X - xMin) * scaleFactor + 10 # the "+10 is for the 10 pixle boarder
                # y axis points in the data manager are inverted compaired to our screen
                # so we invert them
                y = HEIGHT - ((p.Y - yMin) * scaleFactor + 10)
                points.append(Point(x,y))
            """

            # create a new stroke out of the scaled points and add it to the board.
            #s = Stroke(points)
            s = inkStroke.stroke
            self.Board.AddStroke(s)
            self.StrokeList.append(s)
            # Annotate the stroke with the type given in the data manager

        for groupLabel, strokeSet in labelStrokeMap.items():
            self.Board.AnnotateStrokes(list(strokeSet), 
                                       DataManager.DataManagerAnnotation(groupLabel.type))



    def AddQueuedStroke(self):
        #Only process one stroke per round
        if not self.StrokeQueue.empty():
            stk = self.StrokeQueue.get()
            logger.debug("Adding queued stroke %s" % (stk))
            self.Board.AddStroke(stk)
            self.StrokeList.append(stk)
            self.Redraw()
            self.StrokeQueue.task_done()

    def LoadStrokes(self):
      maxX = maxY = 0
      for stroke in self.StrokeLoader.loadStrokes():
         maxX = max(stroke.BoundBottomRight.X, maxX)
         maxY = max(stroke.BoundTopLeft.Y, maxY)
         self.Board.AddStroke(stroke)
         self.StrokeList.append(stroke)
      setBoardScale(maxX, maxY)

    def SaveStrokes(self):
      self.StrokeLoader.saveStrokes(self.StrokeList)
        
    """
    def LoadStrokesFromImage(self):
        fname = askopenfilename(initialdir='/home/jbrowne/src/sketchvision/images/')
        if fname == "":
           return

        try:
           logger.debug( "Loading strokes...")
           strokes = imageToStrokes(fname)
        except Exception as e:
           logger.debug( "Error importing strokes from image '%s':\n %s" % (fname, e))
           return
        logger.debug( "Loaded %s strokes from '%s'" % (len(strokes), fname))

        for s in strokes:
           newStroke = Stroke()
           for x,y in s.points:
              scale = WIDTH / float(1280)
              newPoint = Point(scale * x,HEIGHT - scale * y)
              newStroke.addPoint(newPoint)
           self.Board.AddStroke(newStroke)
           self.StrokeList.append(newStroke)
    """

    def RemoveLatestStroke(self):
        if len (self.StrokeList) > 0:
            stroke = self.StrokeList.pop()
            self.Board.RemoveStroke(stroke)

    def RebuildObjectMenu(self):
        "Search the board for existing objects, and add a menu entry to manipulate it (drawAll)"
        observers = self.Board.GetBoardObservers()
        draw_vars = {}
        for obs in observers:
            key = obs.__class__
            if key not in self.drawMenuOptions and hasattr(obs, "DrawAll"):
                draw_vars[key] = key.DrawAll
        
        for key, var in draw_vars.items():
            self.drawMenuOptions[key] = self.object_menu.add_command(label=key.__name__,command=(lambda class_=key: self.InvertDraw(class_)), underline = 0)

    def InvertDraw(self, class_):
        "Essentially checkbox behavior for BoardObserver.DrawAll variable"
        if hasattr(class_, "DrawAll"):
            class_.DrawAll = not class_.DrawAll
            self.Redraw()


    def InitializeBoard(self):
        """Initialize all of the board observers and register debugable annotations, etc."""
        _initializeBoard(self.Board)
            

    def ResetBoard(self):
        "Clear all strokes and board observers from the board (logically and visually)"
        self.p_x = self.p_y = None

        self.Board = Board(gui = self)
        self.InitializeBoard()
        self.RegisterAnimators()
        self.CurrentPointList = []
        self.StrokeList = []


    def RegisterAnimators(self):
        self.AnimatorDrawtimes = {}
        for obs in self.Board.BoardObservers:
            if Animator in type(obs).__mro__: #Check if it inherits from Animator
                logger.debug( "Registering %s as animator" % (obs))
                self.AnimatorDrawtimes[obs] = time.time()
                
                
    def CanvasMiddleMouseDown(self, event):
        x, y = _p2b(event.x, event.y)
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))
        
        if self.p_x != None and self.p_y != None:
            px = self.p_x
            py = self.p_y
            self.drawLine(px, py, x, y, width=2, color="blue")
        self.p_x = x
        self.p_y = y



    def CanvasMiddleMouseUp(self, event):
        suggestStrokes = set()
        if len(self.CurrentPointList) > 0:
            containerStroke = Stroke( self.CurrentPointList )#, smoothing=True )
            for testStroke in self.Board.Strokes:
                if strokeContainsStroke(containerStroke, testStroke):
                    suggestStrokes.add(testStroke)
            if len(suggestStrokes) > 0:
                annoNameMap = dict( [(k.__name__, k) for k in self.Board.AnnoTargeters.keys() ] )
                d = AnnotationDialog(self, annoNameMap.keys())
                self.wait_window(d.top)
                if d.data is not None:
                    self.Board.SuggestAnnotation(annoNameMap[d.data], list(suggestStrokes))


        self.CurrentPointList = []
        self.p_x = self.p_y = None
        self.Redraw()

    def CanvasRightMouseDown(self, event):
        
        x, y = _p2b(event.x, event.y)
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))
        if self.p_x != None and self.p_y != None:
            px = self.p_x
            py = self.p_y
            self.drawLine(px, py, x, y, width=2, color="gray")

        self.p_x = x
        self.p_y = y



    def CanvasRightMouseUp(self, event):
        delStrokes = set([])
        if len(self.CurrentPointList) > 0:
            stroke = Stroke( self.CurrentPointList )#, smoothing=True )
            self.CurrentPointList = []
            for stk in list(self.StrokeList):
                if len(getStrokesIntersection(stroke, stk) ) > 0:
                    logger.debug( "Removing Stroke")
                    self.Board.RemoveStroke(stk)
                    self.StrokeList.remove(stk)
        self.p_x = self.p_y = None
        self.Redraw()

    def CanvasMouseDown(self, event):
        "Draw a line connecting the points of a stroke as it is being drawn"
        
        
        x, y = _p2b(event.x, event.y)
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))
        if self.p_x != None and self.p_y != None:
            px = self.p_x
            py = self.p_y
            self.drawLine(px, py, x, y, width=2, color="#000000")

        self.p_x = x
        self.p_y = y


    def AddCurrentStroke(self):
        if len(self.CurrentPointList) > 0:
            stroke = Stroke( self.CurrentPointList )#, smoothing=True )
            
            self.Board.AddStroke(stroke)
            self.StrokeList.append(stroke)
            self.CurrentPointList = []
            
        
    def CanvasMouseUp(self, event):
        "Finish the stroke and add it to the board"
        #start a new stroke
        self.AddCurrentStroke()
        self.p_x = self.p_y = None
        self.Redraw()

                
        

        
    def AnimateFrame(self):
        for obs, deadline in self.AnimatorDrawtimes.items():
            if deadline <= 1000 * time.time():
                obs.drawMyself()
                self.AnimatorDrawtimes[obs] = 1000 *( (1 / float(obs.fps)) + time.time() ) #Time the next frame

        
    def Redraw(self):
        "Find all the strokes on the board, draw them, then iterate through every object and have it draw itself"
        global HEIGHT, WIDTH
        self.BoardCanvas.delete(ALL)
        strokes = self.Board.Strokes
        observers = self.Board.BoardObservers
        for s in strokes:
           s.drawMyself()
        for obs in observers:
           obs.drawMyself()

    def drawCircle(self, x, y, radius=1, color="#000000", fill="", width=1.0):
         "Draw a circle on the canvas at (x,y) with radius rad. Color should be 24 bit RGB string #RRGGBB. Empty string is transparent"
         x, y = _b2p(x,y)
         self.BoardCanvas.create_oval(x-radius,y-radius,x+radius,y+radius,width=width, fill=fill, outline = "#BBBBBB")
         self.BoardCanvas.create_oval(x-radius,y-radius,x+radius,y+radius,width=width-1, fill=fill, outline = color)
         
    def drawLine(self, x1, y1, x2, y2, width=2, color="#000000"):
         "Draw a line on the canvas from (x1,y1) to (x2,y2). Color should be 24 bit RGB string #RRGGBB"
         x1, y1 = _b2p(x1,y1)
         x2, y2 = _b2p(x2, y2)
         self.BoardCanvas.create_line(x1, y1, x2 ,y2, fill="#BBBBBB", width = width+1)
         self.BoardCanvas.create_line(x1, y1, x2 ,y2, fill=color, width = width)

    def drawStroke(self, stroke, width = 2, color="#000000", erasable = False):
        if len(stroke.Points) > 0:
            prevPt = stroke.Points[0]
            px, py = _b2p(prevPt.X, prevPt.Y)
            for pt in stroke.Points[1:]:
                x, y = _b2p(pt.X, pt.Y)
                self.BoardCanvas.create_line(px, py, x ,y, fill="#BBBBBB", width = width) #Fake anti-aliasing
                self.BoardCanvas.create_line(px, py, x ,y, fill=color, width = width - 1)
                (px, py) = (x,y)

    def drawText (self, x, y, InText="", size=10, color="#000000"):
        "Draw some text (InText) on the canvas at (x,y). Color as defined by 24 bit RGB string #RRGGBB"
        x, y = _b2p(x,y)
        text_font = ("times", size, "")
        self.BoardCanvas.create_text(x,y,text = InText, fill = color, font = text_font, anchor=NW) 
Ejemplo n.º 13
0
class GTKGui (_SketchGUI, gtk.DrawingArea):
    def __init__(self, dims = (WIDTH, HEIGHT) ):
        # Create a new window
        gtk.DrawingArea.__init__(self)
        self.resize(*dims)# BREAKS when X forwarding

        #Semantic board data
        self.board = None # set by resetBoard()
        self.boardProcThread = BoardThread(self.board) # set by resetBoard()

        #GUI data variables
        self.shouldDrawStrokes = True
        self.currentPoints = None # set by resetBoard()
        self.strokeList = None # set by resetBoard()
        self.opQueue = None # set by resetBoard()

        self.isMouseDown1 = False
        self.isMouseDown3 = False
        self.keyCallbacks = {}
        self.strokeQueue = ProcQueue()
        self.strokeLoader = StrokeStorage()
        self.screenImage = None
        self._pixbuf = None
        self._isFullscreen = False
        #Cairo drawing data
        self.renderBuffer = None
        self.context = None 



        #Event hooks
        gobject.idle_add(self.processOps) #Idle event
        gobject.idle_add(self.processQueuedStrokes) #Async stroke processing
        self.set_property("can-focus", True) #So we can capture keyboard events
        self.connect("button_press_event", self.onMouseDown)
        self.connect("motion_notify_event", self.onMouseMove)
        self.connect("button_release_event", self.onMouseUp)
        self.connect("key_press_event", self.onKeyPress)
        self.connect("expose_event", self.onExpose)
        self.set_events(gtk.gdk.BUTTON_RELEASE_MASK | 
                        gtk.gdk.BUTTON_PRESS_MASK |
                        gtk.gdk.KEY_PRESS_MASK |
                        gtk.gdk.EXPOSURE_MASK |
                        gtk.gdk.POINTER_MOTION_MASK )

        #Enable the board processing
        self.resetBoard()


    def getDimensions(self):
        "Return the (width, height) of the visible board area"
        return self.window.get_size()

    def post(self, op):
        self.opQueue.put(op)

    def registerKeyCallback(self, keyVal, function):
        """Register a function to be called when
        a certain keyVal is pressed"""
        log.debug("Registered function for %s" % (keyVal))
        self.keyCallbacks.setdefault(keyVal, []).append(function)

    def onKeyPress(self, widget, event, data=None):
        key = chr(event.keyval % 256)
        #Run the registered callbacks
        for func in self.keyCallbacks.get(key, ()):
            func()
        
        #Run the hard-coded key events
        if key == 'r':
            self.resetBoard()
        elif key == 'i':
            def ok_callback(fileSelector):
                fname = fileSelector.get_filename()
                fileSelector.destroy()
                self.loadStrokesFromImage(filename=fname)

            fileSelector = gtk.FileSelection("Choose a photo")
            fileSelector.set_filename("./photos/")

            fileSelector.ok_button.connect("clicked", 
                                      (lambda w: ok_callback(fileSelector)) )
            fileSelector.cancel_button.connect("clicked", 
                                      (lambda w: fileSelector.destroy()) )
                                      
            fileSelector.show()
        elif key == 'l':
            self.loadStrokes()
        elif key == 's':
            self.saveStrokes()
        elif key == 'f':
            self.setFullscreen(not self._isFullscreen)
        elif key == 'q':
            gtk.main_quit()
        elif key in ('1','0','-','`',' '):
            self.controlTuringMachines(key)
            self.boardChanged()
        elif key == 'D':
            self.shouldDrawStrokes = not self.shouldDrawStrokes
            if self.shouldDrawStrokes:
                print ("Show Strokes")
            else:
                print("Don't Show Strokes")
            self.draw()


    def controlTuringMachines(self, key):
        """Keyboard bindings specifically hard-coded for controlling 
        any present Turing Machine annotations"""
        tmAnnos = self.board.FindAnnotations(anno_type = TuringMachineAnnotation)
        if key in ['1', '0', '-']:
            for anno in tmAnnos:
                tapeString = anno.getTapeString()
                tapeString+= key
                anno.setTapeString(tapeString)
        elif key == '`':
            fp = open("TuringMachines.dot", "a")
            for anno in tmAnnos:
                print >> fp, anno.dotify()
                anno.setTapeString("")
                anno.restartSimulation()
            fp.close()
        elif key == ' ':
            for anno in tmAnnos:
                anno.simulateStep()
            
    def resetBackBuffer(self):
        """Reset the back painting buffer, for example when the screen
        size changes"""
        x,y,w,h = self.allocation
        log.debug("Reset back buffer %sx%s" % (w,h))
        self.renderBuffer = cairo.ImageSurface(cairo.FORMAT_ARGB32, w,h)
        self.context = cairo.Context(self.renderBuffer)
        #self.screenImage = None

        
    def setFullscreen(self, makeFull):
        """Set the application fullscreen according to makeFull(boolean)"""
        windows = gtk.window_list_toplevels()
#        for (i, win) in enumerate(windows):
#            print i, win.name, "Has focus: %s" % (win.has_toplevel_focus())
#        win = windows[0]
        self._pixbuf = None
        if makeFull:
            self.screenImage = None
            self._isFullscreen = True
            log.debug("Fullscreen")
            for win in windows:
                if self in win.children():
                    win.fullscreen()
            self.opQueue.put(lambda : time.sleep(0.1)) # So we don't reset too early
            self.opQueue.put(lambda : self.resetBackBuffer())
        else:
            self.screenImage = None
            self._isFullscreen = False
            log.debug("UNFullscreen")
            for win in windows:
                if self in win.children():
                    win.unfullscreen()
            self.opQueue.put(lambda : time.sleep(0.1))
            self.opQueue.put(lambda : self.resetBackBuffer())

    def resetBoard(self):
        log.debug("Resetting Board")
        self.opQueue = Queue.Queue()
        self.board = Board(gui = self)
        self.strokeList = []
        self.currentPoints = []
        Config.initializeBoard(self.board)
        self.boardProcThread.stop()
        self.boardProcThread = BoardThread(self.board)
        self.boardProcThread.start()
        self.opQueue.put(lambda : time.sleep(0.1)) # So we don't reset too early
        self.opQueue.put(lambda : self.resetBackBuffer())
        self.opQueue.put(lambda : self.boardChanged())

    def boardChanged(self):
        if self.strokeQueue.empty(): #No strokes waiting to be processed
            if self.board.Lock.acquire(False): #Nobody's using the board
                self.board.Lock.release()
                self.draw()
               
    def loadStrokesFromImage(self, filename=None, image=None):
        width, height = self.window.get_size()
        if image is None:
            if filename is None:
                return
            else:
                image = ImageStrokeConverter.loadFile(filename)

        p = Process(target = processImage,
                    args = (image, self.strokeQueue, (width,height) )
                   )
        p.start()
 
    def loadStrokes(self):
        for stroke in self.strokeLoader.loadStrokes():
            self.addStroke(stroke)

    def saveStrokes(self):
        self.strokeLoader.saveStrokes(self.strokeList)
        with self.board.Lock:
            print ElementTree.tostring(self.board.xml(1280, 720))


    def drawCircle(self, *args, **kargs):
        """Draw a circle on the canvas at (x,y) with radius rad. Color should be
        24 bit RGB string #RRGGBB. Empty string is transparent"""
        op = partial(GTKGui._drawCircle, self, *args, **kargs)
        self.opQueue.put(op)

    def drawText(self, *args, **kargs):
        """Draw some text (InText) on the canvas at (x,y). Color as defined by 24
        bit RGB string #RRGGBB"""
        op = partial(GTKGui._drawText, self, *args, **kargs)
        self.opQueue.put(op)

    def drawLine(self, x1, y1, x2, y2, width=2, color="#FFFFFF"):
        """Draw a line on the canvas from (x1,y1) to (x2,y2). Color should be 24
        bit RGB string #RRGGBB"""
        op = partial(GTKGui._drawLine, self, x1, y1, x2, y2, width, color)
        self.opQueue.put(op)

    def drawStroke(self, *args, **kargs):
        """Draw a stroke on the board with width and color as specified."""
        op = partial(GTKGui._drawStroke, self, *args, **kargs)
        self.opQueue.put(op)

    def drawCurve(self, *args, **kargs):
        "Draw a curve on the board with width and color as specified"
        op = partial(GTKGui._drawCurve, self, *args, **kargs)
        self.opQueue.put(op)

    def drawBox(self, *args, **kargs):
        op = partial(GTKGui._drawBox, self, *args, **kargs)
        self.opQueue.put(op)
        
    def drawBitmap(self, x, y, filename=None, pixbuf=None):
        try:
            if pixbuf is None and filename is not None:
                pixbuf = gtk.gdk.pixbuf_new_from_file(filename)
            elif pixbuf is None:
                raise Exception("Must specify filename or pixbuf")
            else:
                op = partial(GTKGui._drawBitmap, self, x, y, pixbuf)
                self.opQueue.put(op)
        except Exception as e:
            log.warn(str(e))

    #________________________________________
    #  Private, actual draw calls
    #________________________________________

    def _drawCircle(self, x, y, radius=1, color="#FFFFFF", fill="", width=1.0):
        """Draw a circle on the canvas at (x,y) with radius rad. Color should be
        24 bit RGB string #RRGGBB. Empty string is transparent"""
        self.context.save()
        #Draw the line
        pt = self.b2c(Point(x,y))
        self.context.arc(pt.X, pt.Y, radius, 0, math.pi * 2)
        if fill != "":
            self.context.set_source_rgb(* hexToTuple(fill) )
            self.context.fill_preserve()
        #c = hexToTuple(color)
        self.context.set_source_rgb(*hexToTuple(color))
        self.context.stroke()
        self.context.restore()
         
    def _drawLine(self, x1, y1, x2, y2, width, color, _context=None):
        """Draw a line on the canvas from (x1,y1) to (x2,y2). Color should be 24
        bit RGB string #RRGGBB"""
        if _context is None:
            context = self.context
        else:
            context = _context

        context.save()
        #Draw the line
        c = hexToTuple(color)
        context.set_source_rgb(*c)
        p1 = self.b2c(Point(x1, y1))
        p2 = self.b2c(Point(x2, y2))
        context.move_to( p1.X, p1.Y )
        context.line_to( p2.X, p2.Y )
        context.stroke()
        context.restore()
         
    def _drawText (self, x, y, InText="", size=10, color="#FFFFFF"):
        """Draw some text (InText) on the canvas at (x,y). Color as defined by 24
        bit RGB string #RRGGBB"""
        self.context.save()
        ctxt = pangocairo.CairoContext(self.context)
        layout = ctxt.create_layout()
        layout.set_text(InText)
        c = hexToTuple(color)
        self.context.set_source_rgb(*c)
        _, (tlx, tly, brx, bry) = layout.get_pixel_extents()
        pt = Point(x, y)
        #pt = self.b2c(Point(x, y - bry))
        pt = self.b2c(pt)
        ctxt.translate(pt.X, pt.Y)
        ctxt.show_layout(layout)
        self.context.restore()

    # ------------------------------------------------
    #      Optional overloads
    def _drawBox(self, tl, br, topright = None, 
                bottomleft = None, color="#FFFFFF", fill = "", width=2):
        self.context.save()
        tl = self.b2c(tl)
        br = self.b2c(br)

        x = tl.X
        y = tl.Y
        w = br.X - tl.X
        h = br.Y - tl.Y

        self.context.set_source_rgb(* hexToTuple(color))
        self.context.set_line_width(width)
        self.context.rectangle(x,y,w,h)
        if fill != "":
            self.context.set_source_rgb(* hexToTuple(fill))
            self.context.fill_preserve()
        self.context.stroke()
        self.context.restore()
    
    def _drawStroke(self, stroke, width = 2, color="#FFFFFF", erasable = False):
        """Draw a stroke on the board with width and color as specified."""
        self.context.save()
        #Draw the lines
        c = hexToTuple(color)
        self.context.set_source_rgb(*c)
        if len(stroke.Points) > 0:
            pt = self.b2c(stroke.Points[0])
            self.context.move_to( pt.X, pt.Y )
            if len(stroke.Points) > 1:
                for pt in stroke.Points[1:]:
                    pt = self.b2c(pt)
                    self.context.line_to( pt.X, pt.Y)
            else:
                pass
        self.context.stroke()
        self.context.restore()
    
    def _drawBitmap(self, x, y, pixbuf):
        """Draw a bitmap on the board"""
        self.context.save()
        pt = self.b2c(Point(x,y))
        ctxt2 = gtk.gdk.CairoContext(self.context)
        ctxt2.set_source_pixbuf(pixbuf, pt.X, pt.Y)
        ctxt2.paint()
        self.context.restore()        
        
    def _getContext(self):
        return self.window.cairo_create()

    def resize(self, w,h):
        """Set the size of the canvas to w x h"""
        self.set_size_request(w,h)

    def processQueuedStrokes(self):
        if not self.strokeQueue.empty():
            log.debug("Adding queued stroke")
            stroke = self.strokeQueue.get()
            self.strokeList.append(stroke)
            self.boardProcThread.addStroke(stroke, callback = self.boardChanged)
        return True

    def processOps(self):
        """Process one operation from the opqueue"""
        if not self.opQueue.empty():
            op = self.opQueue.get() #Call the next operation in the queue
            op()
            self.opQueue.task_done()
        return True #Call me again next idle time

    def onMouseDown(self, widget, e):
        """Respond to a mouse being pressed"""
        if e.button == 1:
            self.isMouseDown1 = True
            self.currentPoints.append(self.b2c(Point(e.x, e.y)))
            return True
        elif e.button == 3:
            self.isMouseDown3 = True
            self.currentPoints.append(self.b2c(Point(e.x, e.y)))
            return True

    def onMouseMove(self, widget, e):
        """Respond to the mouse moving"""
        if self.isMouseDown1:
            p = self.currentPoints[-1]
            curPt = self.b2c(Point(e.x, e.y))
            self.currentPoints.append(curPt)
            liveContext = self._getContext()
            self._drawLine(p.X, p.Y, curPt.X, curPt.Y, 
                            2, "#ffffff", _context= liveContext)
            return True
        elif self.isMouseDown3:
            p = self.currentPoints[-1]
            curPt = self.b2c(Point(e.x, e.y))
            self.currentPoints.append(curPt)
            liveContext = self._getContext()
            self._drawLine(p.X, p.Y, curPt.X, curPt.Y, 
                            2, "#c00c0c", _context = liveContext)
            return True
    
    def onMouseUp(self, widget, e):
        """Respond to the mouse being released"""
        if e.button == 1:
            self.isMouseDown1 = False
            curPt = self.b2c(Point(e.x, e.y))
            self.currentPoints.append(curPt)
            stroke = Stroke( self.currentPoints)
            self.currentPoints = []
            self.addStroke(stroke)
            #self.opQueue.put(partial(GTKGui.addStroke, self, stroke))
            #self.draw()
            return True
        elif e.button == 3:
            self.isMouseDown3 = False
            curPt = self.b2c(Point(e.x, e.y))
            self.currentPoints.append(curPt)
            stroke = Stroke( self.currentPoints)
            self.currentPoints = []
            shouldRedraw = True
            for stk in list(self.strokeList):
                if len(getStrokesIntersection(stroke, stk) ) > 0:
                    self.eraseStroke(stk)
                    shouldRedraw = False
            if shouldRedraw:
                #pass
                self.draw()
            return True

    def addStroke(self, stroke):
        """Add as stroke to the board and our local bookkeeping"""
        self.strokeQueue.put(stroke)
#        self.boardProcThread.addStroke(stroke, callback = self.boardChanged)

    def eraseStroke(self, stroke):
        """Remove a stroke from the board and our local lists"""
        self.strokeList.remove(stroke)
        self.boardProcThread.removeStroke(stroke, callback = self.boardChanged)
        

    def onExpose(self, widget, e):
        """Respond to the window being uncovered"""
        log.debug("Expose")
        if self.screenImage is not None:
            self.window.draw_pixbuf(None, self.screenImage, 0,0, 0,0)
        else:
            self.draw()

    def clearBoard(self, bgColor="#000000"):
        """Erase the contents of the board"""
        log.debug("Clear")
        self.context.save()
        c = hexToTuple(bgColor)
        self.context.set_source_rgb(*c)
        rect = self.get_allocation()
        self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
        self.context.fill()
        self.context.restore()
        
    def draw(self):
        """Draw the board"""
        log.debug("Redraw")
        self.opQueue.put(partial(GTKGui.clearBoard, self))
        with self.board.Lock:
            if self.shouldDrawStrokes:
                for stk in self.board.Strokes:
                    stk.drawMyself()
            for obs in self.board.BoardObservers:
                obs.drawMyself()
        self.drawStroke(Stroke(self.currentPoints))
        self.doPaint()

    def doPaint(self):
        """A method to commit the current queue of draw events to 
        the screen"""
        self.opQueue.put(partial( GTKGui.flipContext, self) )
        self.opQueue.put(partial( GTKGui._updateScreenImage, self) )
        
    def flipContext(self):
        """Render the drawn surface to the screen"""
        log.debug("Flip image to surface")
        bufferToPaint = self.renderBuffer
        (nw, nh) = bufferToPaint.get_width(), bufferToPaint.get_height()
        log.debug(" Current buffer: %sx%s" % (nw, nh) )
        #Set up a new buffer to paint from
        self.resetBackBuffer()
        #Paint the render buffer to the live context
        liveContext = self._getContext()
        liveContext.set_source_surface(bufferToPaint)
        liveContext.paint()

    def getScreenShot(self):
        width, height = self.window.get_size()

        #if self._pixbuf is None:
        log.debug("  pixbuf size: %sx%s" % (width, height))
        _pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8,
                                width, height)

        #BREAKING
        print os.getpid()
        screenImage = _pixbuf.get_from_drawable(self.window, 
                                        self.window.get_colormap(), 
                                        0, 0, 0, 0, width, height)
     
        retImage = cv.CreateImageHeader( (screenImage.get_width(), screenImage.get_height()), cv.IPL_DEPTH_8U, 3)
        cv.SetData(retImage, screenImage.get_pixels())
        retImage = cv.GetMat(retImage)

        return retImage

    def _updateScreenImage(self):
        """Update the image of the screen we're dealing with"""
        width, height = self.window.get_size()        
        log.debug("Update Screenshot")
        _pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8,
                                width, height)
        self.screenImage = _pixbuf.get_from_drawable(self.window, 
                                        self.window.get_colormap(), 
                                        0, 0, 0, 0, width, height)        
        self.screenImage.save('screenshot.png', 'png')
        
#        saveimg(self.getScreenShot())

        
    def b2c(self, pt):
        """Converts bottom-left origin Board coordinates to raw canvas coords
        and back"""
        rect = self.get_allocation()
        return Point(pt.X, rect.height - pt.Y)
Ejemplo n.º 14
0
class TkSketchFrame(Frame, _SketchGUI):
    """The base GUI class. 
    Class must implement drawText, drawLine and drawCircle. X-Y origin is bottom-left corner.
    Aside from these restrictions, interface options (reset board, etc) are up to the GUI programmer."""
    def __init__(self):
        "Set up the Tkinter GUI stuff as well as the board logic"
        global HEIGHT, WIDTH


        self.root = Tk()
        self.root.title("Sketchy/Scratch")
        Frame.__init__(self, self.root)
        self.pack()
        #Set up the GUI stuff

        self.drawMenuOptions = {}
        
        self.BoardCanvas= Canvas(self, width=WIDTH, height = HEIGHT, bg="white", bd=2)
        self.BoardCanvas.pack(side=BOTTOM)
        self.ClassNameEntry = Entry(self)
        self.ClassNameEntry['width'] = 30
        self.ClassNameEntry.pack(side=RIGHT)

        #Left click bindings
        self.BoardCanvas.bind("<ButtonPress-1>", self.CanvasMouseDown)
        self.BoardCanvas.bind("<B1-Motion>", self.CanvasMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-1>", self.CanvasMouseUp)      

        #Right click bindings
        self.BoardCanvas.bind("<ButtonPress-3>", self.CanvasRightMouseDown)
        self.BoardCanvas.bind("<B3-Motion>", self.CanvasRightMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-3>", self.CanvasRightMouseUp)      

        #Middle click bindings
        self.BoardCanvas.bind("<ButtonPress-2>", self.CanvasMiddleMouseDown)
        self.BoardCanvas.bind("<B2-Motion>", self.CanvasMiddleMouseDown)          
        self.BoardCanvas.bind("<ButtonRelease-2>", self.CanvasMiddleMouseUp)      
        self.MakeMenu()

        #Set up all the logical board stuff before displaying anything
        self.StrokeQueue = Queue.Queue()
        self.CurrentPointList = []
        self.StrokeList = []
        self.StrokeLoader = StrokeStorage()
        self.ResetBoard()
        self._strokeTrainer = Rubine.RubineClassifier(featureSet = FEATURESET(), debug = True)
        #self.NewTrainingClass()

        self.Redraw()

        self.run()

    def run(self):
       try:
           while 1:
               self.root.update()
               self.root.update_idletasks()
       except TclError:
           pass

      
    def MakeMenu(self):
        "Reserve places in the menu for fun actions!"
        win = self.master 
        top_menu = Menu(win)
        win.config(menu=top_menu)
        
        #self.object_menu = Menu(top_menu)
        #top_menu.add_cascade(label="ObjectMenu", menu=self.object_menu)

        top_menu.add_command(label="Reset Board", command = (lambda :self.ResetBoard() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Load strokes.dat", command = (lambda : self.LoadStrokes() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Save strokes.dat", command = (lambda : self.SaveStrokes()), underline=1 )
        top_menu.add_command(label="Undo Stroke", command = (lambda :self.RemoveLatestStroke() or self.Redraw()), underline=1 )
        top_menu.add_command(label="New Rubine Class", command = (lambda :self.NewTrainingClass() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Train Class on Strokes", command = (lambda :self.TrainClassOnStrokes() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Save Training Data", command = (lambda :self.SaveTrainingWeights() or self.Redraw()), underline=1 )
        top_menu.add_command(label="Strokes From Image", command = (lambda :self.LoadStrokesFromImage() or self.Redraw()), underline=1 )


    def TrainClassOnStrokes(self):
        if self.currentSymClass == None:
            logger.warn("Cannot train on strokes. You must choose a class first!")
            return
        for stroke in self.StrokeList:
            self._strokeTrainer.addStroke(stroke, self.currentSymClass)
            logger.debug("Added stroke to examples for %s" % (self.currentSymClass))
        self.StrokeList = []
        self.currentSymClass = None

    def NewTrainingClass(self):
        name = None
        if self.ClassNameEntry.get().strip() != "":
            name = self.ClassNameEntry.get().strip()

        name = self._strokeTrainer.newClass(name = name)
        self.currentSymClass = name
        print "Now training class %s" % (self.currentSymClass)
        self.ResetBoard()

    def SaveTrainingWeights(self):
        self._strokeTrainer.saveWeights("RubineData.xml")
        self.ResetBoard()

    def LoadStrokes(self):
      for stroke in self.StrokeLoader.loadStrokes():
         self.StrokeList.append(stroke)
        #self.object_menu = Menu(top_menu)
        #top_menu.add_cascade(label="ObjectMenu", menu=self.object_menu)

    def LoadStrokesFromImage(self):
        fname = askopenfilename(initialdir='./')
        if fname == "":
           return

        try:
           logger.debug( "Loading strokes...")
           strokes = ImageStrokeConverter.imageToStrokes(fname)
        except Exception as e:
           logger.debug( "Error importing strokes from image '%s':\n %s" % (fname, e))
           return
        logger.debug( "Loaded %s strokes from '%s'" % (len(strokes), fname))

        for s in strokes:
           newStroke = Stroke()
           for x,y in s.points:
              scale = WIDTH / float(1280)
              newPoint = Point(scale * x,HEIGHT - scale * y)
              newStroke.addPoint(newPoint)
           #self.Board.AddStroke(newStroke)
           self.StrokeList.append(newStroke)

    def SaveStrokes(self):
        self.StrokeLoader.saveStrokes(self.StrokeList)

    def RemoveLatestStroke(self):
        if len (self.StrokeList) > 0:
            stroke = self.StrokeList.pop()



    def ResetBoard(self):
        "Clear all strokes and board observers from the board (logically and visually)"
        self.p_x = self.p_y = None

        self.CurrentPointList = []
        self.StrokeList = []


                
    def CanvasMiddleMouseDown(self, event):
        x = event.x
        y = event.y
        #self.BoardCanvas.create_oval(x,y,x,y,activewidth="1", fill="black", outline = "black")
        
        if self.p_x != None and self.p_y != None:
            p_x = self.p_x
            p_y = self.p_y
            self.BoardCanvas.create_line(p_x, p_y, x ,y, fill = "blue", width=2)

        x = event.x
        y = HEIGHT - event.y
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))

        self.p_x = event.x
        self.p_y = event.y

    def CanvasMiddleMouseUp(self, event):
        self.CurrentPointList = []
        self.p_x = self.p_y = None
        self.Redraw()

    def CanvasRightMouseDown(self, event):
        x = event.x
        y = event.y
        
        if self.p_x != None and self.p_y != None:
            p_x = self.p_x
            p_y = self.p_y
            self.BoardCanvas.create_line(p_x, p_y, x ,y, fill = "gray", width=2)

        x = event.x
        y = HEIGHT - event.y
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))

        self.p_x = x
        self.p_y = HEIGHT - y

    def CanvasRightMouseUp(self, event):
        delStrokes = set([])
        if len(self.CurrentPointList) > 0:
            stroke = Stroke( self.CurrentPointList )#, smoothing=True )
            self.CurrentPointList = []
            for stk in list(self.StrokeList):
                if len(getStrokesIntersection(stroke, stk) ) > 0:
                    logger.debug( "Removing Stroke")
                    self.StrokeList.remove(stk)
        self.p_x = self.p_y = None
        self.Redraw()

    def CanvasMouseDown(self, event):
        "Draw a line connecting the points of a stroke as it is being drawn"
        
        x = event.x
        y = event.y
        #self.BoardCanvas.create_oval(x,y,x,y,activewidth="1", fill="black", outline = "black")
        
        if self.p_x != None and self.p_y != None:
            p_x = self.p_x
            p_y = self.p_y
            self.BoardCanvas.create_line(p_x, p_y, x ,y, fill = "black", width=2)

        x = event.x
        y = HEIGHT - event.y
        t = time.time()
        self.CurrentPointList.append(Point(x,y,t))

        self.p_x = x
        self.p_y = HEIGHT - y

    def AddCurrentStroke(self):
        try:
            if len(self.CurrentPointList) > 0:
                stroke = Stroke( self.CurrentPointList )#, smoothing=True )
                self.StrokeList.append(stroke)
                logger.debug("StrokeAdded Successfully")
        except Exception as e:
            print traceback.format_exc()
            print e
        finally:
            self.CurrentPointList = []
        
    def CanvasMouseUp(self, event):
        "Finish the stroke and add it to the board"
        #start a new stroke
        self.AddCurrentStroke()
        self.p_x = self.p_y = None
        self.Redraw()
        
    def Redraw(self):
        "Find all the strokes on the board, draw them, then iterate through every object and have it draw itself"
        global HEIGHT, WIDTH
        self.BoardCanvas.delete(ALL)
        strokes = self.StrokeList
        for s in strokes:
           self.drawStroke( strokeSmooth(s, width = max(int(strokeLength(s) * 0.01), 1) ))
           #for c in strokeApproximateCubicCurves(s):
               #self.drawCurve(c)

    def drawCircle(self, x, y, radius=1, color="#000000", fill="", width=1.0):
         "Draw a circle on the canvas at (x,y) with radius rad. Color should be 24 bit RGB string #RRGGBB. Empty string is transparent"
         y = HEIGHT - y
         self.BoardCanvas.create_oval(x-radius,y-radius,x+radius,y+radius,width=width, fill=fill, outline = color)
         
    def drawLine(self, x1, y1, x2, y2, width=2, color="#000000"):
         "Draw a line on the canvas from (x1,y1) to (x2,y2). Color should be 24 bit RGB string #RRGGBB"
         y1 = HEIGHT - y1
         y2 = HEIGHT - y2
         self.BoardCanvas.create_line(x1, y1, x2 ,y2, fill=color, width = width)

    def drawText (self, x, y, InText="", size=10, color="#000000"):
        "Draw some text (InText) on the canvas at (x,y). Color as defined by 24 bit RGB string #RRGGBB"
        y = HEIGHT - y
        text_font = ("times", size, "")
        self.BoardCanvas.create_text(x,y,text = InText, fill = color, font = text_font, anchor=NW) 
    def drawStroke(self, stroke, width = 2, color="#000000", erasable = False):
        prev = None
        for p in stroke.Points:
            if prev != None:
                self.drawLine(prev.X, prev.Y, p.X, p.Y, width= width, color = color)
            prev = p