Python turtle.ontimer() 完全解説:アニメーション作成の基本

2025-06-06

使い方

基本的な使い方は以下の通りです。

import turtle

def my_function():
    # この関数は、ontimerで指定された時間が経過した後に実行されます
    print("指定された時間が経過しました!")
    turtle.forward(50)

# スクリーンオブジェクトを作成
screen = turtle.Screen()

# 2000ミリ秒(2秒)後にmy_functionを実行
screen.ontimer(my_function, 2000)

# タートルを動かすなど、他の処理を続けることができます
turtle.forward(100)

# イベントループを開始(ウィンドウを閉じないようにする)
turtle.done()

パラメータ

turtle.ontimer() は通常、turtle.Screen() オブジェクトのメソッドとして呼び出されます。

  • t: 遅延時間(ミリ秒単位)を指定します。tミリ秒後にfunが呼び出されます。
  • fun: 実行したい関数オブジェクトを指定します。この関数は引数を取らないものでなければなりません。

なぜ turtle.ontimer() が便利なのか?

  1. アニメーションの制御: タートルの動きを一定時間ごとに制御したり、定期的に画面を更新したりするのに使えます。例えば、ボールが跳ねるアニメーションで、ontimerを使ってボールの位置を少しずつ更新することができます。

  2. イベントの遅延実行: 特定のアクション(例えば、ユーザーがボタンを押した後、数秒経ってからメッセージを表示するなど)を遅延して実行したい場合に便利です。

  3. ブロックしない処理: time.sleep() のようにプログラムの実行を完全に停止させるのではなく、ontimer() はタイマーを設定した後もプログラムの残りの部分が実行され続けます。これは、GUIアプリケーションでユーザーインターフェースが固まらないようにするために非常に重要です。

  4. 再帰的な呼び出し: ontimerで呼び出される関数内で再び ontimer を呼び出すことで、特定の処理を繰り返し実行するループを作成できます。これはゲームのメインループなどによく使われます。

    import turtle
    
    def move_turtle():
        turtle.forward(10)
        # 100ミリ秒後に再度move_turtleを呼び出す
        screen.ontimer(move_turtle, 100)
    
    screen = turtle.Screen()
    turtle.speed(0) # アニメーションを早くするためにスピードを0に設定
    screen.ontimer(move_turtle, 100) # 最初の呼び出し
    
    turtle.done()
    
  • turtle.done() は、タートルグラフィックのウィンドウが開いたままになり、イベントループが開始されることを保証します。ontimer の設定が有効に機能するためには、通常 turtle.done() が必要です。
  • ontimer() は一度だけ実行されます。繰り返し実行したい場合は、呼び出される関数内で再度 ontimer() を呼び出す必要があります。


関数が実行されない(または一度しか実行されない)

よくある間違い

  • ontimer に渡す関数に引数を渡してしまっている(ontimer は引数なしの関数を期待します)。
  • turtle.done() が呼び出されていないため、イベントループが開始されていない。
  • ontimer が一度しか呼ばれていないのに、繰り返し実行されると期待している。

トラブルシューティング

  • 関数の引数
    ontimer に渡す関数は引数を取らない形式にしてください。もし引数を渡したい場合は、lambda 式を使うなどの工夫が必要です。

    import turtle
    
    def greet(name):
        print(f"こんにちは、{name}さん!")
    
    screen = turtle.Screen()
    # 悪い例: greet("太郎") と直接呼び出してしまうと、すぐに実行されてしまう
    # screen.ontimer(greet("太郎"), 1000)
    
    # 良い例: lambdaを使って引数なしの関数として渡す
    screen.ontimer(lambda: greet("太郎"), 1000)
    turtle.done()
    
  • turtle.done() の確認
    turtle.done() がプログラムの最後に呼び出されていることを確認してください。これは、タートルグラフィックのウィンドウが表示され続け、イベントループが開始されるために必要です。イベントループがなければ、ontimer は正しく機能しません。

  • 繰り返し実行したい場合
    ontimer で呼び出される関数の中で、再度 screen.ontimer() を呼び出すようにしてください。これがアニメーションなどの継続的な処理の基本です。

    import turtle
    
    def animate():
        turtle.forward(5)
        # 次のフレームをスケジュールする
        screen.ontimer(animate, 50) # 50ミリ秒後に再度animateを呼び出す
    
    screen = turtle.Screen()
    screen.ontimer(animate, 50) # 最初の呼び出し
    turtle.done()
    

プログラムがすぐに終了してしまう

よくある間違い

  • turtle.done() が呼び出されていない。

トラブルシューティング

  • turtle.done() を追加する
    turtle.done() は、タートルグラフィックのウィンドウが開いたままになり、ontimer などのイベントが処理されるのを待つようにします。これを呼び出さないと、Pythonスクリプトはすべてのコードを実行し終えるとすぐに終了してしまい、ontimer の設定されたタイマーが実行される前にウィンドウが閉じてしまいます。

    import turtle
    
    def show_message():
        print("メッセージを表示しました!")
    
    screen = turtle.Screen()
    screen.ontimer(show_message, 2000)
    # これがないと、2秒経つ前にプログラムが終了してしまう
    turtle.done()
    

関数が期待通りのタイミングで実行されない(遅延や実行されない)

よくある間違い

  • 他の処理が重すぎて、イベントループがブロックされている。
  • t(遅延時間)の値が非常に小さい、または非常に大きい。

トラブルシューティング

  • 重い処理の回避
    ontimer で呼び出される関数内で、非常に時間のかかる処理(例えば、重い計算や無限ループに近い処理)を行うと、イベントループがブロックされ、他のイベント(タイマーを含む)が適切に処理されなくなります。このような場合は、処理を分割したり、別のスレッドで実行することを検討してください(ただし、turtle はスレッドセーフではないため注意が必要です)。
  • t の値の調整
    遅延時間はミリ秒単位です。例えば、1秒は1000ミリ秒です。意図した通りに動作するように、t の値を調整してみてください。

NameError: name 'screen' is not defined

よくある間違い

  • turtle.Screen() オブジェクトを変数に割り当てていない、またはその変数名を間違えている。

トラブルシューティング

  • screen = turtle.Screen() のように、スクリーンオブジェクトを明確に定義し、screen.ontimer(...) のようにそのオブジェクトのメソッドとして ontimer を呼び出していることを確認してください。

    import turtle
    
    def do_something():
        print("何かしました。")
    
    # 必ずscreenオブジェクトを作成し、それを参照する
    screen = turtle.Screen()
    screen.ontimer(do_something, 1000)
    turtle.done()
    

よくある間違い

  • ontimer を呼び出す際に、実行する関数と遅延時間(t)の両方を指定していない。

トラブルシューティング

  • screen.ontimer(your_function, delay_milliseconds) の形式で、両方の引数を正しく渡していることを確認してください。

    import turtle
    
    def my_action():
        print("アクション!")
    
    screen = turtle.Screen()
    # 実行する関数と遅延時間を両方指定する
    screen.ontimer(my_action, 500) # 500ミリ秒後にmy_actionを実行
    turtle.done()
    

turtle.ontimer() を使う際のトラブルシューティングの鍵は、以下の点を理解することです。

  1. イベント駆動型プログラミング
    ontimer はイベントループの中で機能します。turtle.done() がそのイベントループを開始します。
  2. 一度限りの実行
    ontimer は一度の呼び出しにつき、関数を一度だけ実行します。繰り返し実行したい場合は、関数内で再帰的に ontimer を呼び出す必要があります。
  3. 非ブロッキング
    ontimertime.sleep() とは異なり、プログラムの実行をブロックしません。タイマーを設定した後も、他のコードは実行され続けます。


turtle.ontimer() を使ったプログラミング例

turtle.ontimer() は、指定した時間が経過した後に特定の関数を実行するためのものです。これは、アニメーション、ゲーム、時間ベースのイベントなどに非常に役立ちます。

例1:一度だけメッセージを表示する

最も基本的な使い方です。特定の時間後に一度だけ何かを実行します。

import turtle

# 画面とタートルを設定
screen = turtle.Screen()
t = turtle.Turtle()
t.hideturtle() # タートルを非表示にする

def show_message():
    """
    指定時間後に実行される関数
    """
    t.write("Hello, Turtle!", align="center", font=("Arial", 24, "normal"))
    print("メッセージが表示されました。")

# 2000ミリ秒(2秒)後にshow_message関数を実行する
screen.ontimer(show_message, 2000)

# イベントループを開始。これがなければプログラムはすぐに終了してしまう
turtle.done()

解説

  1. import turtle: turtle モジュールをインポートします。
  2. screen = turtle.Screen(): 描画する画面オブジェクトを作成します。
  3. t = turtle.Turtle(): タートルオブジェクトを作成します。
  4. t.hideturtle(): この例ではタートル自身は描画しないので、非表示にします。
  5. def show_message():: 2秒後に実行したい処理をこの関数内に記述します。ここでは、画面中央にテキストを表示し、コンソールにもメッセージを出力しています。
  6. screen.ontimer(show_message, 2000): ここが ontimer の呼び出しです。
    • show_message: 2000ミリ秒後に実行したい関数です。
    • 2000: 遅延時間です。単位はミリ秒なので、2000ミリ秒は2秒です。
  7. turtle.done(): これが非常に重要です。この関数を呼び出すことで、タートルグラフィックのウィンドウが開いたままになり、設定したタイマーイベントが処理されるのを待ちます。これを書かないと、プログラムはすぐに終了してしまい、show_message が実行される機会がありません。

例2:タートルを繰り返し動かす(アニメーションの基本)

ontimer() の最も一般的な使い方の1つは、関数内で自分自身を再帰的に呼び出すことで、アニメーションや継続的な動きを作成することです。

import turtle

screen = turtle.Screen()
t = turtle.Turtle()
t.speed(0) # 描画速度を最速にする
t.penup()  # 線を引かないようにペンを上げる
t.goto(-200, 0) # 開始位置に移動
t.pendown() # ペンを下げる

def move_forward_repeatedly():
    """
    タートルを少しずつ前進させ、自身を繰り返し呼び出す関数
    """
    t.forward(5) # 5歩前進

    # 画面の右端に到達したら方向転換
    if t.xcor() > 200:
        t.right(180) # 180度回転
    elif t.xcor() < -200:
        t.right(180) # 180度回転

    # 50ミリ秒後に再度この関数を呼び出す
    screen.ontimer(move_forward_repeatedly, 50)

# 初めての呼び出し
screen.ontimer(move_forward_repeatedly, 50)

turtle.done()

解説

  1. t.speed(0): アニメーションを滑らかに見せるために、タートルの描画速度を最大に設定します。
  2. t.penup()t.goto(-200, 0)t.pendown(): 初期位置にタートルを移動させます。
  3. def move_forward_repeatedly()::
    • t.forward(5): タートルを5歩前進させます。
    • if t.xcor() > 200: ... elif t.xcor() < -200:: タートルが画面の左右の端に達したら、180度回転して向きを変えるロジックです。
    • screen.ontimer(move_forward_repeatedly, 50): ここがポイントです。 関数内で自分自身 (move_forward_repeatedly) を再度 ontimer に登録しています。これにより、50ミリ秒ごとにこの関数が繰り返し実行され、タートルが連続して前進するアニメーションが作成されます。
  4. screen.ontimer(move_forward_repeatedly, 50): これはアニメーションを開始するための最初の ontimer の呼び出しです。
  5. turtle.done(): 同様に、イベントループを維持するために必要です。

ontimer() を使って、一定時間ごとに画面の背景色をランダムに変える例です。

import turtle
import random # ランダムな色を選ぶために必要

screen = turtle.Screen()
screen.setup(width=600, height=400) # 画面サイズを設定

# 色のリスト
colors = ["red", "green", "blue", "yellow", "purple", "orange", "cyan", "magenta"]

def change_background_color():
    """
    背景色をランダムに変える関数
    """
    # colorsリストからランダムに色を選択
    random_color = random.choice(colors)
    screen.bgcolor(random_color) # 背景色を設定
    print(f"背景色が {random_color} に変わりました。")

    # 1000ミリ秒(1秒)後に再度この関数を呼び出す
    screen.ontimer(change_background_color, 1000)

# 初めての呼び出し
screen.ontimer(change_background_color, 1000)

turtle.done()
  1. import random: ランダムな選択をするために random モジュールをインポートします。
  2. colors = [...]: 変更したい色のリストを定義します。
  3. def change_background_color()::
    • random.choice(colors): リストからランダムに色を選びます。
    • screen.bgcolor(random_color): 選択した色を背景色に設定します。
    • screen.ontimer(change_background_color, 1000): 1秒ごとにこの関数を繰り返し呼び出すことで、背景色が連続して変化します。


turtle.ontimer() の代替手段

turtle.ontimer()turtle グラフィック環境に特化したタイマー関数ですが、Pythonで時間に基づいた処理を制御する方法は他にもいくつかあります。主な代替手段は以下の通りです。

time.sleep() を使用する

最も単純な時間遅延の方法です。プログラムの実行を指定した秒数だけ一時停止させます。

利点

  • 正確な遅延時間。
  • 実装が非常に簡単。

欠点

  • turtle 環境では、アニメーションがカクカクしたり、ウィンドウが固まったりする原因になります。
  • プログラム全体をブロックする: time.sleep() が実行されている間、プログラムは一切の処理(GUIの更新、イベント処理など)を行いません。これにより、GUIアプリケーションが「フリーズ」したように見えたり、ユーザー入力に反応しなくなったりします。

使用例(turtle 環境では非推奨)

import turtle
import time

screen = turtle.Screen()
t = turtle.Turtle()

def draw_square_slowly():
    for _ in range(4):
        t.forward(100)
        time.sleep(1) # 1秒間プログラムを停止
        t.right(90)

# この関数が実行されている間、ウィンドウはフリーズします
# turtle.done() を呼び出すと、time.sleep() の前にイベントループが開始される
# ことが多いですが、sleep中は画面更新も止まります。
draw_square_slowly()

turtle.done() # ウィンドウを閉じずに待機

解説
time.sleep() は単純なスクリプトやコマンドラインアプリケーションでは問題ありませんが、GUIアプリケーション(turtle も含む)では決して使うべきではありません。これにより、ユーザーインターフェースが応答不能になります。

threading.Timer を使用する

Pythonの threading モジュールには Timer クラスがあり、指定した時間が経過した後に、別のスレッドで関数を実行できます。

利点

  • 時間のかかる処理をバックグラウンドで実行できる。
  • メインプログラムの実行をブロックしない(非同期)。

欠点

  • スレッドの概念を理解する必要がある。
  • turtle との相性: turtle モジュールはスレッドセーフではありません。つまり、複数のスレッドから同時に turtle の描画コマンドを実行すると、予期せぬ動作やエラーが発生する可能性があります。threading.Timerturtle の描画を行うのは避けるべきです。

使用例(turtle 描画には直接使わない方が良い)

import threading
import time

def delayed_action():
    print("指定時間が経過しました。")
    # ここでturtleの描画を行わない方が良い
    # 例: print(turtle.pos()) # これは危険

print("タイマーを設定中...")
# 2秒後にdelayed_action関数を別のスレッドで実行
timer = threading.Timer(2, delayed_action)
timer.start() # タイマーを開始

print("メインプログラムは続行中...")
time.sleep(3) # メインスレッドが終了しないように少し待つ
print("メインプログラム終了。")

解説
threading.Timer はメインスレッドをブロックせずに処理を実行できるため、time.sleep() の欠点を補います。しかし、turtle グラフィックスの更新はメインスレッドで行われるべきであり、threading.Timer を使って turtle の描画コマンドを呼び出すと、予測不能な問題が発生する可能性が高いです。

Tkinter (または他のGUIライブラリ) のタイマー機能を使用する

turtle モジュールは内部的にTkinterを使用しています。Tkinterには、after() メソッドという同様のタイマー機能があります。これは turtle.ontimer() と非常に似ています。

利点

  • turtle.ontimer() と同じメカニズムなので、turtle 環境と完全に互換性がある。
  • 繰り返し処理にも対応している。
  • GUIのイベントループと統合されているため、GUIがフリーズしない。

欠点

  • Tkinterを直接使う場合、turtle の高レベルな抽象化が失われる。
  • turtle を直接使う場合、turtle.ontimer() で十分であり、Tkinterの低レベルなAPIに触れる必要はあまりない。

使用例(turtle.ontimer() とほぼ同等)

import turtle
import tkinter as tk # Tkinterをインポート

# turtleは内部でTkinterのルートウィンドウを使用
screen = turtle.Screen()
root = screen._root # Tkinterのルートウィンドウを取得

t = turtle.Turtle()

def flash_color():
    current_color = t.fillcolor()
    if current_color == "red":
        t.fillcolor("blue")
    else:
        t.fillcolor("red")
    t.stamp() # タートルの形状をスタンプ
    
    # Tkinterのafter()で1秒後に再度呼び出す
    root.after(1000, flash_color)

t.shape("square")
t.shapesize(2)
t.fillcolor("red")
t.penup()

# Tkinterのafter()を呼び出す
root.after(1000, flash_color)

turtle.done()

解説
turtle.ontimer() は、実際にはこのTkinterの after() メソッドのラッパーです。したがって、Tkinterを直接操作しても同じ結果が得られますが、通常は turtle.ontimer() を使用する方がシンプルで推奨されます。

非常に特殊なケースや、GUI以外の環境で時間ベースの処理を行いたい場合、自分でループを回し、現在の時刻をチェックして、特定の条件が満たされたら処理を実行するという方法もあります。

利点

  • GUIがない環境でも使える。
  • 完全に制御可能。

欠点

  • turtle 環境では、この方法もGUIをブロックする可能性が高い。
  • 正確な時間制御が難しい場合がある。
  • CPUを消費する可能性がある: 適切な遅延を入れないと、ループがCPUを占有してしまう。

使用例(GUIなしの環境向け)

import time

start_time = time.time()
interval = 2 # 2秒ごとに何かをする

while True:
    current_time = time.time()
    if current_time - start_time >= interval:
        print(f"{interval}秒が経過しました。現在の時刻: {time.ctime()}")
        start_time = current_time # 次のインターバルのために時間をリセット
    
    # CPU使用率を下げ、他の処理に譲る
    time.sleep(0.1) # 短い間だけ待機

    # 例として、一定回数実行したら終了
    if current_time > start_time + 10: # 10秒後には終了する
        break

print("ループ終了。")

解説
この方法はGUIアプリケーションには適していません。turtle のようなGUI環境でこれを試みると、time.sleep() が短時間であっても、他のイベントが処理されずにウィンドウがフリーズしたり、応答が遅くなったりする原因になります。

  • 自作のループと時間チェックは、GUIがない環境や、より低レベルな制御が必要な場合にのみ検討すべきです。
  • Tkinterの after() メソッドは turtle.ontimer() と同等ですが、通常は turtle.ontimer() で十分です。
  • threading.Timer はメインスレッドをブロックしませんが、turtle の描画コマンドを別のスレッドから呼び出すと問題が発生する可能性が高いため、推奨されません。
  • time.sleep() はGUIアプリケーションでは避けるべきです。
  • turtle 環境で時間ベースの処理を行う場合は、turtle.ontimer() が最も推奨される、かつ最も適切な方法です。 これは turtle のイベントループと完全に統合されており、GUIをブロックすることなくアニメーションやイベントをスムーズに処理できます。