turtle.onkeyrelease()

2025-06-06

以下に、より詳しく説明します。

turtleモジュールは、グラフィック描画を簡単に行うためのモジュールで、"タートル"と呼ばれる小さな矢印が画面上を動き回ることで線を描きます。キーボードイベント(キーが押されたり離されたりすること)を検知し、それに応じてタートルを動かすなどのインタラクティブなプログラムを作成する際にonkeyrelease()が役立ちます。

構文

turtle.onkeyrelease(fun, key)
  • key: 監視するキーの名前を文字列で指定します。例えば、"space"(スペースキー)、"Up"(上矢印キー)、"a"(Aキー)などです。大文字・小文字は区別されます。
  • fun: キーが離されたときに呼び出される関数を指定します。この関数は引数をとりません。

動作原理

  1. turtle.onkeyrelease(fun, key)を呼び出すことで、Pythonプログラムは指定されたkeyが離されるのを監視するようになります。
  2. ユーザーがそのkeyを離すと、turtleモジュールは自動的にfunで指定された関数を呼び出します。
  3. これにより、キーが離されるたびに特定の処理を実行することができます。

使用例

以下に、turtle.onkeyrelease()の具体的な使用例を示します。スペースキーが離されるたびにタートルが前進するプログラムです。

import turtle

# タートルオブジェクトを作成
t = turtle.Turtle()
screen = turtle.Screen()

# キーが離されたときに実行される関数
def move_forward():
    t.forward(50) # タートルを50ピクセル前進させる

# イベントリスナーを設定
# スペースキーが離されたらmove_forward関数を実行
screen.onkeyrelease(move_forward, "space")

# キーイベントの待機を開始
screen.listen()

# ウィンドウを閉じないようにする(オプション)
turtle.done()

このコードの説明

  • turtle.done(): turtleグラフィックウィンドウが開いたままになり、ユーザーが閉じるまでプログラムが終了しないようにします。
  • screen.listen(): キーボードイベントを監視するために、これを呼び出す必要があります。これを呼び出さないと、onkeyrelease()は機能しません。
  • screen.onkeyrelease(move_forward, "space"): ここがonkeyrelease()の核心です。screenオブジェクトに対して、「"space"キーが離されたらmove_forward関数を実行してね」と指示しています。
  • def move_forward():: スペースキーが離されたときに呼び出される関数を定義します。この関数では、タートルを50ピクセル前進させています。
  • screen = turtle.Screen(): 描画ウィンドウ(スクリーン)オブジェクトを取得します。
  • t = turtle.Turtle(): 描画を行うタートルオブジェクトを作成します。
  • import turtle: turtleモジュールをインポートします。

onkeypress()との違い

turtleモジュールにはonkeypress()という似た機能もあります。

  • onkeyrelease(fun, key): 指定されたキーが離されたときfunを実行します。キーを押しっぱなしにしても、離されるまでfunは実行されません。
  • onkeypress(fun, key): 指定されたキーが押されたときfunを実行します。キーを押しっぱなしにすると、連続してfunが実行されることがあります(キーのリピートレートによる)。

用途に応じてこれらを使い分けることで、より柔軟なキーボード操作を実現できます。



AttributeError: 'Turtle' object has no attribute 'onkeyrelease'

これは最もよくあるエラーの一つです。

原因
onkeyrelease()メソッドは、タートルオブジェクト (turtle.Turtle()) ではなく、スクリーンオブジェクト (turtle.Screen()) のメソッドです。タートルオブジェクトに対してonkeyrelease()を呼び出そうとすると、このエラーが発生します。

例 (誤り)

import turtle
t = turtle.Turtle()
# 誤り: タートルオブジェクトに対してonkeyreleaseを呼び出している
t.onkeyrelease(my_function, "space")

解決策
必ずスクリーンオブジェクトに対してonkeyrelease()を呼び出してください。

例 (正しい)

import turtle
t = turtle.Turtle()
screen = turtle.Screen() # スクリーンオブジェクトを取得

def my_function():
    t.forward(10)

screen.onkeyrelease(my_function, "space") # スクリーンオブジェクトに対して呼び出す
screen.listen()
turtle.done()

キーイベントが全く発生しない、関数が呼び出されない

onkeyrelease()を設定したにもかかわらず、キーを離しても何も起こらない場合です。

原因と解決策

  • 関数に括弧をつけて呼び出してしまっている
    onkeyrelease()に渡す関数は、関数オブジェクトそのものであり、その場で関数を実行するものではありません。もし関数名に続けて()をつけてしまうと、onkeyrelease()が呼び出される前に一度関数が実行されてしまい、イベント時には何も起こりません。

    例 (誤り)

    screen.onkeyrelease(my_function(), "c") # my_functionが即座に実行されてしまう
    

    解決策
    関数名のみを渡します。

    例 (正しい)

    screen.onkeyrelease(my_function, "c")
    

    もし、イベント時に引数を伴う関数を呼び出したい場合は、lambda式を使用します。

    def move_distance(dist):
        t.forward(dist)
    
    screen.onkeyrelease(lambda: move_distance(100), "d")
    
  • キー名のスペルミス
    "space""Up""Down""Left""Right"などの特殊なキー名は大文字・小文字を区別します。また、一般的な文字キーは通常小文字で指定します(例: "a")。 解決策: 正しいキー名が使用されているか確認してください。公式ドキュメントや信頼できる情報源でキー名のリストを参照することをお勧めします。

  • キーボードのフォーカスがない
    turtleグラフィックウィンドウにキーボードのフォーカス(アクティブなウィンドウ)がない場合、キー入力はプログラムに届きません。 解決策: プログラムを実行した後、turtleウィンドウをマウスでクリックしてアクティブにしてみてください。

  • turtle.done()やscreen.mainloop()の呼び出し忘れ
    Pythonスクリプトが終了してしまうと、turtleウィンドウも閉じられ、イベントを処理するループが動作しません。 解決策: プログラムの最後にturtle.done()(またはscreen.mainloop())を呼び出し、イベントループが継続的に実行されるようにします。これにより、ウィンドウが開いたままになり、キーイベントが処理されます。

    import turtle
    t = turtle.Turtle()
    screen = turtle.Screen()
    
    def my_function():
        t.color("red")
    
    screen.onkeyrelease(my_function, "b")
    screen.listen()
    turtle.done() # または screen.mainloop()
    
  • screen.listen()の呼び出し忘れ
    turtleモジュールは、キーボードイベントを「聞く」ためにscreen.listen()を呼び出す必要があります。これを呼び出さないと、イベントは検知されません。 解決策: onkeyrelease()を設定した後、必ずscreen.listen()を呼び出してください。

    import turtle
    t = turtle.Turtle()
    screen = turtle.Screen()
    
    def my_function():
        t.circle(50)
    
    screen.onkeyrelease(my_function, "a")
    screen.listen() # これが必須
    turtle.done()
    

グローバル変数の問題

イベントハンドラ関数内でグローバル変数を変更しようとした場合、意図したように動作しないことがあります。

原因
関数内で新しい変数を代入しようとすると、Pythonはデフォルトでそれをローカル変数として扱います。もし同名のグローバル変数を変更しようとしている場合、その変更はグローバル変数には反映されません。


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

speed = 10 # グローバル変数

def increase_speed():
    speed += 5 # エラー: ローカル変数として扱われるため、グローバル変数を変更できない

screen.onkeyrelease(increase_speed, "Up")
screen.listen()
turtle.done()

解決策
関数内でグローバル変数を変更する場合は、globalキーワードを使用して、それがグローバル変数であることを明示的に宣言する必要があります。

例 (正しい)

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

speed = 10 # グローバル変数

def increase_speed():
    global speed # globalキーワードで宣言
    speed += 5
    print(f"現在のスピード: {speed}")

def move():
    t.forward(speed)
    screen.ontimer(move, 20) # アニメーションループ

screen.onkeyrelease(increase_speed, "Up")
screen.listen()
move() # アニメーションを開始
turtle.done()

一部のIDE(特にPyCharm)では、turtleモジュールの動作が期待通りにならないことがあります。

原因

  • Tkinterのサポートがない
    turtleモジュールはTkinterを基盤としているため、PythonのインストールにTkinterのサポートが含まれていない場合、グラフィックウィンドウが表示されません。
  • ファイル名がturtle.pyである
    自分のPythonスクリプトファイル名をturtle.pyとすると、Pythonは標準のturtleモジュールではなく、自分で作成したturtle.pyファイルをインポートしようとします。これにより、必要な関数が見つからずにエラーが発生します。

解決策

  • Tkinterのインストールを確認する
    必要であれば、Pythonを再インストールするか、apt-get install python3-tk (Linux) やHomebrew (macOS) などでTkinterパッケージを追加してください。
  • ファイル名を変更する
    turtle.py以外の名前に変更してください(例: my_turtle_app.py)。


例1: スペースキーを離したらタートルが前進する

最も基本的な例です。

import turtle

# 1. タートルとスクリーンをセットアップ
t = turtle.Turtle()
screen = turtle.Screen()
screen.setup(width=600, height=400) # ウィンドウサイズを設定
screen.title("スペースキーで前進")

# 2. キーが離されたときに実行される関数を定義
def move_forward():
    """タートルを前進させる関数"""
    t.forward(50)
    print("スペースキーが離されました!前進しました。")

# 3. onkeyrelease()でイベントを登録
# "space"キーが離されたらmove_forward関数を実行する
screen.onkeyrelease(move_forward, "space")

# 4. キーイベントを監視開始
screen.listen()

# 5. ウィンドウを閉じないようにする
turtle.done()

解説

  • turtle.done(): turtleグラフィックウィンドウが開いたままになり、イベント処理が継続します。
  • screen.listen(): これを呼び出すことで、キーボードイベントの監視が開始されます。これがないと、onkeyrelease()は機能しません。
  • screen.onkeyrelease(move_forward, "space"): ここがポイントです。"space"キーが離された(放された)ときに、move_forwardという名前の関数が実行されるように設定しています。

例2: 矢印キーを離したらタートルが特定の方向に回転する

複数のキーイベントを扱う例です。

import turtle

t = turtle.Turtle()
screen = turtle.Screen()
screen.setup(width=600, height=400)
screen.title("矢印キーで回転(キーを離したとき)")
t.speed(0) # 描画速度を最速に設定

# 各方向への回転関数を定義
def turn_left():
    """タートルを左に90度回転させる"""
    t.left(90)
    print("左矢印キーが離されました。")

def turn_right():
    """タートルを右に90度回転させる"""
    t.right(90)
    print("右矢印キーが離されました。")

def turn_up():
    """タートルを上(0度)に向ける"""
    t.setheading(90) # 上方向は90度
    print("上矢印キーが離されました。")

def turn_down():
    """タートルを下(180度)に向ける"""
    t.setheading(270) # 下方向は270度(または-90度)
    print("下矢印キーが離されました。")

# 各キーにonkeyrelease()を設定
screen.onkeyrelease(turn_left, "Left")
screen.onkeyrelease(turn_right, "Right")
screen.onkeyrelease(turn_up, "Up")
screen.onkeyrelease(turn_down, "Down")

screen.listen()
turtle.done()

解説

  • それぞれのキーが離されたときに異なる関数が実行されるように設定しています。
  • "Left", "Right", "Up", "Down"といった特殊なキー名は、先頭が大文字であることに注意してください。

例3: キーを離したらタートルの色が変わる(引数を持つ関数をlambdaで渡す)

onkeyrelease()に直接引数を渡すことはできませんが、lambda式を使うことで引数を持つ関数を実行できます。

import turtle

t = turtle.Turtle()
screen = turtle.Screen()
screen.setup(width=600, height=400)
screen.title("キーを離したら色変更")

# タートルの色を変更する関数(引数color_nameを受け取る)
def change_color(color_name):
    t.pencolor(color_name) # ペンの色
    t.fillcolor(color_name) # 塗りつぶしの色
    print(f"キーが離されました。色を {color_name} に変更しました。")

# 各キーにonkeyrelease()を設定(lambdaを使用)
# "r"キーを離したら赤に、"g"なら緑に、"b"なら青に
screen.onkeyrelease(lambda: change_color("red"), "r")
screen.onkeyrelease(lambda: change_color("green"), "g")
screen.onkeyrelease(lambda: change_color("blue"), "b")

screen.listen()
turtle.done()

解説

  • lambda: change_color("red") のように記述することで、onkeyrelease()が呼び出されたときに、lambda式がchange_color("red")を実行するように指定しています。これは、「引数なしの匿名関数を定義し、その中で引数ありの関数を呼び出す」というテクニックです。
  • change_color(color_name)は引数を持つ関数です。

キーが離されたときに描画状態を切り替える例です。

import turtle

t = turtle.Turtle()
screen = turtle.Screen()
screen.setup(width=600, height=400)
screen.title("描画の切り替え")
t.speed(3)

# 描画状態を管理するフラグ
drawing_active = False

# キーが離されたときに描画状態を切り替える関数
def toggle_drawing():
    global drawing_active # グローバル変数を変更するために必要
    drawing_active = not drawing_active # TrueとFalseを反転
    if drawing_active:
        t.pendown() # ペンを下ろす(描画開始)
        print("描画開始!")
    else:
        t.penup() # ペンを上げる(描画停止)
        print("描画停止。")

# アニメーションループ(常にタートルを動かす)
def animate():
    t.forward(5) # 少しずつ前進
    screen.ontimer(animate, 50) # 50ミリ秒ごとに自分自身を呼び出す

# "space"キーが離されたらtoggle_drawing関数を実行
screen.onkeyrelease(toggle_drawing, "space")

screen.listen()
animate() # アニメーションを開始
turtle.done()
  • animate()関数とscreen.ontimer():これはキー入力に関わらず、タートルを常に動かし続けるための一般的なアニメーション手法です。これにより、ユーザーがキーを離して描画状態を切り替えることで、タートルが動きながら線を描いたり、描かずに移動したりする様子を確認できます。
  • t.pendown()t.penup():これらでタートルのペンの状態(描画するかしないか)を切り替えます。
  • toggle_drawing()関数内でglobal drawing_activeと宣言することで、関数内からグローバル変数drawing_activeの値を変更できるようになります。
  • drawing_active = False:グローバル変数として、現在の描画状態を保持します。


turtle モジュール自体には、onkeyrelease() と直接的に入れ替えられるような全く同じ機能を持つ代替メソッドは存在しません。しかし、Pythonの他のモジュールや、turtleモジュール内の別のイベントハンドリング機能と組み合わせることで、似たような効果を実現したり、より複雑なインタラクションを構築したりすることができます。

主に以下の2つのアプローチが考えられます。

  1. turtle.onkeypress() と状態管理を組み合わせる
  2. tkinter モジュールを直接利用する (turtleがTkinter上に構築されているため)

turtle.onkeypress() と状態管理を組み合わせる

turtle.onkeypress() はキーが「押されたとき」に発火するイベントですが、これとプログラム内でキーの状態を管理する変数(フラグ)を組み合わせることで、キーが「離された」状況をシミュレートしたり、押しっぱなしと離す動作を区別したりすることができます。

基本的な考え方

  • メインループ(ontimerなどで実現されるアニメーションループ)の中で、これらのフラグの状態をチェックし、フラグがFalseになった瞬間に(つまりキーが離された瞬間に)特定の処理を実行する。
  • キーが離されたとき (onkeyrelease) に、そのキーが「押されていない」状態を示すフラグをFalseにする。
  • キーが押されたとき (onkeypress) に、そのキーが「押されている」状態を示すフラグをTrueにする。

例: スペースキーが離されたときに一度だけ処理を実行する

この例では、onkeyrelease() を直接使う場合とほぼ同じ結果になりますが、より複雑な状態管理の足がかりになります。

import turtle

t = turtle.Turtle()
screen = turtle.Screen()
screen.setup(width=600, height=400)
screen.title("onkeyrelease() の代替 (onkeypress + フラグ)")

space_key_pressed = False # スペースキーが押されているかどうかの状態

def handle_space_press():
    """スペースキーが押されたときに呼び出される"""
    global space_key_pressed
    if not space_key_pressed: # 押しっぱなしで何度も実行されないように
        space_key_pressed = True
        print("スペースキーが押されました。")

def handle_space_release_simulated():
    """スペースキーが離されたと検出されたときに呼び出される"""
    global space_key_pressed
    if space_key_pressed: # 離された状態から再度離されても実行されないように
        space_key_pressed = False
        t.forward(50) # ここにonkeyrelease()と同じ処理を書く
        print("スペースキーが離されました!(代替処理)タートルが前進。")

# onkeyrelease() と onkeypress() を併用する例
screen.onkeypress(handle_space_press, "space")
screen.onkeyrelease(handle_space_release_simulated, "space") # ここは本来のonkeyrelease()を使用

screen.listen()
turtle.done()

解説

  • より複雑な代替策として、onkeypress() でフラグを立て、メインループ内でそのフラグがFalseになった瞬間(つまりキーが離された瞬間)を検出して処理を行う、といった方法が考えられます。しかし、これはポーリング(定期的な状態チェック)が必要になり、リアルタイム性はonkeyrelease()に劣ります。
  • この例では、onkeyrelease() を実際に使っていますが、もしonkeyrelease() が使えない状況であれば、onkeypress() だけを使って「キーが離された」状態を検出する方法を考えることができます。

onkeypress()のみで「キーが離された」をシミュレートする(より複雑なアプローチ)

これは、onkeyrelease()が利用できない(あるいは意図的に避けたい)場合に検討されるアプローチです。キーが押されたタイムスタンプを記録し、ある一定時間キーが押されていない場合に「離された」と判断する、といった方法になりますが、実装が複雑になり、ユーザー体験も悪くなる可能性があります。通常はonkeyrelease()を使うべきです。

turtle モジュールは、内部的にPythonの標準GUIライブラリであるtkinter を利用してグラフィックウィンドウを生成しています。したがって、tkinter のイベントハンドリング機能を直接利用することで、より低レベルで柔軟なキーイベントの制御が可能になります。

tkinter のウィジェット(ウィンドウなど)は、bind() メソッドを使ってイベントをバインドできます。キーイベントには <KeyPress><KeyRelease> があります。

例: tkinterbind() を使う

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

# turtleのセットアップ
t = turtle.Turtle()
screen = turtle.Screen()
screen.setup(width=600, height=400)
screen.title("tkinterによる代替")

# turtleのrootウィンドウ(tkinterのTkオブジェクト)を取得
root = screen._root

# キーが離されたときに実行される関数
def tkinter_key_release(event):
    """tkinterのキーリリースイベントハンドラ"""
    print(f"Tkinterで '{event.keysym}' キーが離されました。")
    if event.keysym == "space":
        t.forward(50)
        print("タートルが前進!")
    elif event.keysym == "Up":
        t.setheading(90)
        print("タートルが上を向きました!")

# rootウィンドウにキーリリースイベントをバインド
# <KeyRelease> は任意のキーが離されたときに発火
# <KeyRelease-space> のように特定のキーを指定することも可能
root.bind("<KeyRelease>", tkinter_key_release)

# turtleのイベントリスナーは不要(tkinterが直接イベントを処理するため)
# screen.listen() は呼び出さない

# tkinterのメインループを開始
# turtle.done() は内部的にscreen.mainloop()を呼び出すが、
# tkinterのイベントを直接扱う場合はtk.mainloop()がより適切
# screen.mainloop() でも動作はする
screen.mainloop()

解説

  • screen.mainloop() または root.mainloop(): tkinterのイベントループを開始し、ウィンドウが開いたままイベントを処理し続けます。
  • event.keysym: イベントオブジェクトのkeysym属性は、どのキーが押されたか(または離されたか)を示す文字列です(例: "space", "Up", "a"など)。
  • root.bind("<KeyRelease>", tkinter_key_release): これがtkinterでのイベントバインドの核心です。
    • "<KeyRelease>": これはイベントシーケンスと呼ばれるもので、「キーが離された」というイベントを表します。特定のキーを指定したい場合は、例えば"<KeyRelease-space>"のように書きます。
    • tkinter_key_release: イベントが発生したときに呼び出される関数です。tkinterのイベントハンドラは、イベントオブジェクトを引数として受け取ります。
  • root = screen._root: turtle.Screenオブジェクトの内部には、基盤となるtkinter.Tkオブジェクトが_root属性として格納されています。これにアクセスしてtkinterの機能を使います。
  • 欠点
    • turtleモジュールが提供する高レベルな抽象化から離れるため、コードが複雑になる可能性がある。
    • tkinterのイベント駆動プログラミングの知識が必要になる。
  • 利点
    • より低レベルなイベント制御が可能になる。
    • tkinterが提供する他のGUIウィジェット(ボタン、テキストボックスなど)との統合が容易になる。
    • turtleモジュールのイベントハンドリングでは実現できないような複雑なイベント処理(例: Ctrl+Cなどの組み合わせキー)も可能になる。
  • tkinterの直接利用
    turtleの提供するイベントハンドリングだけでは不十分な、より高度で複雑なGUIアプリケーションを構築したい場合に強力な選択肢となりますが、学習コストがかかります。
  • 代替手段としてのonkeypressと状態管理
    onkeyrelease()を使えない状況や、キーの押しっぱなしと離す動作を細かく制御したい場合に検討されますが、onkeyrelease()があるなら通常は不要です。
  • 最も推奨される方法
    turtle.onkeyrelease() を直接使うのが最も簡単で意図が明確です。特別な理由がない限り、これを使うべきです。