Python Turtleの落とし穴?get_shapepoly()でよくあるエラーと解決策
Pythonの turtle
モジュールにおける turtle.get_shapepoly()
関数は、現在のタートルの形状を構成する多角形の座標ペアのタプルを返すメソッドです。
簡単に言うと、タートル(描画するカメのアイコン)が現在どのような形をしているか(例えば、デフォルトの矢印、円、四角など)、その形を数値で表現したデータ(頂点の座標)を取得したいときに使います。
詳細
-
使い道
- タートルの現在の形状の具体的な幾何学的情報を取得したい場合。
- 例えば、カスタムシェイプを定義する際に、既存のシェイプの頂点情報を参照したい場合。
- タートルの形状に基づいて、特定の計算(例: 衝突判定など)を行いたい場合。
- タートルのサイズ (
turtlesize()
) を変更した際、その変更がどのように頂点座標に影響するかを確認する場合。
-
引数
- この関数は引数をとりません。
-
- タプルの中に、さらに座標のタプル(例:
((x1, y1), (x2, y2), ..., (xn, yn))
)の形式で、タートルの形状を構成する各頂点の相対座標が格納されて返されます。 - これらの座標は、タートルの中心を (0,0) とした相対的な位置を示します。
- タプルの中に、さらに座標のタプル(例:
例
import turtle
# デフォルトのタートルを作成
t = turtle.Turtle()
# デフォルトの形状('classic')のポリゴン情報を取得
default_shape_poly = t.get_shapepoly()
print(f"デフォルトの形状のポリゴン: {default_shape_poly}")
# 形状を'circle'に変更
t.shape("circle")
circle_shape_poly = t.get_shapepoly()
print(f"円の形状のポリゴン: {circle_shape_poly}")
# 形状を'turtle'に変更し、サイズも変更
t.shape("turtle")
t.turtlesize(5, 5, 2) # 大きさを変える
turtle_shape_poly = t.get_shapepoly()
print(f"サイズ変更後のタートル形状のポリゴン: {turtle_shape_poly}")
turtle.done()
turtle.get_shapepoly()
自体がエラーを発生させることは稀ですが、以下のような状況で問題に遭遇する可能性があります。
NameError: name 'turtle' is not defined または AttributeError: 'module' object has no attribute 'Turtle'
- トラブルシューティング
- コードの冒頭に
import turtle
を追加してください。 turtle.Turtle()
を使ってタートルオブジェクトのインスタンスを作成し、そのインスタンスに対してget_shapepoly()
を呼び出してください。import turtle t = turtle.Turtle() # タートルオブジェクトを作成 poly = t.get_shapepoly() print(poly)
- もしファイル名が
turtle.py
であれば、別の名前に変更してください(例:my_drawing.py
など)。
- コードの冒頭に
- 考えられるシナリオ
import turtle
を忘れている。t = turtle.Turtle()
のようにタートルオブジェクトを作成していないのに、いきなりt.get_shapepoly()
のように呼び出している。- ファイルを
turtle.py
という名前で保存してしまい、Pythonが標準ライブラリのturtle
ではなく、自分のファイルを参照してしまう。
- 原因
turtle
モジュールが正しくインポートされていないか、タートルオブジェクトが作成されていない。
TypeError: get_shapepoly() takes 0 positional arguments but 1 was given
- トラブルシューティング
get_shapepoly()
は引数をとりません。単にt.get_shapepoly()
のように呼び出してください。
- 考えられるシナリオ
t.get_shapepoly(some_argument)
のように、括弧の中に何か記述してしまっている。
- 原因
get_shapepoly()
メソッドに不要な引数を渡してしまっている。
get_shapepoly() が返す値の誤解
- トラブルシューティング
get_shapepoly()
は、タートルの中心を (0,0) とした相対座標を返します。タートルが画面上のどこにいても、この相対座標はタートルの形状そのものを表します。タートルの絶対的な位置を知るには、turtle.pos()
またはturtle.position()
を使用する必要があります。- タートルを移動・回転させても、
get_shapepoly()
が返すポリゴン情報(形状そのもの)は変わりません。これは、タートルの「形」を表すものであり、「位置」を表すものではないからです。 turtle.shape()
で設定された組み込みのシェイプ('arrow'
,'turtle'
,'circle'
,'square'
,'triangle'
,'classic'
)や、turtle.register_shape()
で登録したカスタムシェイプのポリゴン情報を正しく返します。もしカスタムシェイプが意図した通りに表示されない場合は、そのシェイプを定義する際の座標に問題がないか確認してください。
- 考えられるシナリオ
- 返される座標が絶対座標(画面上のピクセル位置)だと勘違いしている。
- タートルを移動させたり、回転させたりした後も、
get_shapepoly()
の返す値が変化しないと誤解している。 - カスタムシェイプを登録した場合に、そのシェイプのポリゴン情報が正しく返されないと勘違いしている。
- 原因
get_shapepoly()
が何を返すか、その座標が何を意味するかを誤解している。
描画が期待通りにならない(論理的な問題)
- トラブルシューティング
get_shapepoly()
が返す相対座標を画面上の絶対座標に変換するには、現在のタートルの位置 (t.pos()
) と方向 (t.heading()
) を考慮する必要があります。これには幾何学的な変換(回転と平行移動)が必要になります。- 複数のタートルを扱っている場合は、どのタートルオブジェクトに対して
get_shapepoly()
を呼び出しているかを明確にしてください。import turtle t1 = turtle.Turtle() t2 = turtle.Turtle() t2.shape("circle") t2.penup() t2.goto(100, 50) poly1 = t1.get_shapepoly() # t1の形状 poly2 = t2.get_shapepoly() # t2の形状 print(f"t1のポリゴン: {poly1}") print(f"t2のポリゴン: {poly2}") turtle.done()
- 考えられるシナリオ
- 相対座標を直接絶対座標として使用して描画しようとしている。
- タートルを複数のインスタンスで扱っている場合に、どのタートルの
get_shapepoly()
を呼び出しているか混乱している。
- 原因
get_shapepoly()
で取得した情報を使って何らかの描画を行おうとしているが、座標の解釈や描画ロジックに誤りがある。
- エラーメッセージをよく読む
Pythonのエラーメッセージは非常に情報量が多く、問題の原因を特定する上で重要です。NameError
,AttributeError
,TypeError
などの種類と、具体的なメッセージ内容を確認してください。 - ドキュメントの参照
turtle
モジュールの公式ドキュメント (https://docs.python.org/3/library/turtle.html
) を参照し、関数の使い方や戻り値の形式を再確認します。 - print() デバッグ
get_shapepoly()
の戻り値や、その値を使用する前後の変数の値をprint()
で出力して、期待通りの値になっているか確認します。 - 最小限の再現可能なコード (Minimal, Reproducible Example - MRE) を作成する
問題が発生している部分だけを切り出し、それ以外の要素を排除した簡潔なコードを作成することで、問題の特定が容易になります。
例1: デフォルトおよび組み込みシェイプのポリゴン情報を取得する
最も基本的な使用例です。様々な組み込みシェイプのポリゴン情報がどのように見えるかを確認できます。
import turtle
# スクリーンとタートルオブジェクトの準備
screen = turtle.Screen()
screen.setup(width=600, height=400)
screen.title("get_shapepoly()の基本例")
t = turtle.Turtle()
t.speed(0) # 描画速度を最速に設定
t.penup() # ペンを上げて移動
t.goto(-250, 150)
t.pendown() # ペンを下ろす
# --- デフォルトのシェイプ('classic') ---
print("--- Classic Shape ---")
t.write("Classic", align="center", font=("Arial", 12, "normal"))
t.goto(t.xcor(), t.ycor() - 20) # テキストの下に移動
t.shape("classic")
classic_poly = t.get_shapepoly()
print(f"Classic shape poly: {classic_poly}")
t.stamp() # タートルの形状をスタンプする
# --- Arrow Shape ---
t.penup()
t.goto(-100, 150)
t.pendown()
print("\n--- Arrow Shape ---")
t.write("Arrow", align="center", font=("Arial", 12, "normal"))
t.goto(t.xcor(), t.ycor() - 20)
t.shape("arrow")
arrow_poly = t.get_shapepoly()
print(f"Arrow shape poly: {arrow_poly}")
t.stamp()
# --- Circle Shape ---
t.penup()
t.goto(50, 150)
t.pendown()
print("\n--- Circle Shape ---")
t.write("Circle", align="center", font=("Arial", 12, "normal"))
t.goto(t.xcor(), t.ycor() - 20)
t.shape("circle")
circle_poly = t.get_shapepoly()
print(f"Circle shape poly: {circle_poly}")
t.stamp()
# --- Square Shape ---
t.penup()
t.goto(200, 150)
t.pendown()
print("\n--- Square Shape ---")
t.write("Square", align="center", font=("Arial", 12, "normal"))
t.goto(t.xcor(), t.ycor() - 20)
t.shape("square")
square_poly = t.get_shapepoly()
print(f"Square shape poly: {square_poly}")
t.stamp()
# --- Turtle Shape ---
t.penup()
t.goto(-100, -50)
t.pendown()
print("\n--- Turtle Shape ---")
t.write("Turtle", align="center", font=("Arial", 12, "normal"))
t.goto(t.xcor(), t.ycor() - 20)
t.shape("turtle")
turtle_poly = t.get_shapepoly()
print(f"Turtle shape poly: {turtle_poly}")
t.stamp()
# 画面を閉じないようにする
screen.exitonclick()
解説
t.stamp()
を使うことで、それぞれのタートルの形状を画面に残して視覚的に確認できます。- 取得したポリゴン情報は、コンソールに出力されます。これらの座標は、タートルの中心を (0,0) とした相対座標です。
- 各シェイプを設定し、
t.get_shapepoly()
でその形状のポリゴン情報を取得しています。
例2: get_shapepoly()
を使ってカスタムシェイプを登録する
import turtle
screen = turtle.Screen()
screen.setup(width=600, height=400)
screen.title("get_shapepoly()でカスタムシェイプを作成")
# デフォルトのタートルシェイプのポリゴン情報を取得
default_turtle = turtle.Turtle()
default_turtle.hideturtle() # デフォルトタートルは非表示
default_turtle.shape("classic")
classic_poly = default_turtle.get_shapepoly()
print(f"Classic shape poly: {classic_poly}")
# Classicシェイプのポリゴンを少し変形させて新しいシェイプを作成
# 例: 矢印の先端を少し広げる
modified_poly = list(classic_poly) # タプルをリストに変換して変更可能にする
# classic_polyは通常、((0,0), (-10,-5), (-7,0), (-10,5)) のような形
# 最初の頂点(0,0)は先端、残りはボディ
# 例として、最後の2点を少し外側に広げてみる
modified_poly[1] = (-15, -10) # 2番目の点を左下へ
modified_poly[3] = (-15, 10) # 4番目の点を左上へ
modified_poly = tuple(modified_poly) # 再びタプルに戻す
print(f"Modified poly: {modified_poly}")
# 新しいシェイプを登録
screen.register_shape("my_custom_arrow", modified_poly)
# 新しいタートルを作成し、カスタムシェイプを適用
my_turtle = turtle.Turtle()
my_turtle.shape("my_custom_arrow")
my_turtle.color("blue")
my_turtle.pensize(2)
my_turtle.speed(1)
my_turtle.forward(100)
my_turtle.left(90)
my_turtle.forward(100)
my_turtle.right(45)
my_turtle.forward(50)
# 元のclassicシェイプのタートルを比較のために表示
original_turtle = turtle.Turtle()
original_turtle.shape("classic")
original_turtle.color("red")
original_turtle.penup()
original_turtle.goto(-100, -100)
original_turtle.pendown()
original_turtle.forward(100)
screen.exitonclick()
解説
- 登録後、新しいタートルオブジェクトに
my_turtle.shape("my_custom_arrow")
を適用して、カスタムシェイプで描画していることを確認します。 screen.register_shape()
を使って、新しいシェイプ名 ("my_custom_arrow"
) と変更したポリゴン情報でカスタムシェイプを登録します。- そのポリゴン情報(タプル)をリストに変換し、いくつかの頂点座標を変更して新しい形状を定義します。ここでは、矢印の根元の部分を広げています。
- まず、
classic
シェイプのポリゴン情報を取得します。
get_shapepoly()
で得られる相対座標は、タートルが向いている方向や位置によって画面上の絶対座標に変換する必要があります。ここでは、タートルの現在の形状をペンを使って描画してみます。
import turtle
import math # 回転計算のために必要
screen = turtle.Screen()
screen.setup(width=600, height=400)
screen.title("get_shapepoly()と描画")
t = turtle.Turtle()
t.speed(1)
t.color("blue")
t.pensize(3)
t.shape("turtle") # タートルシェイプを使用
# タートルの現在の形状のポリゴン情報を取得
shape_poly = t.get_shapepoly()
print(f"Turtle shape poly: {shape_poly}")
# タートルを移動させ、その場で形状を再描画する関数
def draw_current_shape_outline(t_obj):
current_x, current_y = t_obj.pos()
current_heading = t_obj.heading()
shape_poly = t_obj.get_shapepoly()
t_obj.penup() # 形状を描画する間はペンを上げる
t_obj.goto(current_x, current_y) # 現在のタートルの位置に戻る
t_obj.pencolor("red") # 形状の輪郭は赤で描画
t_obj.pendown()
# ポリゴンの各点を変換し、描画
for i, (dx, dy) in enumerate(shape_poly):
# 相対座標 (dx, dy) を現在のタートルの向きと位置に基づいて絶対座標に変換
# 回転行列:
# x' = x * cos(theta) - y * sin(theta)
# y' = x * sin(theta) + y * cos(theta)
# ここでのthetaはタートルの向き(度数をラジアンに変換)
theta_rad = math.radians(current_heading)
rotated_x = dx * math.cos(theta_rad) - dy * math.sin(theta_rad)
rotated_y = dx * math.sin(theta_rad) + dy * math.cos(theta_rad)
# 絶対座標に平行移動
abs_x = current_x + rotated_x
abs_y = current_y + rotated_y
if i == 0:
t_obj.goto(abs_x, abs_y)
else:
t_obj.goto(abs_x, abs_y)
t_obj.goto(current_x + (shape_poly[0][0] * math.cos(theta_rad) - shape_poly[0][1] * math.sin(theta_rad)),
current_y + (shape_poly[0][0] * math.sin(theta_rad) + shape_poly[0][1] * math.cos(theta_rad))) # 最初の点に戻って閉じる
t_obj.penup()
t_obj.pencolor("blue") # 元のペンの色に戻す
t_obj.goto(current_x, current_y) # タートルの位置に戻る
# タートルを動かし、その形状を描画
t.forward(100)
draw_current_shape_outline(t)
t.left(90)
t.forward(50)
draw_current_shape_outline(t)
t.right(45)
t.forward(70)
draw_current_shape_outline(t)
# 画面を閉じないようにする
screen.exitonclick()
- この例では、タートルが動くたびに、その位置と向きに合わせて形状の輪郭が正確に描画されるのがわかります。
- 最も重要なのは、
shape_poly
から得られる相対座標(dx, dy)
を、タートルの現在の向き (current_heading
) を考慮して回転させ、さらに現在の位置 (current_x, current_y
) に平行移動させて、画面上の絶対座標 (abs_x, abs_y
) に変換する部分です。- これには、回転行列の概念が使われています。角度を度数からラジアンに変換 (
math.radians()
) するのを忘れないように注意してください。
- これには、回転行列の概念が使われています。角度を度数からラジアンに変換 (
draw_current_shape_outline
関数は、引数で渡されたタートルの現在の位置と向き、そしてget_shapepoly()
で取得した形状情報を使って、そのタートルの現在の「外形」を赤線で描画します。
Pythonの turtle.get_shapepoly()
は、タートルの現在の形状を構成する多角形の座標を直接取得するユニークなメソッドです。したがって、「完全に同じ情報を、まったく別の方法で取得する」という直接的な代替メソッドは存在しません。
しかし、get_shapepoly()
が解決するであろうプログラミングの課題や、それに近い目的を達成するための「代替アプローチ」はいくつか考えることができます。
get_shapepoly()
の主な目的は以下のいずれか、またはその組み合わせであると考えられます。