1

这是一个地图导航应用程序。也许这个功能是现成的,但从头开始构建它将是一个非常丰富的学习练习。Windows XP 下的 Python 2.6。

作为 OO 设计,我有一个基于 tk.canvas 的地图类,它是可滚动的。这将通过从 openstreetmap 下载图块来获取其背景,我对此没有任何问题(目前)。地图滚动得很好。

作为地图的子项创建的是可编辑路径类的多个实例。此类将包含加载/保存/编辑/显示路径所需的所有功能。路径是坐标元组的列表。坐标显示为圆圈,它们之间的边缘显示为线。我希望能够将圆圈拖到新位置。

如果可能的话,我希望它是无模式的。因此,当我不在路径上时,拖动会滚动地图。当我在路径上时,并且 activefill 显示我在,我编辑路径。

我开始在尝试 vertex_drag 函数中注释掉的语句时遇到问题。首先,在我试图移动顶点的地方,我收到错误数量的参数错误,得到 2 而不是预期的 0 或 4,并且地图拖动。在第二个中,我确实在似乎正确的地方找到了新圈子的踪迹,但地图仍在拖动。

我需要做的是在拖动顶点时抑制地图滚动。我用谷歌搜索并发现断言,如果我在第一个事件处理程序中放置中断,则事件不会从小部件向下传播到父级。但是,如果我这样做,python 会抱怨我在循环外使用了 break。那是因为椭圆不是小部件而是其他东西,还是我一直在错误地查看 3.0 文档?

我该如何做到这一点,以保持地图和路径处理之间的良好客观分离?我想我可以想到一些可能的方法,在两者之间有一些笨拙的东西。

import Tkinter as tki

class Map(tki.Canvas):
    """ will implement a scrollable by dragging slippy map background"""
    def __init__(self, parent):
        tki.Canvas.__init__(self, parent)        
        self.config(height=600, width=600, xscrollincrement=1,yscrollincrement=1)

        self.bind('<ButtonPress-1>',self.grab)
        self.bind('<ButtonRelease-1>',self.release)
        self.bind('<B1-Motion>',self.drag)

        # before we get the map, this will do
        for x in range(-500, 500, 100):
            for y in range(-500, 500, 100):
                self.create_text((x,y), anchor=tki.CENTER, text='{0} {1}'.format(x,y), activefill = 'red')       

    def grab(self,event):
        self._y = event.y
        self._x = event.x
        self.config(cursor='fleur')

    def release(self,event):
        self.config(cursor='arrow')

    def drag(self, event):
        self.yview('scroll',self._y-event.y,'units')
        self.xview('scroll',self._x-event.x,'units')
        self._y = event.y
        self._x = event.x

class Path():
    """ maintains an edittable path as a sequence of coordinates, drawn on the map"""
    def __init__(self, parent, route):
        self.parent=parent
        self.path=route
        self.draw_path(self.path)        

    def draw_path(self, path):
        """ draws the list of coordinates supplied in the arguments
        creates a list of edges and vertices, binding them to handlers"""

        self.vs=[]
        V_RAD=7
        self.edges=[]
        LINE_WIDTH=3

        (xl, yl)=self.path[0]
        self.vs.append(self.parent.create_oval(xl-V_RAD, yl-V_RAD, xl+V_RAD, yl+V_RAD, activefill='blue', tag='vertex'))

        for (xn, yn) in self.path[1:]:
            self.vs.append(self.parent.create_oval(xn-V_RAD, yn-V_RAD, xn+V_RAD, yn+V_RAD, activefill='blue', tag='vertex'))
            self.edges.append(self.parent.create_line(xl, yl, xn, yn, activefill='blue', width=LINE_WIDTH, tag='edge'))
            (xl, yl)= (xn, yn)

        self.parent.tag_bind('vertex', '<ButtonPress-1>', self.vertex_click)
        self.parent.tag_bind('vertex', '<B1-Motion>', self.vertex_drag)
        self.parent.tag_bind('vertex', '<ButtonRelease-1>', self.vertex_release)
        self.parent.tag_bind('edge', '<ButtonPress-1>', self.edge_click)        

    def vertex_click(self,event):
        print 'vertex clicked ', event
        cx=self.parent.canvasx(event.x)
        cy=self.parent.canvasy(event.y)        
        d=self.parent.find_closest(cx,cy)[0] # closest returns a 1-tuple of an ID, not an ID
        print 'we think ',d, ' is the one thats clicked'
        self.moving_vertex=d
        print event.widget

    def vertex_drag(self,event):
        print 'vertex dragging ', event
        cx=self.parent.canvasx(event.x)
        cy=self.parent.canvasy(event.y)
        print cx,cy

        # self.parent.coords(self.moving_vertex,(cx,cy))
        # self.parent.create_oval(cx-5, cy-5, cx+5, cy+5)

        self.parent.update_idletasks()

    def vertex_release(self,event):
        print 'vertex released ', event

    def edge_click(self, event):
        print 'edge clicked', event     


if __name__ == '__main__':

    root=tki.Tk()
    background_map=Map(root)
    background_map.pack()
    route1=[(10,20), (100,100), (100,0), (150, -30), (100, -100)]
    path=Path(background_map, route1)    
    root.mainloop() 
4

1 回答 1

0

在对self.parent.coordscommented in的调用中vertex_drag,您将偏移量作为元组传递。实际上,在 4 个预期参数中,您必须将画布算作第一个 ( self),再加上另外三个参数:项目的 ID、水平偏移量和垂直偏移量。

self.parent.move(self.moving_vertex, dx, dy)

要忽略事件处理程序,您不必使用break语句,而是在事件处理程序中返回字符串"break"。但是,在这种情况下,您可以做一些更简单的事情:False在您单击该项目时设置一个标志,并True在您释放它时再次设置它:

class Map(tki.Canvas):
    def __init__(self, parent):
        tki.Canvas.__init__(self, parent)
        self.can_drag = True
        #...

    def drag(self, event):
        if self.can_drag:
            self.yview('scroll',self._y-event.y,'units')
            self.xview('scroll',self._x-event.x,'units')
            self._y = event.y
            self._x = event.x

class Path():
    #...

    def vertex_click(self,event):
        self.parent.can_drag = False
        cx=self.parent.canvasx(event.x)
        cy=self.parent.canvasy(event.y)
        d=self.parent.find_closest(cx,cy)[0] # closest returns a 1-tuple of an ID, not an ID
        self.offset = cx, cy
        self.moving_vertex = d

    def vertex_drag(self,event):
        cx = self.parent.canvasx(event.x)
        cy = self.parent.canvasy(event.y)
        dx = cx-self.offset[0]
        dy = cy-self.offset[1]
        self.parent.move(self.moving_vertex, dx, dy)
        self.offset = cx, cy

    def vertex_release(self,event):
        self.parent.can_drag = True
于 2013-03-21T00:59:11.367 回答