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

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

ただいまの
回答率

90.83%

  • Python 3.x

    4887questions

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

Pythonでキーが押されたことを判定したい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 143

yoko4401

score 1

 前提・実現したいこと

Python初心者です。2択クイズゲームを作ろうと思っています。マウス操作に慣れていない方でも使えるようにキーボードからの入力で回答させたいので、とりあえずキーを判定する部分を作ってみました。aキーかbキーが押されたことを確認するためにprint(key)でkeyの値を取り出そうとしているのですがうまくいきません。
最終的にprint(key)でkeyの値を取り出したいのですがどうすればよいですか。

 発生している問題・エラーメッセージ

def keyA(event):でaキーを押されたときkey="A"を代入するようにしておき、frame.bind("a",keyA)でaキーが押された時にkeyAを呼び出して、それをprint(key)で取り出したいが取り出し方がわからない。

 該当のソースコード

Python3.6

 -*- coding: utf8 -*-

import tkinter as tk
import winsound

root = tk.Tk()
frame = tk.Canvas(root, width=800, height=600)
pimg = tk.PhotoImage(file="wall.png")
frame.place(x=0, y=0)
frame.create_image(400, 300, image=pimg)

imgA = tk.PhotoImage(file = 'pressed_A.png')
imgB = tk.PhotoImage(file = 'Pressed_B.png')

def keyA(event):
<indent> global key
<indent> key="A"
<indent> frame.place(x=0, y=0)
<indent> frame.create_image(320, 220, image=imgA, tag="illust")
<indent> root.update()
<indent> winsound.PlaySound("pressed_A.wav", winsound.SND_FILENAME)

def keyB(event):
<indent> global key
<indent> key="B"
<indent> frame.place(x=0, y=0)
<indent> frame.create_image(320, 220, image=imgB, tag="illust")
<indent> root.update()
<indent> winsound.PlaySound("pressed_B.wav", winsound.SND_FILENAME)

frame.bind("a", keyA)
frame.focus_set()
frame.pack()

frame.bind("b", keyB)
frame.focus_set()
frame.pack()

print(key)

root.mainloop()

 試したこと

keyイベント内でprint(key)を表示させることはできるが、メインの実行時にキーを判定できない。タイマーイベントで対応するときはどうすればよいでしょうか。
または、キー判定のためにまったく別の良い方法があれば教えてください。

 補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • KSwordOfHaste

    2018/06/01 19:35

    本サイトでプログラムコードを記載するにはマークダウン記法を使う必要があります。特にpythonは字下げを正確に再現していただかないとコードが正しく伝わりません。「https://teratail.com/help#about-markdown」の「コードを入力」を参考にして質問を編集し、より正確なコード内容が閲覧者に伝わるようご協力ください。

    キャンセル

回答 2

checkベストアンサー

+1

tkinterの動作モデルは所謂イベント駆動型になっており、ウィンドウはroot.mainloop()を実行したときに初めて表示されそれ以降はウィンドウの操作をしている間中ずっとこのmainloop関数から返ってこないという特徴を持ってます。

このイベント駆動型の制御方式はGUIプログラミングではおなじみなので、プログラムの制御方法を「上から下へ順番に実行する」という発想から「何か操作されたら個々の関数が呼び出される」という発想に切り替える必要があります。

ではありますが、Pythonでtkinterを用いる場合、次のようなコードで非イベント駆動型の制御論理を書くこともできてしまうようです。これが常に適切に動作してくれるかどうか少々自身ないですが・・・

import tkinter as tk
import threading
import time


root = tk.Tk()
canvas = tk.Canvas(root, width=800, height=600)
canvas.place(x=0, y=0)


def keyA(event):
    global key
    key="A"
    # root.update()

def keyB(event):
    global key
    key="B"
    # root.update()

canvas.bind("a", keyA)
canvas.bind("b", keyB)
canvas.focus_set()
canvas.pack()

def main():
    # 制御論理を無理やり逐次実行的に書いてみる
    global key

    while not stop:
        time.sleep(0.1)  # 最初この行を入れ忘れてました!!!
        if key is not None:
            print('pressed {}'.format(key))
            if key == 'A':
                color = '#FF0000'
            elif key == 'B':
                color = '#00FF00'
            canvas.create_rectangle((10, 10), (20, 20), width=0, fill=color)
            key = None

key, stop = None, False
game_thread = threading.Thread(target=main)
game_thread.start()

root.mainloop()

stop = True
game_thread.join()
print('end of execution')

ポイントはmainloopの実行前に主たる制御論理(上の例ではmain関数)を別のスレッドで動かすことです。メインスレッドではtkinterの通常の動作(イベント駆動型)が行われておりずっとmainloop関数が動きっぱなしになります。それとは別のスレッドでmain関数が実行され、keyA, keyBで押されたキーをグローバル変数に格納するとmain関数の中でそれを検出して何かするという感じですね。
実験のためメインスレッドと別のスレッドでtkinterのキャンバス上に描画してみましたが、動くように見えました。他の言語のGUIライブラリーだと複数のスレッドから画面を触るのは「いけません」てことになっているのですが、PythonのGIL(Global Interpreter Lock)の仕様のためそのあたりの制限はゆるーくなっているのかも知れません(もしかたしたらダメなのかも知れません。自身ないです)

しかし、上のように書くことはできますがこう書いたからといってプログラミングしやすいかは少々疑問です。普通にイベント駆動型でプログラミングしてもよい気がするのですが・・・どうでしょうか?

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/01 23:20

    すみません、最初のコード例でwhile文のところにsleep入れるのを忘れておりました。それだとCPUを使いまくるのでよくない例だと思います。sleepを追加しておきました。

    キャンセル

0

キー判定のためにまったく別の良い方法 -> keyboardパッケージ

import keyboard
keyboard.on_press_key("a", print)
keyboard.on_press_key("b", print)

pipでkeyboardパッケージをインストールしてから、上記を実行してaキーやbキーを打ってみてください。ドキュメントはココです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/01 23:17

    ありがとうございました。特にプログラムにこだわりはなく、シンプルなほうが大歓迎なのですが、
    keyboardパッケージをインストールした後、実行したのですが何も起こりませんでした。
    当方のスキル不足かと思われますので、教えていただいた解説を参考にもう少し勉強したいと思います。
    上手く動けば、もしかしたらこちらのほうを採用するかもしれません。もう少し頑張ってみます。
    アドバイスがあればまた、ご教唆ください。

    キャンセル

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

  • ただいまの回答率 90.83%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Python 3.x

    4887questions

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