質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

1回答

7616閲覧

python tkinter canvas イベントについて

Platycerium

総合スコア34

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2018/12/01 15:11

tkinterのcanvasでクリックイベントで
最初のポイントを緑色、以降のポイントは赤色にしています。

再び緑色のポイントの上でクリックをしたときに起こるイベント[例えばprint("hello")]を実装したいのですが、
どのようにしたら良いのでしょうか?
(タグ名をstartとして取得しようとしたのですがうまくできませんでした。)

import tkinter as tk zahyo=[] def pos(event): cvs.delete("line") if len(zahyo)>0: cvs.create_line(zahyo[-2], zahyo[-1], event.x, event.y ,tag="line") def click(event): zahyo.append(event.x) zahyo.append(event.y) if len(zahyo)==2: cvs.create_oval(event.x-5, event.y-5, event.x+5, event.y+5,fill='green',tag="start") if len(zahyo)>2: cvs.create_oval(event.x-5, event.y-5, event.x+5, event.y+5,fill='RED') cvs.create_line(zahyo[-4], zahyo[-3], event.x, event.y) window = tk.Tk() window.title('サンプル') cvs = tk.Canvas(window, height=600, width=1000, bg='white',bd=1,relief="ridge") cvs.grid(row=0, column=0) cvs.bind('<Motion>', pos) cvs.bind('<Button-1>', click) window.mainloop()

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

取り敢えず 2 案ほど。

a) オブジェクトに click イベントを bind する

一番単純なのは、 create_oval() する際、最初の 円に対して click イベントを bind しておく ことではないでしょうか。具体的には次のように、別途 click_start() 関数を定義しておき、 create_oval() 時に tag_bind() を使います。そうすると、その円をクリックされた時にだけイベントが呼び出されるようになります。

# 開始円のクリック時に呼び出される関数 def click_start(event): print('hello') def click(event): ... if len(zahyo)==2: start_oval = cvs.create_oval(event.x-5, event.y-5, event.x+5, event.y+5,fill='green',tag="start") # 開始円にイベントを bind cvs.tag_bind(start_oval, '<Button-1>', click_start)

但し、現状の実装ではマウスを動かす度、その座標に向けてに新しい line が最上位に描画されている為、 どこをクリックしてもイベントを受け取るのがマウス直下にある新鮮な line になってしまっているかと存じます。

そのため、最低でも次のように、 line の描画後に tag_lower() で背面に移動しておく必要が出てきます。

def pos(event): ... if len(zahyo)>0: line = cvs.create_line(zahyo[-2], zahyo[-1], event.x, event.y ,tag="line") cvs.tag_lower(line, 'start')

※実際は delete()create_line() を繰り返すよりは、 coords() で一度作成済みの線を移動させた方が (背面に位置させたまま移動できることもあり) 良いと思いますが、細かくなるので割愛します

以上で目的は達成できますし、実装上も直観に即しているので分かりやすいように思いますが、 イベントの propagation を止める仕組みが無いので、 click_start() が呼ばれるときは同時に click() も呼ばれてしまう という課題があります。また、そもそも関数が別々になるということもあり、今回のような処理ではあまり適していない方法かもしれません。

b) クリック座標を含むオブジェクトを列挙する

もう一つの案として、同じ click() イベントハンドラで処理したいのであれば、 find_overlapping を使ってクリックされた座標を含むオブジェクトを列挙し、そこに "start" タグを含むものが存在するかを確認する 方法があるかと思います。

具体的には、 click() 関数の先頭に次のような処理を追加すれば良いでしょう。

def click(event): start_clicked = [ obj for obj in cvs.find_overlapping(*((event.x, event.y) * 2)) if 'start' in cvs.itemcget(obj, 'tags') ] if start_clicked: print('hello') return ...

こちらの方法ですと、直感的にはやっていることが分かりづらいかもしれませんが、同じ関数の処理に条件分岐を加える形になるため、おやりになりたい処理は書きやすいかもしれません。

投稿2018/12/01 16:51

argparse

総合スコア1017

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Platycerium

2018/12/02 01:28

2案目で思い通りのことができるようになりました。 原因の解説もわかりやすく勉強になりました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問