今日も見に来てくださって、ありがとうございます。石川さんです。
Canvasで箱を描いて、動かすことをやってきましたけど、箱と箱をつなぐためのコネクションのような線を描くことをやってみました。まあまあうまいことできたように思いますので、紹介させていただきます。
できあがりイメージ
できあがりイメージは以下のとおりです。箱を描いて、まずは中心同士で直線を結ぶことにしました。箱を動かすと線がついてくる、というのが目標です。隠線処理は、次回のテーマとしたいと思います。
ソースコード
ソースコードは以下のとおりです。先日お話したとおり、pylintでチェックしているのですけど、一部、リファクタリングのメッセージが残っています。それは次々回以降のテーマでお話したいと思います。(R0902: too-many-instance-attributesとR0913: too-many-arguments)
import tkinter as tk class Entity(): ''' Entity class ''' def __init__(self, canvas, x, y, width=60, height=40): self.canvas = canvas self.x, self.y, self.width, self.height = x, y, width, height self.start_x = self.start_y = None self.connections = [] self.id = self.canvas.create_rectangle(x, y, x + width, y + height, fill="lightblue", width=3) self.canvas.tag_bind(self.id, "<ButtonPress>", self.button_press) self.canvas.tag_bind(self.id, "<Motion>", self.move) self.canvas.tag_bind(self.id, "<ButtonRelease>", self.button_release) def button_press(self, event): ''' マウスのボタンが押されたときの処理 ''' self.start_x = self.canvas.canvasx(event.x) self.start_y = self.canvas.canvasy(event.y) def move(self, event): ''' マウスが移動したときの処理 ''' if self.start_x is None: return if event.state & 256: # マウスボタン1が押されているときだけ(ドラッグ中のみ) can_x = self.canvas.canvasx(event.x) can_y = self.canvas.canvasy(event.y) coords = self.canvas.coords(self.id) coords[0] -= self.start_x - can_x coords[1] -= self.start_y - can_y coords[2] -= self.start_x - can_x coords[3] -= self.start_y - can_y self.canvas.coords(self.id, coords) self.start_x = can_x self.start_y = can_y self.x, self.y = coords[0:2] for connection in self.connections: connection.move(self) def button_release(self, event): # pylint: disable=unused-argument ''' マウスのボタンが離されたとき ''' self.start_x = self.start_y = None def get_center(self): ''' 中心座標を戻します ''' return self.x + self.width//2, self.y + self.height//2 def add_listener(self, connection): ''' コネクションのリスナーを登録します ''' self.connections.append(connection) class Connection(): ''' Connection class ''' def __init__(self, canvas, start_entity, end_entity): self.canvas = canvas self.start_e = start_entity self.end_e = end_entity start_entity.add_listener(self) end_entity.add_listener(self) self.make_figure() def make_figure(self): ''' コネクションを描きます ''' start_point = self.start_e.get_center() end_point = self.end_e.get_center() self.id = self.canvas.create_line(start_point, end_point) def move(self, entity): ''' エンティティが移動したときの処理(エンティティから呼び出される)''' coords = self.canvas.coords(self.id) if entity == self.start_e: coords[0:2] = entity.get_center() elif entity == self.end_e: coords[2:4] = entity.get_center() self.canvas.coords(self.id, coords) class Application(tk.Tk): ''' Application class ''' def __init__(self): super().__init__() self.title("Connecter test") self.geometry("640x320") self.canvas = tk.Canvas(self, background="white") self.canvas.pack(fill=tk.BOTH, expand=True) entity1 = Entity(self.canvas, 40, 80) entity2 = Entity(self.canvas, 240, 160) Connection(self.canvas, entity1, entity2) entity3 = Entity(self.canvas, 420, 60) Connection(self.canvas, entity2, entity3) def main(): ''' main function ''' application = Application() application.mainloop() if __name__ == "__main__": main()
説明
箱を動かす部分については、これまでに書いてきているので省略します。今回の一つ目のポイントは、Entityクラスです。10行目で接続されたコネクションを受け取るためのリストを作成しています。50~52行で定義されたadd_listener()メソッドで、リスナーとしてコネクションを登録できるようにしています。Entityクラスのmove()メソッドの39~40行で、登録されたコネクションのmove()メソッドが呼び出されます。
2つ目のポイントは、Connectionクラスです。55~79行目です。コネクションが登録されたときに、箱の情報を取得するようにしています。指定された箱のadd_listener()メソッドを使って自分自身を登録します。登録することで、箱が移動したときに、こちらのmove()メソッドを呼び出してもらって、箱の動作にあわせてコネクションがついていくようになります。
まとめ
箱に連動するコネクターが作れるようになりました。次回は接続部分の隠線処理について書きたいと思います。