Python/tkinterでの多角形の作り方(2)


2023年 11月 09日

実験

数字をベタに書いていたところをちょっといじってみた。

# N多角形の描画  Canvas.create_polygon

import tkinter as tk
from tkinter import Canvas
import numpy as np

app = tk.Tk()
app.title("Star test1")

canvas = Canvas(app,width=400,height=400)
canvas.pack()

## canvas.create_polygon(200,40,105,329,352,150,47,150,294,329,fill="red")
canvas.create_polygon([200,40,105,329,352,150,47,150,294,329],fill="red")

app.mainloop()

変更は、ベタ書きしていた数字列を、リストにしてみた。
結果は、問題なく同じ表示になった。
座標値のベタ書きの代わりに、リストを渡す方法が可能なようだ。

これで、地図の輪郭頂点が与えられれば、地図を描くことが可能だ。
ふつうのことだけど、やはり普通の方法が使えるのだった。

追加実験

もう少しいろいろな書き換えを試してみよう。

# N多角形の描画  Canvas.create_polygon

import tkinter as tk
from tkinter import Canvas
import numpy as np

app = tk.Tk()
app.title("Star test2")

canvas = Canvas(app,width=400,height=400)
canvas.pack()

## canvas.create_polygon(200,40,105,329,352,150,47,150,294,329,fill="red")  # 元

## canvas.create_polygon([200,40,105,329,352,150,47,150,294,329],fill="red")
## canvas.create_polygon([[200,40],[105,329],[352,150],[47,150],[294,329]],fill="red")
## canvas.create_polygon([(200,40),(105,329),(352,150),(47,150),(294,329)],fill="red")

## canvas.create_polygon((200,40,105,329,352,150,47,150,294,329),fill="red")
## canvas.create_polygon(([200,40],[105,329],[352,150],[47,150],[294,329]),fill="red")
## canvas.create_polygon(((200,40),(105,329),(352,150),(47,150),(294,329)),fill="red")

## canvas.create_polygon(200,40,[105,329,352,150,47,150,294,329],fill="red")
## canvas.create_polygon([200,40],[[105,329],[352,150],[47,150],[294,329]],fill="red")
canvas.create_polygon((200,40),[(105,329),(352,150),(47,150),(294,329)],fill="red")

app.mainloop()

実験の最後の1行だけコメントアウトしてあるが、全て動作した。

リストの代わりにタプルにしたり、x座標とy座標のセットを[x,y]でも(x,y)のどちらでも可能であった。

さらに、すべてをリストあるいはタプルの中に入れるのではなく、ベタに書くのとリスト/タプルを交ぜて書いても大丈夫であった。

実に、自由な書き方が許されているのであった。

悪ノリ実験

実験は、以上を行えば十分と普通考えると思うが、少し無謀な実験に踏み込もう。

# N多角形の描画  Canvas.create_polygon  悪乗り実験

import tkinter as tk
from tkinter import Canvas
import numpy as np

app = tk.Tk()
app.title("Star test3")

canvas = Canvas(app,width=400,height=400)
canvas.pack()

## canvas.create_polygon(200,40,105,329,352,150,47,150,294,329,fill="red")  # 元

## canvas.create_polygon([[200,40],105,329,[352,150],(47,150),[294,329]],fill="red")
## canvas.create_polygon([[200,40,105],329,[352,150],(47,150,294),329],fill="red")
## canvas.create_polygon([[200,(40,105)],329,[[352,150],(47,150,294)],329],fill="red")
## canvas.create_polygon([[200,(40,105)],329,[],[[352,150],(),(47,[150,294])],329],fill="red")
canvas.create_polygon([200,(40,105)],329,[],[[[352,150],(),(47,[150,294])],329],fill="red")

app.mainloop()

どうやら、やたらに自由である。
1つの頂点のx座標とy座標でリストやタプルにする必要はないようだ。
リストやタプルの入れ子の階層も自由なようだ。
リストやタプルの中が、頂点の区切りである必要はなく、要素数が奇数個のリストも可能だ。
空のリストやタプルを含ませても構わない。

要するに、頂点列の座標値の並びを想像できないような状態でもまったく関係ないようだ。

多分、可変長引数として入力されたものすべてをフラット化して処理しているらしい。

もっと詳しく知りたい場合には、ソースを見れば良い。

普通の多角形の書き方

# N多角形の描画  Canvas.create_polygon    普通の書き方

import tkinter as tk
from tkinter import Canvas
import numpy as np

# m角形の頂点を、n個おきに繋ぐ単位円上の点列。
def get_polygon_points( m, n ):
    points = []
    th = 2 * np.pi / m * n
    for k in range(m):
        phi = th*k + np.pi/2
        points += [np.cos(phi),-np.sin(phi)]
    return points

# ----- main -----

app = tk.Tk()
app.title("Thorn")

canvas = Canvas(app,width=400,height=400)
canvas.pack()

points = get_polygon_points(50,21)
points = np.array(points) * 160 + 200
points = list(points.astype(int))
print(points,sep=',')

canvas.create_polygon(points,fill="blue")

app.mainloop()

get_polygon_pointsで、単位円周上の頂点座標を求めている。m多角形の頂点をn個置きにめぐるようになっている。Canvas の座標系ではx軸が右、y軸が下になるが、一番上の頂点からの開始になるように、ちょっとだけ工夫した。まあ、このあたりの書き方はいろいろあろう。

あとは、単位円だったのを160倍して、キャンバスの中心になるように200を加えた。

できた点列はNumpyの配列なので、リストにしてからcreate_polygonに渡して多角形を作った。

できた数字のリストpointsをプリントしているが、前回の数字が100個も並んだ部分は、これの出力を使った訳である。

マニュアル

ライブラリをちゃんと使おうと思ったら、下手にその辺りに転がっているサンプルを見てコピーしてはいけない。
マニュアルを見よう。それも、ちゃんとした正規のものを。

ということで探していたら、ぶち当たったのがこれである。

正規のものは多くの場合英語である。

 create_polygon(*args, **kw)

    Draw a polygon. Returns the item id.

    args is two or more coordinate points of the polygon. These will be it’s vertices (corners). Because tkinter flattens these, both (x1, y1, ..., xn, yn) and ((x1, y1), ..., (xn, yn)) are acceptable.

    kw is the options, which can be any of the following:

   ・・・ 以下省略

thinker-docsというドキュメントの中のCanvasのページからの引用である。

これによると、可変個引数で受けて、それをフラット化していることが分かる。フラット化では、リストもタプルもフラット化してしまうから、どちらでもOKとなっているはずである。

どうやら、このレベルのドキュメントを読んでいる人は少ないのだろう。

可変個引数を受け取るというところから、仕様を

def create_polygon(x1, y1, x2, y2, ...., xn, yn, option, ....)

だけに誰かが解釈してしまったのだろう。確かに、こちらの方が直感的にわかりやすく入門者に受け入れられる。
でも、説明の抜け落ちた部分は実用上とても大きい。

ライブラリを使う時、巷に転がっている説明だけに頼るのは危険である。
必ず正規のドキュメント(多くの場合、英語)やソースコードを参照しよう。