Пример #1
0
class PhraseEditor(HasTraits):
    """A graphical editor for musical phrases."""

    phrase = Instance(Phrase)
    """The phrase which is edited."""

    selected_point = Instance(Point)
    """The point that is currently moved around."""

    use_grid = Bool
    """Whether added points shall be snapped to a grid."""

    grid_resolution = Range(2,64, 16)
    """The resolution of the grid."""

    point_handle_size = Int(4)
    """The size of the handles used to move points around."""

    def __init__(self, toplevel, phrase, **kwargs):
        """
        Initializes a PhraseEditor.

        @param toplevel: the Tk Toplevel window
        @param phrase: the phrase to be edited
        """

        # The Tk Toplevel object to which the editor will be attached 
        self.toplevel = toplevel

        control_frame = Frame(self.toplevel)
        control_frame.pack(side=TOP)

        title_frame = Frame(control_frame, padx=8)
        title_frame.pack(side=LEFT)

        Label(title_frame, text="Title").pack(side=LEFT)

        self.title_entry_manager = EntryManager(
            title_frame, self, 'phrase.name'
        )
        self.title_entry_manager.entry.pack(side=LEFT)

        grid_frame = Frame(control_frame, padx=8)
        grid_frame.pack(side=LEFT)

        self.grid_checkbutton_manager = CheckbuttonManager(
            grid_frame, self, 'use_grid', text="Grid"
        )
        self.grid_checkbutton_manager.checkbutton.pack(side=LEFT)

        Label(grid_frame, text="Resolution").pack(side=LEFT)

        self.grid_resolution_spinbox_manager = SpinboxManager(
            grid_frame, self, 'grid_resolution',
        )
        self.grid_resolution_spinbox_manager.spinbox.pack(side=LEFT)

        steps_frame = Frame(control_frame, padx=8)
        steps_frame.pack(side=LEFT)

        Label(steps_frame, text="Steps").pack(side=LEFT)

        self.steps_spinbox_manager = SpinboxManager(
            steps_frame, self, 'phrase.steps',
        )
        self.steps_spinbox_manager.spinbox.pack(side=LEFT)

        transpose_frame = Frame(control_frame, padx=8)
        transpose_frame.pack(side=LEFT)

        Label(transpose_frame, text="Transpose").pack(side=LEFT)

        self.transpose_spinbox_manager = SpinboxManager(
            transpose_frame, self, 'phrase.transpose',
        )
        self.transpose_spinbox_manager.spinbox.pack(side=LEFT)

        self.v_point_type = StringVar()
        self.v_point_type.set('Note')

        OptionMenu(
            control_frame, self.v_point_type, 'Note', 'Rest',
        ).pack(side=LEFT)

        self.canvas = Canvas(self.toplevel, width=600, height=400)
        self.canvas.pack(side=BOTTOM)

        # Maps a Point to the ID of its handle, which is a rectangle on the
        # canvas. 
        self._point_handles = {}

        # Maps a tuple of two Points to the ID of the line connecting them on
        # the canvas.
        self._point_lines = {}

        # Maps a Point to the line to its next Point that is currently played.
        self._playing_lines = {}

        # A set of dotted lines marking the grid on the canvas.
        self._grid_lines = set()

        super(PhraseEditor, self).__init__(
            phrase=phrase, use_grid=True, **kwargs
        )

        def find_point(x,y):
            """
            @return: the point at the specified position on the canvas.
            """
            s = self.point_handle_size
            for point in self.phrase.points:
                px,py = point.pos
                if px-s <= x <= px+s and py-s <= y <= py+s:
                    return point
            return None

        def snap_to_grid(x,y):
            """
            Rounds the given coordinates to the grid defined by
            L{PhraseEditor.grid_resolution}.
            """
            res = self.grid_resolution
            return round(x/float(res))*res, round(y/float(res))*res

        def button_pressed(event):
            """
            When the left mouse button is pressed over a point, it becomes the
            L{PhraseEditor.selected_point}, which can be moved around by
            L{button_motion()}.

            If there is no point under the mouse pointer, a new point is
            appended to the L{Phrase}.
            """
            x = self.canvas.canvasx(event.x)
            y = self.canvas.canvasy(event.y)
            point = find_point(x,y)
            if point is None:
                if self.use_grid:
                    x,y = snap_to_grid(x,y)
                point = Point(
                    pos=(int(x),int(y)),
                    type=self.v_point_type.get(),
                )
                self.phrase.points.append(point)
            self.selected_point = point
        self.canvas.bind('<Button-1>', button_pressed)

        def button_motion(event):
            """
            If the mouse is moved while the left or miffle mouse button is held
            down and a point is selected, this point is moved to the current
            mouse pointer position.
            """
            if self.selected_point is None:
                return
            x = self.canvas.canvasx(event.x)
            y = self.canvas.canvasy(event.y)
            if self.use_grid:
                x,y = snap_to_grid(x,y)
            canvas_config = self.canvas.config()
            canvas_width = int(canvas_config['width'][-1])
            canvas_height = int(canvas_config['height'][-1])
            self.selected_point.pos = (
                min(canvas_width, max(0, int(x))),
                min(canvas_height, max(0, int(y)))
            )
        self.canvas.bind('<B1-Motion>', button_motion)
        self.canvas.bind('<B2-Motion>', button_motion)

        def button_released(event):
            """
            When releasing the left or middle mouse button, the currently
            selected point is deselected.
            """
            self.selected_point = None
        self.canvas.bind('<ButtonRelease-1>', button_released)
        self.canvas.bind('<ButtonRelease-2>', button_released)

        def right_button_pressed(event):
            """
            Pressing the right mouse button over a point removes it from the
            L{Phrase}.
            """
            x = self.canvas.canvasx(event.x)
            y = self.canvas.canvasy(event.y)
            point = find_point(x,y)
            if point is not None:
                self.phrase.points.remove(point)
        self.canvas.bind('<Button-3>', right_button_pressed)

        def middle_button_pressed(event):
            if self.selected_point is not None:
                return
            x = self.canvas.canvasx(event.x)
            y = self.canvas.canvasy(event.y)
            point = find_point(x,y)
            if point is None:
                return
            new_point = Point(pos=point.pos, type=self.v_point_type.get())
            self.phrase.points.insert(
                self.phrase.points.index(point)+1,
                new_point
            )
            self.selected_point = new_point
        self.canvas.bind('<Button-2>', middle_button_pressed)

    def close(self):
        """
        Closes the editor, destroying its Toplevel window.
        """
        #del self.phrase
        #del self.selected_point
        #del self._point_handles
        #del self._point_lines
        #del self._playing_lines
        self.toplevel.destroy()

    def _add_point_handle(self, point):
        """
        Adds a point handle for the given point to the canvas.
        """
        s = self.point_handle_size
        px, py = point.pos
        self._point_handles[point] = self.canvas.create_rectangle(
            px-s,py-s, px+s,py+s,
        )

    def _remove_point_handle(self, point):
        """
        Removes the point handle for the given point.
        """
        self.canvas.delete(self._point_handles[point])
        del self._point_handles[point]

    def _add_point_line(self, point1, point2):
        """
        Adds a line between two points on the canvas.
        """
        px1, py1 = point1.pos
        px2, py2 = point2.pos
        self._point_lines[
            (point1, point2)
        ] = self.canvas.create_line(
            px1,py1, px2,py2,
            dash=[2,2] if point2.type == 'Rest' else None
        )

    def _remove_point_line(self, point1, point2):
        """
        Removes the line between two given points.
        """
        px1, py1 = point1.pos
        px2, py2 = point2.pos
        self.canvas.delete(self._point_lines[(point1, point2)])
        del self._point_lines[(point1, point2)]

    def _remove_point_lines(self):
        for points, line in self._point_lines.iteritems():
            self.canvas.delete(line)
        self._point_lines = {}

    def _remove_point_handles(self):
        for point, handle in self._point_handles.iteritems():
            self.canvas.delete(handle)
        self._point_handles = {}

    @on_trait_change('phrase, phrase:points')
    def _points_changed(self, obj, name, old, new):
        if self.phrase is None:
            return
        self._remove_point_handles()
        self._remove_point_lines()
        points = self.phrase.points
        for i, point in enumerate(points):
            self._add_point_handle(point)
            if i > 0:
                self._add_point_line(points[i-1], point)

    @on_trait_change('phrase:points:pos')
    def _point_pos_changed(self, point, name, old, new):
        """
        When a point's position changes, its handle and lines to its
        surrounding points are redefined.
        """
        self._remove_point_handle(point)
        self._add_point_handle(point)
        points = self.phrase.points
        assert point in points
        i = points.index(point)
        if i > 0:
            source_point = points[i-1]
            self._remove_point_line(source_point, point)
            self._add_point_line(source_point, point)
        if i < len(points)-1:
            target_point = points[i+1]
            self._remove_point_line(point, target_point)
            self._add_point_line(point, target_point)

    @on_trait_change('phrase.name')
    def _phrase_name_changed(self):
        """
        When the name of the phrase changes, the window title is update.
        """
        if self.phrase is None:
            return
        self.toplevel.title("Phrase: %s" % self.phrase.name)

    def add_playing_point(self, point, color):
        if point in self._playing_lines:
            self.remove_playing_point(point, color)
        if point not in self.phrase.points:
            return
        px,py, px2,py2 = self.phrase.get_line(point)
        self._playing_lines[point] = self.canvas.create_line(
            px,py, px2,py2, fill=color, width=2.0,
            dash=[2,2] if point.type == 'Rest' else None
        )

    def remove_playing_point(self, point, color):
        if point not in self._playing_lines:
            return
        self.canvas.delete(self._playing_lines[point])
        del self._playing_lines[point]

    @on_trait_change('use_grid, grid_resolution')
    def _grid_changed(self):
        """
        Draws a grid if L{PhraseEditor.use_grid} is True, otherwise removes it.
        """
        for rect in self._grid_lines:
            self.canvas.delete(rect)
        self._grid_lines.clear()
        if self.use_grid:
            config = self.canvas.config()
            w = int(config['width'][-1])
            h = int(config['height'][-1])
            res = self.grid_resolution
            for y in range(0, h, res):
                self._grid_lines.add(self.canvas.create_line(
                    0,y, w,y, dash=[1,res-1], fill="#666666"
                ))
Пример #2
0
class TkFlameGraphRenderer:
    """Renders a call forest to a flame graph using Tk graphics."""
    def __init__(self, call_forest, tk, width, height, colours):
        self.call_forest = call_forest
        self.tk = tk
        self.width = width
        self.height = height
        self.colours = colours
        self.font = ('Courier', 12)
        self.character_width = 0.1 * Font(family='Courier',
                                          size=12).measure('mmmmmmmmmm')
        self.min_width_for_text = 3 * self.character_width
        self.min_height_for_text = 10
        self.x_scale = float(width) / call_forest.samples()
        self.y_scale = float(height) / call_forest.depth()
        self.top = Toplevel(tk)
        self.top.title(call_forest.name)
        self.canvas = Canvas(self.top, width=width, height=height)
        self.canvas.pack()

    def render(self):
        x = 2.0  # ************************************
        y = self.height - self.y_scale
        sorted_roots = sorted(self.call_forest.roots,
                              key=lambda n: n.signature)
        for root in sorted_roots:
            self.render_call_tree(root, x, y)
            x += self.x_scale * root.samples
        # Not sure why I need this
        self.canvas.tag_raise('signature')

    def render_call_tree(self, root, x, y):
        self.render_call_tree_node(root, x, y)
        child_x = x
        child_y = y - self.y_scale
        sorted_children = sorted(root.children, key=lambda n: n.signature)
        for child in sorted_children:
            self.render_call_tree(child, child_x, child_y)
            child_x += self.x_scale * child.samples

    def render_call_tree_node(self, node, x, y):
        bbox = self.bounding_box(node, x, y)
        colour = self.colours.colour_for(node)
        id = self.canvas.create_rectangle(bbox, fill=colour)
        self.bind_events(id, node)
        self.text(node, x, y)

    def bounding_box(self, node, x, y):
        left = x
        right = left + self.x_scale * node.samples
        top = y
        bottom = y + self.y_scale
        bbox = (left, top, right, bottom)
        return bbox

    def text(self, node, x, y):
        height = self.y_scale
        width = self.x_scale * node.samples
        if height > self.min_height_for_text and width > self.min_width_for_text:
            sig = node.signature
            i = sig.rfind('.')
            method = sig[i:]
            char_width = int(width / self.character_width)
            chars = method[:char_width] if len(
                method) >= char_width else sig[-char_width:]
            # chars = node.signature[:char_width]
            id = self.canvas.create_text((x + width / 2, y + height / 2),
                                         text=chars,
                                         anchor='center',
                                         font=self.font,
                                         tags='signature')
            self.bind_events(id, node)

    def bind_events(self, id, node):
        self.canvas.tag_bind(id, '<Enter>',
                             lambda e: self.mouse_enter(e, node))
        self.canvas.tag_bind(id, '<Leave>', self.mouse_leave)
        self.canvas.tag_bind(id, '<Double-Button-1>',
                             lambda e: self.zoom_in(e, node))

    def mouse_enter(self, event, node):
        self.show_tooltip(self.canvas.canvasx(event.x),
                          self.canvas.canvasy(event.y), node)

    def mouse_leave(self, event):
        self.hide_tooltip()

    def zoom_in(self, event, new_root):
        new_call_forest = CallForest(new_root.signature)
        new_call_forest.add_root(new_root)
        new_renderer = TkFlameGraphRenderer(new_call_forest, self.tk,
                                            self.width, self.height,
                                            self.colours)
        new_renderer.render()

    def show_tooltip(self, x, y, node):
        signature, samples = node.signature, node.samples
        percentage = (100.0 * samples) / self.call_forest.samples()
        text = '{} {} {:.2f}%'.format(signature, samples, percentage)
        c = self.canvas
        y_offset = (-10 if y > 30 else 20)
        anchor = 'sw' if x < self.width * 0.3 else 's' if x < self.width * 0.7 else 'se'
        label = c.create_text((x, y + y_offset),
                              text=text,
                              anchor=anchor,
                              tags='tooltip',
                              font=('Courier', 12))
        bounds = c.bbox(label)
        c.create_rectangle(bounds, fill='white', width=0, tags='tooltip')
        c.tag_raise(label)
        pass

    def hide_tooltip(self):
        self.canvas.delete('tooltip')
class AppAnalysis:


    def __init__(self, root):

        self.canvas = Canvas(root, width = 400, height = 350)
        self.canvas.configure(cursor="crosshair")
        self.canvas.pack(expand=YES, fill=BOTH, side='right')

        self.canvas.bind("<Key>", self.handle_key)
        self.canvas.bind("<Double-Button-1>", self.set_focus)
        self.canvas.bind("<Button-1>", self.set_cursor)
        self.canvas.bind("<Return>", self.remove_highlight)

        self.image, self.ponto1, self.ponto2 = (None, None, None)

        self.menubar = Menu(root)

        filemenu = Menu(self.menubar, tearoff=0)
        filemenu.add_command(label="Open Image", command=self.openImage)
        filemenu.add_command(label="Save", command=self.hello)
        filemenu.add_separator()
        filemenu.add_command(label="Exit", command=root.quit)
        self.menubar.add_cascade(label="File", menu=filemenu)

        editmenu = Menu(self.menubar, tearoff=0)
        for e in ("Cut","Copy","Paste"):
            editmenu.add_command(label=e, command=self.hello)

        self.menubar.add_cascade(label="Edit", menu=editmenu)

        filtermenu = Menu(self.menubar, tearoff=0)
        filtermenu.add_command(label="Threshold", command=self.thresholdFilter)
        self.menubar.add_cascade(label="Filter", menu=filtermenu)

        reportmenu = Menu(self.menubar, tearoff=0)
        reportmenu.add_command(label="Relatorio.txt",
        command=self.generateReport)
        reportmenu.add_command(label="Relatorio.pdf")
        reportmenu.add_command(label="Email")
        self.menubar.add_cascade(label="Report", menu=reportmenu)

        helpmenu = Menu(self.menubar, tearoff=0)
        helpmenu.add_command(label="About", command=self.hello)
        self.menubar.add_cascade(label="Help", menu=helpmenu)

        root.config(menu=self.menubar)

        self.toolbar = Frame(root)
        self.toolbar.pack(side='left', fill='both')
        clean = Label(self.toolbar, text='Clean')
        clean.bind("<Button-1>", self.clean)
        b = Label(self.toolbar, text='B')
        c = Label(self.toolbar, text='C')
        d = Label(self.toolbar, text='D')

        for w in (clean,b,c,d):
            w.configure(relief="groove", font="Times 12 bold")
            w.pack(fill='both')

    def openImage(self):
        arquivo = tkFileDialog.askopenfile(parent=self.canvas,mode='rb',
        title='Imagem')
        e = ['GIF','JPEG','JPG','BMP','PNG','TIF']
        if(e.__contains__(arquivo.name.split(".")[-1].upper())):
            self.ponto1, self.ponto2 = (None,None)
            img_tmp = Image.open(arquivo)
            #self.img_name = path.dirname(path.abspath(arquivo.name))
            self.img_name = arquivo.name
            print self.img_name
            self.new_img_name = arquivo.name.split('/')[-1] + "_tmp.gif"
            pathtemp = mydir +"/temp/"+ self.new_img_name
            img_tmp.save(pathtemp)
            self.image = PhotoImage(file=pathtemp)
            self.setImage()
            self.canvas.bind("<Button-1>", self.click)
            self.proporcao = ""

    def clean(self, event):
        self.ponto1, self.ponto2 = (None,None)
        self.setImage()
        self.proporcao = ""

    def setImage(self):
        self.canvas.delete(ALL)
        if self.image.width() > 200 and self.image.height > 200:
            self.canvas.config(width = self.image.width())
            self.canvas.config(height = self.image.height())
        self.canvas.create_image(0, 0, image=self.image, anchor=NW)

    def generateReport(self):
        report = GeradorRelatorio(self.img_name)
        report.start()

    def hello(self):
        print "hello!"

    def thresholdFilter(self):
        img = Image.open(self.img_name)
        new_img = img.filter(ImageFilter.BLUR)
        aux = mydir +"/temp/"+ self.new_img_name
        new_img.save(aux)
        self.image = PhotoImage(file=aux)
        self.setImage()

    def click(self, event):
        if not self.ponto1:
            self.canvas.create_oval(event.x, event.y, event.x+5, event.y+5,
            fill="red")
            self.ponto1 = (event.x,event.y)
        else:
            if not self.ponto2:
                self.canvas.create_oval(event.x, self.ponto1[1],
                event.x+5, self.ponto1[1]+5, fill="red")

                self.ponto2 = (event.x,self.ponto1[1])

                pontos = [self.ponto1[0]+1,self.ponto1[1]+2,
                self.ponto2[0]+1,self.ponto2[1]+2]

                self.canvas.create_line(pontos, tags="theline", fill='red')
                x = (self.ponto2[0] + self.ponto1[0]) / 2
                self.canvas.create_text(x, self.ponto1[1]+8, text="1 umm")

    def remove_highlight(self,event):
        self.canvas.delete("highlight")

    def highlight(self, item):
        bbox = self.canvas.bbox(item)
        self.canvas.delete("highlight")
        if bbox:
            i = self.canvas.create_rectangle(
            bbox, fill="white",
            tag="highlight"
            )
            self.canvas.lower(i, item)

    def has_focus(self):
        return self.canvas.focus()

    def has_selection(self):
        return self.canvas.tk.call(self.canvas._w, 'select', 'item')

    def set_focus(self, event):
        if self.canvas.type(CURRENT) != "text":
            return

        self.highlight(CURRENT)

        self.canvas.focus_set()
        self.canvas.focus(CURRENT)
        self.canvas.select_from(CURRENT, 0)
        self.canvas.select_to(CURRENT, END)

    def set_cursor(self, event):
        item = self.has_focus()
        if not item:
            return

        x = self.canvas.canvasx(event.x)
        y = self.canvas.canvasy(event.y)

        self.canvas.icursor(item, "@%d,%d" % (x, y))
        self.canvas.select_clear()

    def handle_key(self, event):
        item = self.has_focus()
        if not item:
            return

        insert = self.canvas.index(item, INSERT)

        if event.char >= " ":
            if self.has_selection():
                self.canvas.dchars(item, SEL_FIRST, SEL_LAST)
                self.canvas.select_clear()
            self.canvas.insert(item, "insert", event.char)
            self.highlight(item)

        elif event.keysym == "BackSpace":
            if self.has_selection():
                self.canvas.dchars(item, SEL_FIRST, SEL_LAST)
                self.canvas.select_clear()
            else:
                if insert > 0:
                    self.canvas.dchars(item, insert-1, insert)
            self.highlight(item)

        elif event.keysym == "Home":
            self.canvas.icursor(item, 0)
            self.canvas.select_clear()
        elif event.keysym == "End":
            self.canvas.icursor(item, END)
            self.canvas.select_clear()
        elif event.keysym == "Right":
            self.canvas.icursor(item, insert+1)
            self.canvas.select_clear()
        elif event.keysym == "Left":
            self.canvas.icursor(item, insert-1)
            self.canvas.select_clear()
        else:
            pass
Пример #4
0
class MazePlannerCanvas(Frame):
    """
    MazePlannerCanvas contains the main frontend workhorse functionality of the entire
    application.
    it allows the user to graphically place nodes and define the edges between them
    """
    def __init__(self, parent, status=None, manager=DataStore()):
        """
        Construct an instance of the MazePlannerCanvas

        :param parent:              The parent widget that the mazePlannerCanvas will sit in
        :param status:              The statusbar that will receive mouse updates
        :type manager: DataStore
        :return:
        """
        Frame.__init__(self, parent)
        self._manager = manager
        self._canvas = Canvas(self, bg="grey", cursor="tcross")
        self._canvas.pack(fill=BOTH, expand=1)
        self._commands = {
            (ControlSpecifier.DRAG_NODE,    ExecutionStage.START)       : self._begin_node_drag,
            (ControlSpecifier.CREATE_EDGE,  ExecutionStage.START)       : self._begin_edge,
            (ControlSpecifier.DRAG_NODE,    ExecutionStage.END)         : self._end_node_drag,
            (ControlSpecifier.CREATE_EDGE,  ExecutionStage.END)         : self._end_edge,
            (ControlSpecifier.DRAG_NODE,    ExecutionStage.EXECUTE)     : self._execute_drag,
            (ControlSpecifier.CREATE_EDGE,  ExecutionStage.EXECUTE)     : self._execute_edge,
            (ControlSpecifier.MENU,         ExecutionStage.EXECUTE)     : self._launch_menu,
            (ControlSpecifier.CREATE_NODE,  ExecutionStage.EXECUTE)     : self.create_new_node,
        }
        self._commands = load_controls(self._commands)
        self._edge_cache = \
            {
                "x_start"       : None,
                "y_start"       : None,
                "x_end"         : None,
                "y_end"         : None,
                "item_start"    : None,
                "item_end"      : None,
                "edge"          : None
            }
        self._command_cache = None
        self._cache = \
            {
                "item"  : None,
                "x"     : 0,
                "y"     : 0,
                "event" : None
            }
        self._status = status
        self._edge_bindings = {}
        self._node_listing = {}
        self._object_listing = {}
        self._curr_start = None
        self._construct(parent)

    def _construct(self, parent):
        """
        Construct all of the event bindings and callbacks for mouse events
        """
        self._canvas.focus_set()
        self._canvas.bind("<B1-Motion>", lambda event, m_event=Input_Event.DRAG_M1: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<B2-Motion>", lambda event, m_event=Input_Event.DRAG_M2: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<B3-Motion>", lambda event, m_event=Input_Event.DRAG_M3: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<ButtonPress-2>", lambda event, m_event=Input_Event.CLICK_M2: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<ButtonRelease-2>", lambda event, m_event=Input_Event.RELEASE_M2: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<ButtonPress-1>", lambda event, m_event=Input_Event.CLICK_M1: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<ButtonPress-3>", lambda event, m_event=Input_Event.CLICK_M3: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<ButtonRelease-1>", lambda event, m_event=Input_Event.RELEASE_M1: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<ButtonRelease-3>", lambda event, m_event=Input_Event.RELEASE_M3: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<Return>", lambda event, m_event=Input_Event.RETURN: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<Double-Button-1>", lambda event, m_event=Input_Event.D_CLICK_M1: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<Double-Button-2>", lambda event, m_event=Input_Event.D_CLICK_M2: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<Double-Button-3>", lambda event, m_event=Input_Event.D_CLICK_M3: self._handle_mouse_events(m_event, event))
        self._canvas.bind("<Motion>", lambda event, m_event=None : self._handle_mot(m_event, event))
        self._canvas.bind("<Enter>", lambda event: self._canvas.focus_set())
        self._canvas.bind("<space>", lambda event, m_event=Input_Event.SPACE: self._handle_mouse_events(m_event, event))

    def _handle_mot(self, m_event, event):
        """
        Callback function to handle movement of the mouse

        Function updates the mouse location status bar as well as setting cache values to the current location
        of the mouse

        :m_event:           The specifier for the type of event that has been generated
        :event:             The tk provided event object
        """
        event.x = int(self._canvas.canvasx(event.x))
        event.y = int(self._canvas.canvasy(event.y))
        self._status.set_text("Mouse X:" + str(event.x) + "\tMouse Y:" + str(event.y))
        item = self._get_current_item((event.x, event.y))
        if self._is_node(item):
            Debug.printi("Node: " + str(item), Debug.Level.INFO)
        if self._is_edge(item):
            d_x = self._edge_bindings[item].x_start - self._edge_bindings[item].x_end
            d_y = self._edge_bindings[item].y_start - self._edge_bindings[item].y_end
            square = (d_x * d_x) + (d_y * d_y)
            distance = int(math.sqrt(square))
            Debug.printi("Edge: " + str(item) + " | Source: "
                         + str(self._edge_bindings[item].item_start) + " | Target: "
                         + str(self._edge_bindings[item].item_end) + " | Length: "
                         + str(distance))
        self._cache["x"] = event.x
        self._cache["y"] = event.y

    def _handle_mouse_events(self, m_event, event):
        """
        Function that routes mouse events to the appropriate handlers

        Prints logging and UI information about the state of the mouse and then routes
        the mouse event to the appropriate handler

        :m_event:           The specifier for the tupe of event that has been generated
        :event:             The tk provided event object
        """
        event.x = int(self._canvas.canvasx(event.x))
        event.y = int(self._canvas.canvasy(event.y))
        self._status.set_text("Mouse X:" + str(self._cache["x"]) + "\tMouse Y:" + str(self._cache["y"]))
        Debug.printet(event, m_event, Debug.Level.INFO)
        self._cache["event"] = event
        try:
            self._commands[m_event]((event.x, event.y))
        except KeyError:
            Debug.printi("Warning, no control mapped to " + m_event, Debug.Level.ERROR)
        self._command_cache = m_event


    def _begin_node_drag(self, coords):
        """
        Handles starting operations for dragging a node

        Updates the cache information regarding a node drag event, we will used this cache value
        as the handle on which node to update the information for

        :coords:            The mouse coordinates associated with this event
        """
        # Determine which node has been selected, cache this information
        item = self._get_current_item(coords)
        if item in self._node_listing:
            self._update_cache(item, coords)

    def _end_node_drag(self, coords):
        """
        Performs actions to complete a node drag operation

        Validates node location, and other associated object information and updates the cache
        when a node drag is completed

        :coords:            The coordinates associated with this event
        """
        if self._cache["item"] is None:
            return

        # Obtain the final points
        x = coords[0]
        y = coords[1]
        item = self._cache["item"]
        self._validate_node_position(coords)

        container = self._manager.request(DataStore.DATATYPE.NODE, item)
        container.x_coordinate = x
        container.y_coordinate = y
        self._manager.inform(DataStore.EVENT.NODE_EDIT, container.empty_container(), self._cache["item"])
        Debug.printi("Node " + str(self._cache["item"]) + " has been moved", Debug.Level.INFO)
        # Clean the cache
        self._clear_cache(coords)

    def _validate_node_position(self, coords):
        """
        if x < 0:
            x = 0
        if y < 0:
            y = 0
        if x > self._canvas.winfo_width():
            x = self._canvas.winfo_width()-25
        if y > self._canvas.winfo_height():
            y = self._canvas.winfo_height()-25
        self._canvas.move(item, x, y)
        """
        pass

    def _execute_drag(self, coords):
        """
        Updates object position on canvas when user is dragging a node

        :param coords:          The coordinates associated with this event
        """
        # Abort drag if the item is not a node
        if self._cache["item"] not in self._node_listing:
            return
        # Update the drawing information
        delta_x = coords[0] - self._cache["x"]
        delta_y = coords[1] - self._cache["y"]

        # move the object the appropriate amount as long as the drag event has not been done on the empty canvas
        if not self._cache["item"] is None:
            self._canvas.move(self._cache["item"], delta_x, delta_y)

        # record the new position
        self._cache["x"] = coords[0]
        self._cache["y"] = coords[1]

        self._update_attached_edges(self._cache["item"], coords)
        self._update_attached_objects(self._cache["item"], coords)

    def _update_attached_objects(self, item, coords):
        if item not in self._object_listing:
            return
        container = self._manager.request(DataStore.DATATYPE.OBJECT, item)
        container.x_coordinate = coords[0]
        container.y_coordinate = coords[1]
        self._manager.inform(DataStore.EVENT.OBJECT_EDIT, container.empty_container(), item)

    def _update_attached_edges(self, node, coords):
        """
        Updates all associated edges related to a node drag event

        :param node:            The node that has been dragged
        :param coords:          The mouse coordinates which are the new coordinates of the node
        """
        # Go through dictionary and gather list of all attached edge bindings for a node
        start_bindings = []
        for key, binding in self._edge_bindings.iteritems():
            if binding.item_start == node:
                start_bindings.append(binding)

        end_bindings = []
        for key, binding in self._edge_bindings.iteritems():
            if binding.item_end == node:
                end_bindings.append(binding)

        #  Adjust the bindings with this node as the starting edge
        for binding in start_bindings:
            self._canvas.delete(binding.edge)
            del self._edge_bindings[binding.edge]
            old_edge = binding.edge
            binding.edge = self._canvas.create_line(coords[0], coords[1], binding.x_end,
                                                    binding.y_end, tags="edge", activefill="RoyalBlue1", tag="edge")
            self._edge_bindings[binding.edge] = binding
            self._manager.update_key(DataStore.EVENT.EDGE_EDIT, binding.edge, old_edge)
            binding.x_start = coords[0]
            binding.y_start = coords[1]

        # Adjust the bindings with this node as the ending edge
        for binding in end_bindings:
            self._canvas.delete(binding.edge)
            del self._edge_bindings[binding.edge]
            old_edge = binding.edge
            binding.edge = self._canvas.create_line(binding.x_start, binding.y_start,
                                                    coords[0], coords[1], tags="edge", activefill="RoyalBlue1", tag="edge")
            self._edge_bindings[binding.edge] = binding
            self._manager.update_key(DataStore.EVENT.EDGE_EDIT, binding.edge, old_edge)
            binding.x_end = coords[0]
            binding.y_end = coords[1]

        # Remember to adjust all of the edges so that they sit under the node images
        self._canvas.tag_lower("edge")

    def _launch_menu(self, coords):
        """
        Callback function in response to the pressing of the Return key

        Launches a context menu based on the location of the mouse
        :param coords:
        :return:
        """
        # Configure the "static" menu entries -- they can't be static without seriously destroying readability
        # due to the Python version that is being used -.- so now it has to be not optimal until I find a better
        # solution
        p_menu = Menu(self._canvas)
        item = self._get_current_item((self._cache["x"], self._cache["y"]))
        updated_coords = self._canvas_to_screen((self._cache["x"], self._cache["y"]))
        if item is None:
            # No node is currently selected, create the general menu
            p_menu.add_command(label="Place Room", command=lambda: self.create_new_node((self._cache["x"], self._cache["y"])))
            p_menu.add_command(label="Delete All", command=lambda: self.delete_all())
            p_menu.tk_popup(updated_coords[0], updated_coords[1])
            return

        if self._is_node(item):
            # Create the node specific menu
            p_menu.add_command(label="Place Object", command=lambda: self._mark_object((self._cache["x"], self._cache["y"])))
            p_menu.add_command(label="Edit Room", command=lambda: self._selection_operation((self._cache["x"], self._cache["y"])))
            p_menu.add_command(label="Delete Room", command=lambda: self.delete_node(self._get_current_item((self._cache["x"], self._cache["y"]))))
            p_menu.add_command(label="Mark as start", command=lambda: self._mark_start_node(self._get_current_item((self._cache["x"], self._cache["y"]))))

            if self._is_object(item):
                # Launch the node menu as well as an an added option for selecting stuff to edit an object
                p_menu.add_command(label="Edit Object", command=lambda: self._edit_object(coords))
                p_menu.add_command(label="Delete Object", command=lambda: self._delete_object(self._get_current_item((self._cache["x"], self._cache["y"]))))
                p_menu.delete(0)
            p_menu.tk_popup(updated_coords[0], updated_coords[1])
            return

        if self._is_edge(item):
            p_menu.add_command(label="Edit Corridor", command=lambda: self._selection_operation((self._cache["x"], self._cache["y"])))
            p_menu.add_command(label="Delete Corridor", command=lambda: self.delete_edge(self._get_current_item((self._cache["x"], self._cache["y"]))))
            p_menu.tk_popup(updated_coords[0], updated_coords[1])
            return

        self._clear_cache(coords)

    def _edit_object(self, coords):

        """
        Awkward moment when you find a threading related bug in the Tkinter library, caused by some
        Tcl issue or something like that.
        The below line must be left commented out otherwise the window_wait call in the dialog will crash
        out with a Tcl ponter based issue :/
        item = self._get_current_item((self._cache["x"], self._cache["y"]))
        This means that we can only use the mouse to edit objects
        """

        item = self._get_current_item(coords)

        if item not in self._object_listing:
            Debug.printi("Not a valid object to edit", Debug.Level.ERROR)
            return
        obj = ObjectDialog(self, coords[0] + 10, coords[1] + 10, populator=self._manager.request(DataStore.DATATYPE.OBJECT, item))
        Debug.printi("Editing object " + str(item), Debug.Level.INFO)
        self._manager.inform(DataStore.EVENT.OBJECT_EDIT, obj._entries, item)
        Debug.printi("Editing object " + str(item), Debug.Level.INFO)

    def _delete_object(self, item):
        if item not in self._object_listing:
            Debug.printi("Object does not exist to delete", Debug.Level.ERROR)
            return
        del self._object_listing[item]
        self._manager.inform(DataStore.EVENT.OBJECT_DELETE, data_id=item)
        self._canvas.itemconfig(item, outline="red", fill="black", activeoutline="black", activefill="red")

    def _mark_object(self, coords, prog=False, data=None):
        """
        Mark a node as containing an object
        :param coords:
        :return:
        """
        # Retrieve the item
        item = self._get_current_item(coords)

        if not prog:
            if item not in self._node_listing:
                Debug.printi("Invalid object placement selection", Debug.Level.ERROR)
                return

            if item in self._object_listing:
                Debug.printi("This room already has an object in it", Debug.Level.ERROR)
                return
            # Retrieve its coordinates
            # Launch the object maker dialog
            obj = ObjectDialog(self, coords[0] + 10, coords[1] + 10, populator=Containers.ObjectContainer(key_val={
                "x_coordinate"  :   coords[0],
                "y_coordinate"  :   coords[1],
                "name"          :   "Object_"+str(item),
                "mesh"          :   None,
                "scale"         :   None
            }))
            entries = obj._entries
        else:
            entries = {
                "x_coordinate": coords[0],
                "y_coordinate": coords[1],
                "name": data["name"],
                "mesh": data["mesh"],
                "scale": data["scale"]
            }
        # Save informatoin to the manager
        self._manager.inform(DataStore.EVENT.OBJECT_CREATE, entries, item)
        self._object_listing[item] = item
        self._canvas.itemconfig(item, fill="blue")
        Debug.printi("Object created in room " + str(item), Debug.Level.INFO)

    def _valid_edge_cache(self):
        """
        Return true if the edge cache contains a valid edge descriptor

        A valid edge descriptor is when the edge has a valid starting node, if the
        edge does not contain a valid starting node, this means that the edge was not
        created in the proper manner and should thus be ignored by any edge operations
        """
        valid = not self._edge_cache["item_start"] == (None,)
        return valid

    def _canvas_to_screen(self, coords):
        """
        Convert canvas coordinates into screen coordinates

        :param coords:              The current canvas coordinates
        :return:
        """
        """
        # upper left corner of the visible region
        x0 = self._canvas.winfo_rootx()
        y0 = self._canvas.winfo_rooty()

        # given a canvas coordinate cx/cy, convert it to window coordinates:
        wx0 = x0 + coords[0]
        wy0 = y0 + coords[1]


        # upper left corner of the visible region

        x0 = self._canvas.canvasx(0)
        y0 = self._canvas.canvasy(0)

        # given a canvas coordinate cx/cy, convert it to window coordinates:
        wx0 = coords[0] - x0
        wy0 = coords[1] - y0
        #"""
        return (self._cache["event"].x_root, self._cache["event"].y_root)

    def _begin_edge(self, coords):
        """
        Begin recording information regarding the placement of an edge

        :param coords:               The coordinates associated with this event
        """
        # Record the starting node
        self._edge_cache["item_start"] = self._get_current_item((self._cache["x"], self._cache["y"]))

        # Abort the operation if the item was not a valid node to be selecting
        if self._edge_cache["item_start"] is None or self._edge_cache["item_start"] not in self._node_listing:
            self._clear_edge_cache()
            return

        self._edge_cache["x_start"] = self._cache["x"]
        self._edge_cache["y_start"] = self._cache["y"]

    def _end_edge(self, coords, prog=False, data=None):
        """
        Perform the operations required to complete an edge creation operation
        :param coords:
        :return:
        """
        # Check if the cursor is over a node, if so continue, else abort
        curr = self._get_current_item((coords[0], coords[1]))
        if not prog:
            if curr is None or not self._valid_edge_cache() or curr not in self._node_listing:
                # Abort the edge creation process
                self._canvas.delete(self._edge_cache["edge"])
                self._clear_edge_cache()
                return

            # Check if this edge already exists in the program
            if self._check_duplicate_edges(self._edge_cache["item_start"], curr):
                self.delete_edge(self._edge_cache["edge"])
                Debug.printi("Multiple edges between rooms not permitted", Debug.Level.ERROR)
                return

            #Ensure that edges arent made between the same room
            if curr == self._edge_cache["item_start"]:
                Debug.printi("Cannot allow paths starting and ending in the same room", Debug.Level.ERROR)
                return

        self._canvas.tag_lower("edge")
        self._edge_cache["item_end"] = curr

        # Note that we use the edge ID as the key
        self._edge_bindings[self._edge_cache["edge"]] = EdgeBind(self._edge_cache)
        self._edge_bindings[self._edge_cache["edge"]].x_end = coords[0]
        self._edge_bindings[self._edge_cache["edge"]].y_end = coords[1]
        # Inform the manager
        if not prog:
            self._manager.inform(
                DataStore.EVENT.EDGE_CREATE,
                    {
                        "source"    :   self._edge_cache["item_start"],
                        "target"    :   self._edge_cache["item_end"],
                        "height"    :   None,
                        "wall1"     :   {
                            "height":Defaults.Edge.WALL_HEIGHT,
                            "textures":{
                                Defaults.Wall.PATH: {
                                    "path":Defaults.Wall.PATH,
                                    "tile_x":Defaults.Wall.TILE_X,
                                    "tile_y":Defaults.Wall.TILE_Y,
                                    "height":None
                                }
                            }
                        } if Defaults.Config.EASY_MAZE else None,
                        "wall2"     : {
                            "height": Defaults.Edge.WALL_HEIGHT,
                            "textures": {
                                Defaults.Wall.PATH: {
                                    "path": Defaults.Wall.PATH,
                                    "tile_x": Defaults.Wall.TILE_X,
                                    "tile_y": Defaults.Wall.TILE_Y,
                                    "height":None
                                }
                            }
                        }
                    } if Defaults.Config.EASY_MAZE else None,
                self._edge_cache["edge"])
        else:
            # We are programmatically adding the edges in
            self._manager.inform(
                DataStore.EVENT.EDGE_CREATE,
                {
                    "source": self._edge_cache["item_start"],
                    "target": self._edge_cache["item_end"],
                    "height": None,
                    "wall1": data["wall1"],
                    "wall2": data["wall2"]
                },
                self._edge_cache["edge"])

        Debug.printi("Edge created between rooms "
                     + str(self._edge_cache["item_start"])
                     + " and "
                     + str(self._edge_cache["item_end"])
                     , Debug.Level.INFO)
        self._clear_edge_cache()
        self._clear_cache(coords)

    def _check_duplicate_edges(self, start_node, end_node):
        for binding in self._edge_bindings.itervalues():
            if ( start_node == binding.item_start and end_node == binding.item_end )\
            or ( start_node == binding.item_end and end_node == binding.item_start):
                return True
        return False

    def _execute_edge(self, coords):
        """
        Perform the operations that occur during the motion of an edge drag

        :param coords:
        :return:
        """
        # Update the line position
        # We will update the line position by deleting and redrawing
        if not self._valid_edge_cache():
            return

        self._canvas.delete(self._edge_cache["edge"])
        self._edge_cache["edge"] = self._canvas.create_line( \
            self._edge_cache["x_start"], self._edge_cache["y_start"],
            coords[0]-1, coords[1]-1, tags="edge", activefill="RoyalBlue1", tag="edge")
        d_x = self._edge_cache["x_start"] - coords[0]
        d_y = self._edge_cache["y_start"] - coords[1]
        square = (d_x * d_x) + (d_y * d_y)
        distance = math.sqrt(square)
        Debug.printi("Current corridor distance: " + str(int(distance)))

    def _update_cache(self, item, coords):
        """
        Update the local cache with the item id and coordinates of the mouse

        :param item:                The item with which to update the cache
        :param coords:              The current event coordinates
        """
        self._cache["item"] = item
        self._cache["x"] = coords[0]
        self._cache["y"] = coords[1]

    def _clear_cache(self, coords):
        """
        Clear the cache

        Set the cache values to the current mouse position and None the item
        :param coords:              The coordinates of the mouse at that event time
        """
        self._cache["item"] = None
        self._cache["x"] = coords[0]
        self._cache["y"] = coords[1]

    def _clear_edge_cache(self):
        """
        Clear the edge cache to None for all values
        :return:
        """
        self._edge_cache["x_start"]       = None,
        self._edge_cache["y_start"]       = None,
        self._edge_cache["x_end"]         = None,
        self._edge_cache["y_end"]         = None,
        self._edge_cache["item_start"]    = None,
        self._edge_cache["item_end"]      = None,
        self._edge_cache["edge"]          = None


    def _get_current_item(self, coords):
        """
        Return the item(if any) that the mouse is currently over
        :param coords:                  The current coordinates of the mouse
        :return:
        """
        item = self._canvas.find_overlapping(coords[0]-1, coords[1]-1, coords[0]+1, coords[1]+1)

        if item is ():
            return None

        # Hacky solution
        # Return the first node that we come across, since they seem to be returned by tkinter
        # in reverse order to their visual positioning, we'll go through the list backwards
        for val in item[::-1]:
            if val in self._node_listing:
                return val

        # Else, just return the first item and be done with it
        return item[0]

    def _is_node(self, obj):
        """
        Returns true if the supplied object is a node
        :param obj:             The object id to id
        :return:
        """
        return obj in self._node_listing

    def _is_edge(self, obj):
        """
        Returns true if the supplied object is an edge

        :param obj:             The object id to id
        :return:
        """
        return obj in self._edge_bindings

    def _is_object(self, obj):
        """
        Returns true if the supplied object is an object

        :param obj:             The object id to id
        :return:
        """
        return obj in self._object_listing

    def _get_obj_type(self, obj):
        """
        Returns the Object type of the supplied object

        :param obj:             The object to identify
        :return:
        """
        if self._is_node(obj):
            return EditableObject.NODE
        if self._is_edge(obj):
            return EditableObject.EDGE
        if self._is_object(obj):
            return EditableObject.OBJECT
        return None

    def _selection_operation(self, coords):
        """
        Contextually create or edit a node
        :param coords:
        :return:
        """
        # Determine the item ID
        item = self._get_current_item(coords)
        self._cache["item"] = item
        true_coords = self._canvas_to_screen((self._cache["x"], self._cache["y"]))

        if self._is_node(item):
            Debug.printi("Node Selected : " + str(item) + " | Launching Editor", Debug.Level.INFO)
            # Make request from object manager using the tag assigned
            populator = self._manager.request(DataStore.DATATYPE.NODE, item)
            updated_node = NodeDialog(self, true_coords[0] + 10, true_coords[1] + 10, populator=populator)
            # post information to object manager, or let the dialog handle it, or whatever
            self._manager.inform(DataStore.EVENT.NODE_EDIT, updated_node._entries, item)
            return

        if self._is_edge(item):
            Debug.printi("Edge Selected : " + str(item) + " | Launching Editor", Debug.Level.INFO)
            # Make a request from the object manager to populate the dialog
            populator = self._manager.request(DataStore.DATATYPE.EDGE, item)
            updated_edge = EdgeDialog(self, true_coords[0] + 10, true_coords[1] + 10, populator=populator)
            # Make sure that information is posted to the object manager
            self._manager.inform(DataStore.EVENT.EDGE_EDIT, updated_edge._entries, item)

            return

        if self._is_object(item):
            self._edit_object(coords)
            return

    def create_new_node(self, coords, prog = False, data=None):
        """
        Creates a new node on the Canvas and adds it to the datastore
        :param coords:
        :return:
        """
        # Create the node on Canvas
        self._cache["item"] = self._canvas.create_rectangle(coords[0], coords[1], coords[0]+25, coords[1]+25,
                                                            outline="red", fill="black", activeoutline="black", activefill="red", tag="node")

        self._node_listing[self._cache["item"]] = self._cache["item"]
        if not prog:
            if not Defaults.Config.EASY_MAZE:

                true_coords = self._canvas_to_screen((self._cache["x"], self._cache["y"]))
                new_node = NodeDialog(self, true_coords[0] + 25, true_coords[1] + 25,
                                      populator=Containers.NodeContainer(
                                          {
                                              "node_id": self._cache["item"],
                                              "x_coordinate": self._cache["x"],
                                              "y_coordinate": self._cache["y"],
                                              "room_texture": None,
                                              "wall_pictures": None
                                          }))
                entries = new_node._entries
            else:
                entries = {
                    "node_id": self._cache["item"],
                    "x_coordinate": self._cache["x"],
                    "y_coordinate": self._cache["y"],
                    "room_texture": Defaults.Node.ROOM_TEXTURE,
                    "wall_pictures": None
                }
        else:
            pics = data[1]
            data = data[0]
            entries = {
                "node_id": data["id"],
                "x_coordinate": data["x"],
                "y_coordinate": data["y"],
                "room_texture": data["texture"],
                "wall_pictures": pics
            }
        # Inform the datastore
        self._manager.inform(DataStore.EVENT.NODE_CREATE, entries, self._cache["item"])
        self._clear_cache(coords)

    def delete_all(self):
        """
        Delete all nodes and associated edges and objects from the canvas
        """
        # Iterate over each node in the node listing and delete it using delete node
        for key in self._node_listing.keys():
            self.delete_node(key)

        # Delete any rouge edge bindings that may exist
        for binding in self._edge_bindings:
            self.delete_edge(binding)

        self._object_listing.clear()

        # Delete any naughty objects that are left
        self._canvas.delete("all")
        self._manager.inform(DataStore.EVENT.DELETE_ALL)

    def delete_node(self, node_id):
        """
        Delete a node and all its associated edges and object from the canvas

        :param node_id:             The tkinter id of the node to be deleted
        """
        # Delete from our internal representations
        if node_id not in self._node_listing:
            return

        del self._node_listing[node_id]
        # Delete from the canvas
        self._canvas.delete(node_id)

        # Iterate through the edge bindings and delete all of those
        for key in self._edge_bindings.keys():
            if self._edge_bindings[key].item_start == node_id or self._edge_bindings[key].item_end == node_id:
                self.delete_edge(key)
        # Inform the object manager that a node as been deleted
        if node_id in self._object_listing:
            self._delete_object(node_id)
        self._manager.inform(DataStore.EVENT.NODE_DELETE, data_id=node_id)

    def delete_edge(self, edge_id):
        """
        Delete the specified edge from the MazeCanvas

        :param edge_id:             The edge to be deleted
        :return:
        """
        # Go through the edge bindings and delete the appropriate edge
        try:
            # try to delete the edge binding if it exists
            del self._edge_bindings[edge_id]
        except KeyError:
            # Terrible I know, but I dont have the time to find the root cause
            pass
        # Delete the edge from the canvas
        self._canvas.delete(edge_id)
        # Inform the object manager that an edge has been deleted
        self._manager.inform(DataStore.EVENT.EDGE_DELETE, data_id=edge_id)

    def _mark_start_node(self, node_id):
        """
        Mark the passed in node as the starting node
        :param node_id:
        :return:
        """
        # Print the debug information
        # Mark as the new starting node on the canvas, first check that it is a node
        if node_id in self._node_listing:
            Debug.printi("Node:" + str(node_id) + " has been marked as the new starting node", Debug.Level.INFO)
            if self._curr_start is not None:
                # Return the old starting node to its normal colour
                self._canvas.itemconfig(self._curr_start, outline="red", fill="black", activeoutline="black", activefill="red")
            self._curr_start = node_id
            self._canvas.itemconfig(node_id, outline="black", fill="green", activeoutline="green", activefill="black")

        # Inform the object manager that there is a new starting node
        environment_container = self._manager.request(DataStore.DATATYPE.ENVIRONMENT)
        environment_container.start_node = node_id
        self._manager.inform(DataStore.EVENT.ENVIRONMENT_EDIT, environment_container)
Пример #5
0
class Application(Frame, object):
    """The application main class."""
    WIDTH, HEIGHT = 1280, 720
    BG = 'white'
    FONT = 'Verdana'
    FILE_OPEN_OPTIONS = {
        'mode': 'rb',
        'title': 'Choose *.json file',
        'defaultextension': '.json',
        'filetypes': [('JSON file', '*.json')]
    }
    DEFAULTS = 'default_settings.yaml'

    def __init__(self, master=None):
        """Creates application main window with sizes self.WIDTH and self.HEIGHT.

        :param master: instance - Tkinter.Tk instance
        """
        super(Application, self).__init__(master)
        self.master.title('Engine Game')
        self.master.geometry('{}x{}'.format(self.WIDTH, self.HEIGHT))
        self.master.protocol('WM_DELETE_WINDOW', self.exit)

        self.source = None
        self._map = None
        self.points = None
        self.lines = None
        self.captured_point = None
        self.x0 = None
        self.y0 = None
        self.scale_x = None
        self.scale_y = None
        self.font_size = None
        self.coordinates = {}
        self.captured_lines = {}
        self.canvas_obj = AttrDict()
        self.icons = {
            0: PhotoImage(file=join('icons', 'player_city.png')),
            1: PhotoImage(file=join('icons', 'city.png')),
            2: PhotoImage(file=join('icons', 'market.png')),
            3: PhotoImage(file=join('icons', 'store.png')),
            4: PhotoImage(file=join('icons', 'point.png')),
            5: PhotoImage(file=join('icons', 'player_train.png')),
            6: PhotoImage(file=join('icons', 'train.png')),
            7: PhotoImage(file=join('icons', 'crashed_train.png')),
            8: PhotoImage(file=join('icons', 'collision.png')),
            9: PhotoImage(file=join('icons', 'play.png')),
            10: PhotoImage(file=join('icons', 'play_pressed.png')),
            11: PhotoImage(file=join('icons', 'stop.png')),
            12: PhotoImage(file=join('icons', 'stop_pressed.png'))
        }
        self.queue_requests = {
            0: self.set_status_bar,
            1: self.set_player_idx,
            2: self.build_map,
            3: self.refresh_map,
            4: self.set_available_games,
            99: self.bot_control
        }

        self.settings_window = None
        if exists(expanduser(self.DEFAULTS)):
            with open(expanduser(self.DEFAULTS), 'r') as cfg:
                defaults = DefaultsDict.from_yaml(cfg)
            self.host = None if not defaults.host else str(defaults.host)
            self.port = None if not defaults.port else int(defaults.port)
            self.timeout = None if not defaults.timeout else int(defaults.timeout)
            self.username = None if not defaults.username else str(defaults.username)
            self.password = None if not defaults.password else str(defaults.password)
        else:
            self.host, self.port, self.timeout, self.username, self.password = None, None, None, None, None
        self.player_idx = None
        self.posts = {}
        self.trains = {}
        self.select_game_window = False
        self.available_games = None
        self.game = None
        self.num_players = None
        self.num_turns = None
        self.bot = Bot()
        self.bot_thread = None

        self.menu = Menu(self)
        filemenu = Menu(self.menu)
        filemenu.add_command(label='Open file', command=self.file_open)
        filemenu.add_command(label='Server settings', command=self.open_server_settings)
        filemenu.add_command(label='Select game', command=self.select_game)
        filemenu.add_command(label='Exit', command=self.exit)
        self.menu.add_cascade(label='Menu', menu=filemenu)
        master.config(menu=self.menu)

        self._status_bar = StringVar()
        self.label = Label(master, textvariable=self._status_bar)
        self.label.pack()

        self.frame = Frame(self)
        self.frame.bind('<Configure>', self._resize_frame)
        self.canvas = Canvas(self.frame, bg=self.BG, scrollregion=(0, 0, self.winfo_width(), self.winfo_height()))
        self.canvas.bind('<Button-1>', self._capture_point)
        self.canvas.bind('<Motion>', self._move_point)
        self.canvas.bind('<B1-ButtonRelease>', self._release_point)
        self.canvas.bind('<Configure>', self._resize_canvas)
        hbar = Scrollbar(self.frame, orient=HORIZONTAL)
        hbar.pack(side=BOTTOM, fill=X)
        hbar.config(command=self.canvas.xview)
        vbar = Scrollbar(self.frame, orient=VERTICAL)
        vbar.pack(side=RIGHT, fill=Y)
        vbar.config(command=self.canvas.yview)
        self.canvas.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)
        self.canvas.pack(fill=BOTH, expand=True)
        self.play = Label(self.canvas, bg='white')
        self.play.configure(image=self.icons[9])
        self.play.bind('<Button-1>', self._play_press)
        self.play.bind('<B1-ButtonRelease>', self._play_release)
        self.stop = Label(self.canvas, bg='white')
        self.stop.configure(image=self.icons[11])
        self.stop.bind('<Button-1>', self._stop_press)
        self.stop.bind('<B1-ButtonRelease>', self._stop_release)
        self.frame.pack(fill=BOTH, expand=True)

        self.weighted = IntVar(value=1)
        self.weighted_check = Checkbutton(self, text='Proportionally to length', variable=self.weighted,
                                          command=self._proportionally)
        self.weighted_check.pack(side=LEFT)

        self.show_weight = IntVar()
        self.show_weight_check = Checkbutton(self, text='Show length', variable=self.show_weight,
                                             command=self.show_weights)
        self.show_weight_check.pack(side=LEFT)

        self.pack(fill=BOTH, expand=True)
        self.requests_executor()
        self.get_available_games()
        self.set_status_bar('Click Play to start the game')
        self.play.place(rely=0.5, relx=0.5, anchor=CENTER)

    @property
    def map(self):
        """Returns the actual map."""
        return self._map

    @map.setter
    def map(self, value):
        """Clears previously drawn map and assigns a new map to self._map."""
        self.clear_map()
        self.canvas.configure(scrollregion=(0, 0, self.canvas.winfo_width(), self.canvas.winfo_height()))
        self.x0, self.y0 = self.canvas.winfo_width() / 2, self.canvas.winfo_height() / 2
        self._map = value

    @staticmethod
    def midpoint(x_start, y_start, x_end, y_end):
        """Calculates a midpoint coordinates between two points.

        :param x_start: int - x coordinate of the start point
        :param y_start: int - y coordinate of the start point
        :param x_end: int - x coordinate of the end point
        :param y_end: int - y coordinate of the end point
        :return: 2-tuple of a midpoint coordinates
        """
        return (x_start + x_end) / 2, (y_start + y_end) / 2

    def _resize_frame(self, event):
        """Calculates new font size each time frame size changes.

        :param event: Tkinter.Event - Tkinter.Event instance for Configure event
        :return: None
        """
        self.font_size = int(0.0125 * min(event.width, event.height))

    def _resize_canvas(self, event):
        """Redraws map each time Canvas size changes. Scales map each time visible part of Canvas is enlarged.

        :param event: Tkinter.Event - Tkinter.Event instance for Configure event
        :return: None
        """
        if self.map:
            k = min(float(event.width) / float(self.x0 * 2), float(event.height) / float(self.y0 * 2))
            self.scale_x, self.scale_y = self.scale_x * k, self.scale_y * k
            self.x0, self.y0 = self.x0 * k, self.y0 * k
            self.redraw_map()
            self.redraw_trains()
            x_start, y_start, x_end, y_end = self.canvas.bbox('all')
            x_start = 0 if x_start > 0 else x_start
            y_start = 0 if y_start > 0 else y_start
            self.canvas.configure(scrollregion=(x_start, y_start, x_end, y_end))

    def _proportionally(self):
        """Rebuilds map and redraws trains."""
        self.build_map()
        self.redraw_trains()

    def _capture_point(self, event):
        """Stores captured point and it's lines.

        :param event: Tkinter.Event - Tkinter.Event instance for ButtonPress event
        :return: None
        """
        x, y = self.canvas.canvasx(event.x), self.canvas.canvasy(event.y)
        obj_ids = self.canvas.find_overlapping(x - 5, y - 5, x + 5, y + 5)
        if not obj_ids:
            return
        for obj_id in obj_ids:
            if obj_id in self.canvas_obj.point.keys():
                self.captured_point = obj_id
                point_idx = self.canvas_obj.point[obj_id]['idx']
                self.captured_lines = {}
                for line_id, attr in self.canvas_obj.line.items():
                    if attr['start_point'] == point_idx:
                        self.captured_lines[line_id] = 'start_point'
                    if attr['end_point'] == point_idx:
                        self.captured_lines[line_id] = 'end_point'
        if self.weighted.get():
            self.weighted.set(0)

    def _release_point(self, event):
        """Writes new coordinates for a moved point and resets self.captured_point and self.captured_lines.

        :param event: Tkinter.Event - Tkinter.Event instance for ButtonRelease event
        :return: None
        """
        if self.captured_point:
            idx = self.canvas_obj.point[self.captured_point]['idx']
            x, y = self.canvas.canvasx(event.x), self.canvas.canvasy(event.y)
            self.coordinates[idx] = (x, y)
            self.points[idx]['x'], self.points[idx]['y'] = (x - self.x0) / self.scale_x, (y - self.y0) / self.scale_y
            self.captured_point = None
            self.captured_lines = {}

    def _move_point(self, event):
        """Moves point and its lines. Moves weights if self.show_weight is set to 1.

        In case some point is moved beyond Canvas border Canvas scrollregion is resized correspondingly.
        :param event: Tkinter.Event - Tkinter.Event instance for Motion event
        :return: None
        """
        if self.captured_point:
            new_x, new_y = self.canvas.canvasx(event.x), self.canvas.canvasy(event.y)
            self.canvas.coords(self.captured_point, new_x, new_y)
            indent_y = self.icons[self.canvas_obj.point[self.captured_point]['icon']].height() / 2 + self.font_size
            if self.canvas_obj.point[self.captured_point]['text_obj']:
                self.canvas.coords(self.canvas_obj.point[self.captured_point]['text_obj'], new_x, new_y - indent_y)
            self.coordinates[self.canvas_obj.point[self.captured_point]['idx']] = (new_x, new_y)
            self.canvas.configure(scrollregion=self.canvas.bbox('all'))

            for line_id, attr in self.captured_lines.items():
                line_attrs = self.canvas_obj.line[line_id]
                if attr == 'start_point':
                    x, y = self.coordinates[line_attrs['end_point']]
                    self.canvas.coords(line_id, new_x, new_y, x, y)
                else:
                    x, y = self.coordinates[line_attrs['start_point']]
                    self.canvas.coords(line_id, x, y, new_x, new_y)
                if self.show_weight.get():
                    mid_x, mid_y = self.midpoint(new_x, new_y, x, y)
                    self.canvas.coords(line_attrs['weight_obj'][1], mid_x, mid_y)
                    r = self.font_size * len(str(line_attrs['weight']))
                    self.canvas.coords(line_attrs['weight_obj'][0], mid_x - r, mid_y - r, mid_x + r, mid_y + r)

            self.redraw_trains()

    def _play_press(self, _):
        """Draws play button pressed icon."""
        self.play.configure(image=self.icons[10])

    def _play_release(self, _):
        """Draws play button icon and calls bot_control method."""
        self.play.configure(image=self.icons[9])
        self.bot_control()

    def _stop_press(self, _):
        """Draws stop button pressed icon."""
        self.stop.configure(image=self.icons[12])

    def _stop_release(self, _):
        """Draws stop buton icon and calls bot_control method."""
        self.stop.configure(image=self.icons[11])
        self.bot_control()

    def set_player_idx(self, value):
        """Sets a player idx value."""
        self.player_idx = value

    def file_open(self):
        """Opens file dialog and builds and draws a map once a file is chosen. Stops bot if its started."""
        path = tkFileDialog.askopenfile(parent=self.master, **self.FILE_OPEN_OPTIONS)
        if path:
            if self.bot_thread:
                self.bot_control()
            self.posts, self.trains = {}, {}
            self.source = path.name
            self.weighted_check.configure(state=NORMAL)
            self.build_map()

    def open_server_settings(self):
        """Opens server settings window."""
        ServerSettings(self, title='Server settings')

    def select_game(self):
        """Opens select game window."""
        self.select_game_window = True
        SelectGame(self, title='Select game')
        self.select_game_window = False
        self.set_status_bar('Click Play to start the game')

    def exit(self):
        """Closes application and stops bot if its started."""
        if self.bot_thread:
            self.bot_control()
        self.master.destroy()

    def bot_control(self):
        """Starts bot for playing the game or stops it if it is started."""
        if not self.bot_thread:
            self.bot_thread = Thread(target=self.bot.start, kwargs={
                'host': self.host,
                'port': self.port,
                'time_out': self.timeout,
                'username': self.username,
                'password': self.password,
                'game': self.game,
                'num_players': self.num_players,
                'num_turns': self.num_turns})
            self.bot_thread.start()
        else:
            self.bot.stop()
            self.bot_thread.join()
            self.bot_thread = None

    def get_available_games(self):
        """Requests a list of available games."""
        if self.select_game_window:
            self.bot.get_available_games(host=self.host, port=self.port, time_out=self.timeout)
        self.after(1000, self.get_available_games)

    def set_available_games(self, games):
        """Sets new value for available games list."""
        self.available_games = games

    def set_status_bar(self, value):
        """Assigns new status bar value and updates it.

        :param value: string - status bar string value
        :return: None
        """
        self._status_bar.set(value)
        self.label.update()

    def build_map(self, source=None):
        """Builds and draws new map.

        :param source: string - source string; could be JSON string or path to *.json file.
        :return: None
        """
        if source:
            self.source = source
        if self.source:
            self.map = Graph(self.source, weighted=self.weighted.get())
            self.set_status_bar('Map title: {}'.format(self.map.name))
            self.points, self.lines = self.map.get_coordinates()
            self.draw_map()

    def draw_map(self):
        """Draws map by prepared coordinates."""
        self.draw_lines()
        self.draw_points()

    def clear_map(self):
        """Clears previously drawn map and resets coordinates and scales."""
        self.canvas.delete('all')
        self.scale_x, self.scale_y = None, None
        self.coordinates = {}

    def redraw_map(self):
        """Redraws existing map by existing coordinates."""
        if self.map:
            self.coordinates = {}
            for obj_id in self.canvas_obj.line:
                self.canvas.delete(obj_id)
            self.draw_lines()
        self.redraw_points()

    def redraw_points(self):
        """Redraws map points by existing coordinates."""
        if self.map:
            for obj_id, attrs in self.canvas_obj.point.items():
                if attrs['text_obj']:
                    self.canvas.delete(attrs['text_obj'])
                self.canvas.delete(obj_id)
            self.draw_points()

    def redraw_trains(self):
        """Redraws existing trains."""
        if self.trains and hasattr(self.canvas_obj, 'train'):
            for obj_id, attrs in self.canvas_obj.train.items():
                self.canvas.delete(attrs['text_obj'])
                self.canvas.delete(obj_id)
        self.draw_trains()

    @prepare_coordinates
    def draw_points(self):
        """Draws map points by prepared coordinates."""
        point_objs = {}
        captured_point_idx = self.canvas_obj.point[self.captured_point]['idx'] if self.captured_point else None
        for idx in self.points.keys():
            x, y = self.coordinates[idx]
            if self.posts and idx in self.posts.keys():
                post_type = self.posts[idx]['type']
                if post_type == 1:
                    status = '{}/{} {}/{} {}/{}'.format(self.posts[idx]['population'],
                                                        self.posts[idx]['population_capacity'],
                                                        self.posts[idx]['product'],
                                                        self.posts[idx]['product_capacity'],
                                                        self.posts[idx]['armor'],
                                                        self.posts[idx]['armor_capacity'])
                elif post_type == 2:
                    status = '{}/{}'.format(self.posts[idx]['product'], self.posts[idx]['product_capacity'])
                else:
                    status = '{}/{}'.format(self.posts[idx]['armor'], self.posts[idx]['armor_capacity'])
                image_id = 0 if post_type == 1 and self.posts[idx]['player_idx'] == self.player_idx else post_type
                point_id = self.canvas.create_image(x, y, image=self.icons[image_id])
                y -= (self.icons[post_type].height() / 2) + self.font_size
                text_id = self.canvas.create_text(x, y, text=status, font="{} {}".format(self.FONT, self.font_size))
            else:
                post_type = 4
                point_id = self.canvas.create_image(x, y, image=self.icons[post_type])
                text_id = None
            point_objs[point_id] = {'idx': idx, 'text_obj': text_id, 'icon': post_type}
            self.captured_point = point_id if idx == captured_point_idx else self.captured_point
        self.canvas_obj['point'] = point_objs

    @prepare_coordinates
    def draw_lines(self):
        """Draws map lines by prepared coordinates and shows their weights if self.show_weight is set to 1."""
        line_objs, captured_lines_idx = {}, {}
        if self.captured_lines:
            for line_id in self.captured_lines.keys():
                captured_lines_idx[self.canvas_obj.line[line_id]['idx']] = line_id
        for idx, attrs in self.lines.items():
            x_start, y_start = self.coordinates[attrs['start_point']]
            x_stop, y_stop = self.coordinates[attrs['end_point']]
            line_id = self.canvas.create_line(x_start, y_start, x_stop, y_stop)
            line_objs[line_id] = {'idx': idx, 'weight': attrs['weight'], 'start_point': attrs['start_point'],
                                  'end_point': attrs['end_point'], 'weight_obj': ()}
            if idx in captured_lines_idx.keys():
                self.captured_lines[line_id] = self.captured_lines.pop(captured_lines_idx[idx])
        self.canvas_obj['line'] = line_objs
        self.show_weights()

    @prepare_coordinates
    def draw_trains(self):
        """Draws trains by prepared coordinates."""
        trains = {}
        for train in self.trains.values():
            start_point = self.lines[train['line_idx']]['start_point']
            end_point = self.lines[train['line_idx']]['end_point']
            weight = self.lines[train['line_idx']]['weight']
            position = train['position']
            x_start, y_start = self.coordinates[start_point]
            x_end, y_end = self.coordinates[end_point]
            delta_x, delta_y = int((x_start - x_end) / weight) * position, int((y_start - y_end) / weight) * position
            x, y = x_start - delta_x, y_start - delta_y
            if train['cooldown'] > 0:
                icon = 7
                status = None
            else:
                icon = 5 if train['player_idx'] == self.player_idx else 6
                status = '{}/{}'.format(train['goods'], train['goods_capacity'])
            indent_y = self.icons[icon].height() / 2
            train_id = self.canvas.create_image(x, y - indent_y, image=self.icons[icon])
            text_id = self.canvas.create_text(x, y - (2 * indent_y + self.font_size), text=status,
                                              font="{} {}".format(self.FONT, self.font_size)) if status else None
            trains[train_id] = {'icon': icon, 'text_obj': text_id}
        self.canvas_obj['train'] = trains

    def show_weights(self):
        """Shows line weights when self.show_weight is set to 1 and hides them when it is set to 0."""
        if not self.canvas_obj:
            return
        if self.show_weight.get():
            for line in self.canvas_obj.line.values():
                if line['weight_obj']:
                    for obj in line['weight_obj']:
                        self.canvas.itemconfigure(obj, state='normal')
                else:
                    x_start, y_start = self.coordinates[line['start_point']]
                    x_end, y_end = self.coordinates[line['end_point']]
                    x, y = self.midpoint(x_start, y_start, x_end, y_end)
                    value = line['weight']
                    size = self.font_size
                    r = int(size) * len(str(value))
                    oval_id = self.canvas.create_oval(x - r, y - r, x + r, y + r, fill=self.BG, width=0)
                    text_id = self.canvas.create_text(x, y, text=value, font="{} {}".format(self.FONT, str(size)))
                    line['weight_obj'] = (oval_id, text_id)
        else:
            for line in self.canvas_obj.line.values():
                if line['weight_obj']:
                    for obj in line['weight_obj']:
                        self.canvas.itemconfigure(obj, state='hidden')

    def requests_executor(self):
        """Dequeues and executes requests. Assigns corresponding label to bot control button."""
        if not self.bot.queue.empty():
            request_type, request_body = self.bot.queue.get_nowait()
            if request_type == 99 and request_body:
                self.open_server_settings()
                request_body = None
            if request_body is not None:
                self.queue_requests[request_type](request_body)
            else:
                self.queue_requests[request_type]()
        if self.bot_thread and self.bot_thread.is_alive():
            if self.play.place_info():
                self.play.place_forget()
                self.stop.place(rely=0.99, relx=0.995, anchor=SE)
        else:
            if self.stop.place_info():
                self.stop.place_forget()
                self.play.place(rely=0.5, relx=0.5, anchor=CENTER)
        self.after(50, self.requests_executor)

    def refresh_map(self, dynamic_objects):
        """Refreshes map with passed dynamic objects.

        :param dynamic_objects: dict - dict of dynamic objects
        :return: None
        """
        for post in dynamic_objects['posts']:
            self.posts[post['point_idx']] = post
        for train in dynamic_objects['trains']:
            self.trains[train['idx']] = train
        self.redraw_points()
        self.redraw_trains()
Пример #6
-1
class Viewer(Frame):
    def __init__(self, master,x=600,y=200, onLeft=None, onRight=None, **kwargs):
        self.root=master
        self.xsize=x
        self.ysize=y
        self.onLeft=onLeft
        self.onRight=onRight
        self.ratio=100.
        Frame.__init__(self, master,width=x,height=y, **kwargs)
        self.canvas = Canvas(self, width=x, height=y, background="white")
        self.xsb = Scrollbar(self, orient="horizontal", command=self.canvas.xview)
        self.ysb = Scrollbar(self, orient="vertical", command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.ysb.set, xscrollcommand=self.xsb.set)
        self.canvas.configure(scrollregion=(0,0,x,y))

        self.xsb.grid(row=1, column=0, sticky="ew")
        self.ysb.grid(row=0, column=1, sticky="ns")
        self.canvas.grid(row=0, column=0, sticky="nsew")
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

        self.canvas.bind("<Button-1>", self.clickL)
        self.canvas.bind("<Button-3>", self.clickR)

        # This is what enables using the mouse:
        self.canvas.bind("<ButtonPress-1>", self.move_start)
        self.canvas.bind("<B1-Motion>", self.move_move)
        #linux scroll
        self.canvas.bind("<Button-4>", self.zoomerP)
        self.canvas.bind("<Button-5>", self.zoomerM)

        self.canvas.bind_all("<Prior>", self.zoomerP)
        self.canvas.bind_all("<Next>", self.zoomerM)
        self.canvas.bind_all("E", self.zoomExtens)
        #windows scroll
        self.canvas.bind_all("<MouseWheel>",self.zoomer)

    def reset(self):
        pass

    def set_title(self, title):
        self.root.title( title)

    #move
    def move_start(self, event):
        self.canvas.scan_mark(event.x, event.y)
        return True
    def move_move(self, event):
        self.canvas.scan_dragto(event.x, event.y, gain=1)
        return True

    #windows zoom
    def zoomer(self,event):
        if (event.delta > 0):
            self.ratio *= 1.1
        elif (event.delta < 0):
            self.ratio *= 0.9
        self.redraw()

    def redraw(self):
        self.canvas.delete("all")
        self.canvas.configure(scrollregion = self.canvas.bbox("all"))

    def zoomExtens(self, *args):
        x0,y0,x1,y1 = self.canvas.bbox("all")
        xlen=x1-x0
        ylen=y1-y0
        height=self.canvas.winfo_height()
        width=self.canvas.winfo_width()
        unfit=min(width/float(xlen), height/float(ylen))
        self.ratio*=unfit
        self.redraw()
        #
        """"
        if xlen and ylen:
            self ratio*=
            self.canvas.scale("all", xlen/2., ylen/2., float(self.xsize)/xlen, float(self.ysize)/ylen)
            self.canvas.configure(scrollregion = self.canvas.bbox("all"))
        """
    #linux zoom
    def zoomerP(self,event):
        self.canvas.scale("all", event.x, event.y, 1.1, 1.1)
        self.canvas.configure(scrollregion = self.canvas.bbox("all"))
    def zoomerM(self,event):
        self.canvas.scale("all", event.x, event.y, 0.9, 0.9)
        self.canvas.configure(scrollregion = self.canvas.bbox("all"))
    def pose2p(self, pose):
        x,y=pose[:2]
        return int(x*self.ratio), -int(y*self.ratio)

    def line2p(self, line):
        if type(line[0]) in (float, int):
            line=zip(line[::2],line[1::2])
        return map(self.pose2p, line)
    def p2m(self, x, y):
        return x/self.ratio, -y/self.ratio
    def create_line(self, coords, **kwargs):
        return self.canvas.create_line(self.line2p(coords), **kwargs)
    def create_polygon(self, coords, **kwargs):
        return self.canvas.create_polygon(self.line2p(coords), **kwargs)
    def delete(self, object):
        self.canvas.delete(object)

    def clickL(self, event):
        if self.onLeft:
            x,y=self.p2m(int(self.canvas.canvasx(event.x)), int(self.canvas.canvasy(event.y)))
            self.onLeft(x,y)
        return True

    def clickR(self, event):
        if self.onRight:
            x,y=self.p2m(int(self.canvas.canvasx(event.x)), int(self.canvas.canvasy(event.y)))
            self.onRight(x,y)
        return True