这是一个地图导航应用程序。也许这个功能是现成的,但从头开始构建它将是一个非常丰富的学习练习。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()