描画系

プログラムの紹介

まずは簡単なプログラムの紹介。

gui_draw.png

四角形を drag 操作で追加することができ(図の赤)、追加しおわった四角形群(図の青)とともに、順番に表示しています。

コンポーネントとしては、真ん中に Graph があり、あとボタンが付いているだけ。 Graph は、クリック操作だけでなく drag 操作を扱いたいです。複数の event を割り当てるため、 bind_event で各 event に対して、key を割り当てています。プログラム中に書いたように、 例えば、<ButtonPress>graph-press という event 種別が割り当てられています。

    my_graph = eg.Graph(size=(500, 500),
                        background_color='white',
                        key='graph-',  # event を 'graph-' で始める
                        bind_events={
                            "<ButtonPress>": "press",  # マウスボタンを押したときの event が 'graph-press' として扱われる
                            "<ButtonRelease>": "release",  # マウスボタンを離したときは 'graph-release'
                            "<Motion>": "motion",  # マウスを動かしたときは 'graph-motion'
                        })

MVC モデル

こういう描画系では、MVC モデルをつかうのが一般的です。

  • Model: 論理的なデータ構造
  • View: 論理データを GUI に表示したもの
  • Control: 論理データに操作するためのもの

モデルの例

まずは Model。今回は、追加済みの四角形や、 drag に関する情報を持たせています。

    shapes = []  # 今までに配置した四角形を配置
    in_drag = False  # drag 中か否か
    drag_begin = (0, 0)  # drag 中は、drag 始点を格納
    drag_current = (0, 0)  # drag 中は、drag 終点を格納

描画の例

View ですね。 Model に応じて描画をおこなうのですが、こんな感じ。 Graph には draw_rectangle など、図形を描画するためのメソッドが色々あるので、使ってください。 ポイントは、Event に応じて、毎回「差分」を書くんじゃなくて、最初に erace() して、全部書き直しているところです。

    def draw_canvas(self):
        self.my_graph.erase()
        for left_top, right_bottom in self.shapes:
            self.my_graph.draw_rectangle(left_top, right_bottom, fill_color='blue', line_color='white')
        if self.in_drag:
            self.my_graph.draw_rectangle(self.drag_begin, self.drag_current, fill_color='red', line_color='white')

マウスイベントの処理

Control ですね。 マウスイベントは、普通は click されたときに、その位置座標をつかってなにかすればいいんだと思います。 drag の場合は、drag 開始時点と終了時点を判別するため、mouse のボタンダウン、アップを判別しないといけません。

以下のようにmouse 操作に応じて、Model に変更し、draw_canvas()すれば基本大丈夫なはず。

    def on_click(self, event):  # drag 開始・drag 中の処理
        pos = event.x, event.y
        if not self.in_drag:
            self.in_drag = True
            self.drag_begin = pos
        self.drag_current = pos
        print(self.drag_begin, self.drag_current)
        self.draw_canvas()

    def on_drag(self, event):
        self.drag_current = event.x, event.y
        print(self.drag_begin, self.drag_current)
        self.draw_canvas()

    def on_release(self, event):  # drag 終点なら、図形として追加
        pos = event.x, event.y
        if self.in_drag and self.drag_begin != pos:
            self.shapes.append((pos, self.drag_begin))
        self.in_drag = False
        self.draw_canvas()