地図ナビアプリです。おそらく、この機能はすぐに利用できるものですが、ゼロから構築するには、かなりの学習が必要です。Windows XP での Python 2.6。
OO デザインとして、スクロール可能な tk.canvas に基づくマップ クラスがあります。これは、openstreetmap からタイルをダウンロードすることで背景を取得しますが、(まだ) 問題はありません。マップは問題なくスクロールします。
マップの子として作成されるのは、編集可能なパス クラスの多数のインスタンスです。このクラスには、パスのロード/保存/編集/表示に必要なすべての機能が含まれます。パスは座標タプルのリストです。座標は円として表示され、それらの間のエッジは線として表示されます。円を新しい位置にドラッグできるようにしたい。
できればモードレスにしたい。そのため、経路上にいないときは、ドラッグすると地図がスクロールします。パスを越えてアクティブフィルが表示されたら、パスを編集します。
vertex_drag 関数でコメントアウトされたステートメントを試すのに問題が発生し始めました。最初に、頂点を移動しようとすると、間違った数の引数でエラーが発生し、予想される 0 または 4 ではなく 2 になり、マップがドラッグします。2 つ目は、適切な場所にあるように見える新しい円の軌跡を取得しますが、地図はまだ引きずっています。
私がする必要があるのは、頂点をドラッグしているときにマップのスクロールを抑制することです。最初のイベント ハンドラーに break を入れると、イベントはウィジェットから親に伝達されないという主張をグーグルで見つけました。ただし、これを行うと、python は、ループの外側でブレークを使用したと不平を言います。それは、楕円形がウィジェットではなく別のものであるためですか、それとも 3.0 のドキュメントを間違って見ているのでしょうか?
マップとパス処理の間の適切なオブジェクト分離を維持する方法で、これを行うにはどうすればよいですか? 2つの間にぎこちないものを入れて、いくつかの方法を考えることができると思います。
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()