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

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

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

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

Q&A

解決済

1回答

2130閲覧

python tkinter canvas 画面のズーム・移動後のマウス座標について

Platycerium

総合スコア34

Python 3.x

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

0グッド

1クリップ

投稿2018/12/28 05:45

pythonのcanvasで左クリックで赤色の円を配置ができるシンプルなものを作成しました。
また、マウスホイールで画面のズーム、右クリックで全体移動を出来るように実装しています。

最初の画面のままであれば問題は無いのですが、画面のズームや、移動した後に、円を配置しようとすると
マウス位置から離れた所に円が描画されてしまいます。

どのようにすれば、画面変更した後でもマウス位置に円を描画できるのでしょうか?

import tkinter as tk from tkinter import * def pos(event): global ini_size canvas.delete("line") x_ent.delete(0, tk.END) x_ent.insert(0, event.x) y_ent.delete(0, tk.END) y_ent.insert(0, event.y) def lclick(event): canvas.create_oval(int(x_ent.get())-5, int(y_ent.get())-5, int(x_ent.get())+5, int(y_ent.get())+5,activefill = 'black',fill='red') def zoomer(event): sf = 1.1 if event.delta > 0 else 0.9 canvas.scale("all", 0,0,sf, sf) canvas.configure(scrollregion=canvas.bbox("all")) def move_start(event): canvas.scan_mark(event.x, event.y) def move_move(event): canvas.scan_dragto(event.x, event.y, gain=1) def show_width(event): window_width.delete(0, tk.END) window_width.insert(0, event.width) window = tk.Tk() window.title('サンプル') frame_T = tk.Frame(window,bd=1,relief="ridge") frame_T.grid(row=0, column=0,sticky='news') frame1_1 = tk.Frame(window,bd=1,relief="ridge") frame1_1.grid(row=1, column=0,sticky='news') frame_main = tk.Frame(window,bd=1,relief="ridge") frame_main.grid(row=2, column=0,sticky='news') y_ent = tk.Entry(frame_T) y_ent.pack(side=RIGHT) x_ent = tk.Entry(frame_T) x_ent.pack(side=RIGHT) window_width = tk.Entry(frame_T) window_width.pack(side=RIGHT) canvas = tk.Canvas(master = frame_main, height=600, width=1000, bg='white',bd=1,relief="ridge") canvas.grid(row=0, column=0,sticky='news') xsb = tk.Scrollbar(frame_main, orient="horizontal", command=canvas.xview) ysb = tk.Scrollbar(frame_main, orient="vertical", command=canvas.yview) canvas.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set) xsb.grid(row=1, column=0,sticky="ew") ysb.grid(row=0, column=1, sticky="ns") canvas.bind('<Motion>', pos) canvas.bind('<Button-1>', lclick) canvas.bind("<MouseWheel>",zoomer) canvas.bind("<ButtonPress-3>", move_start) canvas.bind("<B3-Motion>", move_move) canvas.bind("<Configure>", show_width) frame_main.grid_columnconfigure(0,weight=1) frame_main.grid_rowconfigure(0,weight=1) window.grid_columnconfigure(0,weight=1) window.grid_rowconfigure(2,weight=1) window.mainloop()

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

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

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

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

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

guest

回答1

0

ベストアンサー

以下のようにcanvas.canvasxを用いてウインドウ座標をキャンバスのスクリーン座標に置き換えて管理する必要があるようです。
参考:
9. ボードゲーム用 GUI を作ろう
8.6. Methods on Canvas widgets
Tkinter canvas zoom + move/pan

Python

1def pos(event): 2 x = int(canvas.canvasx(event.x)) 3 y = int(canvas.canvasy(event.y)) 4 5 canvas.delete("line") 6 x_ent.delete(0, tk.END) 7 x_ent.insert(0, x) 8 y_ent.delete(0, tk.END) 9 y_ent.insert(0, y)

追記:円の大きさを倍率に合わせる

試行錯誤していたら、円の大きさを倍率に合わせることができたので貼っておきます。
正確な理屈は理解できていませんが。

Python

1import tkinter as tk 2from tkinter import * 3 4g_scale = 1.0 # 累積倍率 5g_tagno = 1 # 各円のタグ名。新規追加した円のみスケーリングするために。 6 7def pos(event): 8 x = int(canvas.canvasx(event.x)) 9 y = int(canvas.canvasy(event.y)) 10 11 canvas.delete("line") 12 x_ent.delete(0, tk.END) 13 x_ent.insert(0, x) 14 y_ent.delete(0, tk.END) 15 y_ent.insert(0, y) 16 17def lclick(event): 18 x, y = int(x_ent.get()), int(y_ent.get()) 19 20 global g_tagno 21 tag = 'temp{}'.format(g_tagno) 22 canvas.create_oval(x-5, y-5, x+5, y+5, activefill = 'black',fill='red',tag=tag) 23 g_tagno += 1 24 canvas.scale(tag, x, y, g_scale, g_scale) # この円だけスケーリングする 25 26def zoomer(event): 27 sf = 1.1 if event.delta > 0 else 0.9 28 29 # 累積倍率を記録しておく 30 global g_scale 31 g_scale *= sf 32 33 canvas.scale("all", 0,0,sf, sf) 34 canvas.configure(scrollregion=canvas.bbox("all")) 35 36# 以下は変わらず 37 38def move_start(event): 39 canvas.scan_mark(event.x, event.y) 40def move_move(event): 41 canvas.scan_dragto(event.x, event.y, gain=1) 42 43def show_width(event): 44 window_width.delete(0, tk.END) 45 window_width.insert(0, event.width) 46 47window = tk.Tk() 48window.title('サンプル') 49 50frame_T = tk.Frame(window,bd=1,relief="ridge") 51frame_T.grid(row=0, column=0,sticky='news') 52frame1_1 = tk.Frame(window,bd=1,relief="ridge") 53frame1_1.grid(row=1, column=0,sticky='news') 54frame_main = tk.Frame(window,bd=1,relief="ridge") 55frame_main.grid(row=2, column=0,sticky='news') 56 57y_ent = tk.Entry(frame_T) 58y_ent.pack(side=RIGHT) 59x_ent = tk.Entry(frame_T) 60x_ent.pack(side=RIGHT) 61window_width = tk.Entry(frame_T) 62window_width.pack(side=RIGHT) 63 64canvas = tk.Canvas(master = frame_main, height=600, width=1000, bg='white',bd=1,relief="ridge") 65canvas.grid(row=0, column=0,sticky='news') 66 67xsb = tk.Scrollbar(frame_main, orient="horizontal", command=canvas.xview) 68ysb = tk.Scrollbar(frame_main, orient="vertical", command=canvas.yview) 69canvas.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set) 70xsb.grid(row=1, column=0,sticky="ew") 71ysb.grid(row=0, column=1, sticky="ns") 72 73canvas.bind('<Motion>', pos) 74canvas.bind('<Button-1>', lclick) 75canvas.bind("<MouseWheel>",zoomer) 76canvas.bind("<ButtonPress-3>", move_start) 77canvas.bind("<B3-Motion>", move_move) 78canvas.bind("<Configure>", show_width) 79 80frame_main.grid_columnconfigure(0,weight=1) 81frame_main.grid_rowconfigure(0,weight=1) 82window.grid_columnconfigure(0,weight=1) 83window.grid_rowconfigure(2,weight=1) 84 85window.mainloop()

投稿2018/12/28 21:22

編集2018/12/28 22:38
can110

総合スコア38266

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

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

Platycerium

2018/12/29 05:50

理想の動きができるようになりました。 ご返答いただきありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問