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

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

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

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

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Q&A

解決済

1回答

2756閲覧

Python3 Tkinter canvasの図形描画位置について

person

総合スコア223

Python 3.x

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

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

0グッド

0クリップ

投稿2020/09/05 18:40

Tkinterで円を等間隔に描画したいです。
ただし、ウィンドウサイズを変えた場合は、円の大きさは変えず、位置はgrid()やpack()みたいに動的に移動する。

canvasのcreate_oval()で楕円の描画ができるようなので下記のようにとりあえず書いてみました。

grid()やpack()みたいに動的にできるか分からないので、とりあえずバインドでリサイズするたびにウィンドウの大きさを取得してその値を使って適当に移動するようにしました。

このときの円の座標の求め方がわかりません。
・円の大きさは、可能であれば後からソースコードで簡単に書き変えられる方が望ましい(が、create_oval()の座標指定を変えることになるか?)
・円は等間隔に表示
・とりあえず今回は円の個数は1個。多分後から「横に3個並べる」、「縦に2個並べる」みたいなことになるかも

座標については下記ソースコードのx1,y1,x2,y2で決めています。

Python

1import tkinter as tk 2 3def resized(e): 4 win_width = win.winfo_width() 5 win_height = win.winfo_height() 6 x1 = win_width * 0.3 7 y1 = win_height * 0.3 8 x2 = x1 + 100 9 y2 = y1 + 100 10 11 cv.delete("oval") 12 cv.create_oval(x1, y1, x2, y2, tag="oval", fill="red", outline="blue", width=5) 13 14win = tk.Tk() 15 16win.rowconfigure(0, weight=1) 17win.columnconfigure(0, weight=1) 18 19win_width = win.winfo_width() 20win_height = win.winfo_height() 21 22""" 参考記事 23 24 https://shizenkarasuzon.hatenablog.com/entry/2018/12/31/080612 25 canvas.create_oval()関数の引数は、(円の左上x座標、円の左上y座標、円の右下のx座標、円の右下のy座標)です。 26 27 https://kotsubu-chan.hatenadiary.org/entry/20020820/1249123877 28 keyword arguments 29 fill 図形の内部を塗るときの色(規定値は 'black')を指定します 30 outline 図形の輪郭を描くときの色(規定値は 'black')を指定します 31 width 図形の輪郭を描くときの幅(画素数:規定値は 1)を指定します 32 33""" 34 35x1 = win_width * 0.3 36y1 = win_height * 0.3 37x2 = x1 + 100 38y2 = y1 + 100 39 40cv = tk.Canvas(win) 41cv.create_oval(x1, y1, x2, y2, tag="oval", fill="red") 42cv.grid(row=0, column=0, sticky="nsew") 43 44win.bind("<Configure>", resized) 45 46 47win.mainloop()

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

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

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

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

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

guest

回答1

0

ベストアンサー

用途に合うか解りませんが、簡単な方法は、キャンバスに描画するのが円だけなら
キャンバスを複数作り grid() で並べる方法もあります。

このときの円の座標の求め方がわかりません。

  • まずは、左上のセルの円の中心座標 (x, y) を求めましょう。
  • キャンバスサイズを等分割した値は、各「セルの幅」です。
  • 円は中央に表示する訳ですから、その半分が円の中心座標になります。
  • x1,y1,x2,y2 は中心座標に半径を足し引きする事で求まります。

左上の円の中心座標を起点に、縦横のセルの幅を足し合わせていくと
他の円の座標も求まります。(range関数が使えます)

python

1import tkinter as tk 2 3def resized(e): 4 canvas = e.widget # <-- bind 対象は win ではなく canvas 5 column = 3 6 row = 2 7 r = 50 8 9 # 他のウィジェットを表示する時、 10 # ウィンドウサイズとキャンバスのサイズは一致しないことがあるので、 11 # キャンバスサイズを元に計算する。 12 canvas_width = canvas.winfo_width() 13 canvas_height = canvas.winfo_height() 14 15 # 等間隔に分割する際の、セルの縦横幅 16 cell_width = canvas_width // column 17 cell_height = canvas_height // row 18 19 canvas.delete("oval") 20 for x in range(cell_width//2, canvas_width, cell_width): 21 for y in range(cell_height//2, canvas_height, cell_height): 22 x1, x2 = x-r, x+r 23 y1, y2 = y-r, y+r 24 25 canvas.create_oval(x1, y1, x2, y2, tag="oval", fill="red", outline="blue", width=5) 26 27if __name__ == '__main__': 28 win = tk.Tk() 29 win.rowconfigure(0, weight=1) 30 win.columnconfigure(0, weight=1) 31 32 canvas = tk.Canvas(win) 33 canvas.grid(row=0, column=0, sticky="nsew") 34 canvas.bind("<Configure>", resized) # <-- win.bind ではなく canvas のイベントにする 35 win.mainloop()

尚、tk.Tk() 直下で所得してるウィンドウのサイズですが、
初期化時の円の描画は意図通りになってません。
ウィンドウが表示されるまでは win.winfo_width(), win.win_height() はそれぞれ 1

mainloop呼び出し後、直ぐに resize が呼ばれてるので、
問題にはなってませんが、無駄なコードになってます。

投稿2020/09/08 02:18

編集2020/09/08 05:28
teamikl

総合スコア8664

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

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

person

2020/09/08 04:39 編集

回答ありがとうございます。 参考にresied()を以下に変更してみました。 cv_width = cv.winfo_width() cv_height = cv.winfo_height() # 円の中心座標 x0 = cv_width / 2 y0 = cv_height / 2 # 円の半径 r = 100 # 円の左上の座標 x1 = x0 - r/math.sqrt(2) y1 = y0 - r/math.sqrt(2) # 円の右上の座標 x2 = x0 + r/math.sqrt(2) y2 = x0 + r/math.sqrt(2) # 再描画 cv.delete("oval") cv.create_oval(x1, y1, x2, y2, tag="oval",fill="red", outline="blue", width=5) geometryでウィンドウ縦横比率を1:1にした時(win.geometry("500x500")など)は中心に円を描画できてい(るように見え)ます。 ちなみに、ウィンドウ縦横比率が1:1でない場合にも(どの角度でも半径が同じ)円を描く方法ってありますかね? (楕円描画の関数を使っているから楕円になって当たり前と言われそうですが・・・)
teamikl

2020/09/08 05:11

x2-x1 == y2-y1 となるはずなので、 縦横比に関わらず同じになるはずですけど、sqrt(2) は何でしょう? 質問文のコードなら直径が100なので半径は50です。 複数の円(縦:row, 横:column分割)を等間隔に想定した場合、 左上の中心座標は x = cv_width // column // 2 y = cv_height // row // 2
person

2020/09/08 05:55

間違えていました。 y2 = x0 + r/math.sqrt(2) y0をx0にしていたから変な形になっていたっぽいです。 半径をルート2で割っていたのは、(x1, y1)が円弧上の座標と解釈しているので、中心から(x1, y1)までの距離のx成分y成分を三平方の定理で求めていたからです。
teamikl

2020/09/08 06:47

> 円弧上の座標と解釈しているので、 使い方が誤りです。参考記事内の説明に有りますが、 > ※ここでいう左上、右下の点というのは、描きたい円(楕円)に「外接する長方形(矩形)の」左上、右下の座標のことです。 なので、円弧上の座標を求める必要はなく、 左上・右下は、単純に中心から半径を足し引きした座標です。
person

2020/09/08 07:12

create_ovalで画像検索かけて、説明の意味を理解しました。 ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問