1

地図ナビアプリです。おそらく、この機能はすぐに利用できるものですが、ゼロから構築するには、かなりの学習が必要です。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() 
4

1 に答える 1

0

self.parent.coordscommented inの呼び出しでvertex_dragは、オフセットをタプルとして渡しています。実際には、期待される 4 つの引数では、キャンバスを最初の引数 ( self) としてカウントし、さらに 3 つの引数 (アイテムの 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 に答える