描画系
プログラムの紹介
まずは簡単なプログラムの紹介。
四角形を 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()