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)
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 __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()
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 __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 __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 __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()
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)
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)
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)
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))
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)
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)
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