ChatGPTでプログラミング学習ー02

tkinterを使ってみる

1.Hello, World!

前報でPythonのturtle graphicsの経験を報告しました.。今回はPythonのGUI(Graphic User Interface)についての体験学習です・
まずは、GUIのイメージつくりにサンプルプログラムをリクエストしてみました。

おなじみの Hello,World! ですね

windowの大きさを設定していないので、小さな可愛いHello,World!でした。文字はラベルの上に書くのですね。

2.ボタン、ラベル、テキストボックスの使用例

botton.click という関数を定義していますね
このコードでは、button_click()関数を作成し、ボタンがクリックされた時に呼び出されます。textbox.get()を使用してテキストボックスに入力されたテキストを取得し、label.config(text=text)を使用してラベルに設定します。また、commandオプションを使用して、ボタンがクリックされた時にbutton_click()関数を呼び出すように設定しています。
text=textbox.get() -> label.config(text=text)->command=button_click

の一連の流れを覚えておきましょう。結果の図を下に示します。Windowの大きさを300x100とした場合です。

初期状態
こんにちはを入力後、表示ボタンをクリック

こんな質問もしました。

位置ではなく、方向というのが味噌ですかね
LEFTをRIGHT等に変えてみました

結構使いこなすのは難しい気もしますね。

3.ジャンケン

前にpygameでジャンケンをやって、結構難しかった。今回、ボタンを使えば簡単そうだったので、再度チャレンジした。ジャンケンのルールはすでにtkinterは知っていました。

3個のボタンは(x,y)で配置されますが、必ずしも正しい一ではありませんね
とのことですが、いまひとつわかりにくいですね
右のぼたんが切れてしまってますね。隙間も空いています。

必ずしもピクセル単位でレイアウトを決めるのではなく、テキスト文などと同じく文字数で幅などは決めていることがわかりました。
一寸ボタンが小さいなどの不満はありますが、最終的には適宜決めるということで次に進みます。

相手のボタンを設定し、ランダムに手が決まるようにお願いしました
import tkinter as tk
import random

# ボタンがクリックされたときの処理
def btn_click(hand):
    # プレイヤーの手を取得
    player_hand = hand
    # コンピュータの手をランダムに取得
    hands = ["グー", "チョキ", "パー"]
    computer_hand = random.choice(hands)
    # 結果を表示
    result_label.configure(text=f"あなたは {player_hand} を出しました\nコンピュータは {computer_hand} を出しました")

# ウィンドウを作成
window = tk.Tk()
window.title("じゃんけんゲーム")
window.geometry("300x500")

# ラベルを作成
result_label = tk.Label(window, text="", font=("Helvetica", 14))
result_label.place(x=10, y=100)

# ボタンを作成
gu_btn = tk.Button(window, text="グー", font=("Helvetica", 14), command=lambda: btn_click("グー"))
choki_btn = tk.Button(window, text="チョキ", font=("Helvetica", 14), command=lambda: btn_click("チョキ"))
pa_btn = tk.Button(window, text="パー", font=("Helvetica", 14), command=lambda: btn_click("パー"))
gu_btn.place(x=20, y=400)
choki_btn.place(x=120, y=400)
pa_btn.place(x=220, y=400)

# ウィンドウを表示
window.mainloop()
今回はボタンの位置をピクセル単位で指定しているようですね
初期状態です。ボタンの配置はとりあえずOKとします。
下のボタンのグーをクリックすると上のラベルに自分の手と相手の手の状態が出されました。

とりあえずは相手の手の表示をしてもらいたいとこです。下方の3個の一つをクリックすると相手の手も決まります。ここまではできています。

ちょっとうまく表示されませんでした。話し方を変えて次に進みました

一気にここまでのお願いをしました。
import tkinter as tk
import random

# ボタンがクリックされたときの処理
def btn_click(hand):
    # プレイヤーの手を取得
    player_hand = hand
    # コンピュータの手をランダムに取得
    hands = ["グー", "チョキ", "パー"]
    computer_hand = random.choice(hands)
    
    # 勝敗の判定
    if player_hand == computer_hand:
        result_label.configure(text="引き分け", bg="yellow")
        player_btn.configure(bg="yellow")
        computer_btn.configure(bg="yellow")
    elif (player_hand == "グー" and computer_hand == "チョキ") or \
         (player_hand == "チョキ" and computer_hand == "パー") or \
         (player_hand == "パー" and computer_hand == "グー"):
        result_label.configure(text=f"あなたの勝ち!({player_hand} vs {computer_hand})", bg="blue")
        player_btn.configure(bg="blue")
        computer_btn.configure(bg="red")
    else:
        result_label.configure(text=f"コンピュータの勝ち…({computer_hand} vs {player_hand})", bg="red")
        player_btn.configure(bg="red")
        computer_btn.configure(bg="blue")

# ウィンドウを作成
window = tk.Tk()
window.title("じゃんけんゲーム")
window.geometry("300x500")

# ラベルを作成
result_label = tk.Label(window, text="相手の手", font=("Helvetica", 14), bg="white", width=20, height=2)
result_label.place(x=50, y=100)

# ボタンを作成
player_btn = tk.Button(window, text="グー", font=("Helvetica", 14), command=lambda: btn_click("グー"), width=8, height=2)
computer_btn = tk.Button(window, text="?", font=("Helvetica", 14), state="disabled", width=8, height=2)
player_btn.place(x=20, y=400)
computer_btn.place(x=220, y=400)

# ウィンドウを表示
window.mainloop()
初期状態です
グーをクリックしたとき

ボタンの配置がおかしいですが、一応ジャンケンをうまくやっているようですね。もう一押しですね(^^♪

再度のお願いです
import tkinter as tk
import random

def play_game():
    # コンピュータの手をランダムに選択
    hands = ['グー', 'チョキ', 'パー']
    computer_hand = random.choice(hands)

    # プレイヤーが選択した手を取得
    player_hand = player_var.get()

    # 結果を判定
    result_label.config(fg='black')
    if player_hand == computer_hand:
        result_label.config(text='引き分け', fg='yellow')
    elif (player_hand == 'グー' and computer_hand == 'チョキ') or \
         (player_hand == 'チョキ' and computer_hand == 'パー') or \
         (player_hand == 'パー' and computer_hand == 'グー'):
        result_label.config(text='勝ち', fg='blue')
    else:
        result_label.config(text='負け', fg='red')

# ウィンドウを作成
root = tk.Tk()
root.title('じゃんけん')

# プレイヤーの手を選択するためのボタンを作成
player_var = tk.StringVar()
player_var.set('グー')

rock_btn = tk.Radiobutton(root, text='グー', variable=player_var, value='グー')
scissors_btn = tk.Radiobutton(root, text='チョキ', variable=player_var, value='チョキ')
paper_btn = tk.Radiobutton(root, text='パー', variable=player_var, value='パー')

rock_btn.pack(side='left', padx=10, pady=10)
scissors_btn.pack(side='left', padx=10, pady=10)
paper_btn.pack(side='left', padx=10, pady=10)

# コンピュータの手を表示するラベルを作成
computer_label = tk.Label(root, text='相手の手')
computer_label.place(x=10, y=100)

computer_var = tk.StringVar()
computer_var.set('-')

computer_hand_label = tk.Label(root, textvariable=computer_var)
computer_hand_label.place(x=10, y=200)

# 結果を表示するラベルを作成
result_label = tk.Label(root, text='結果')
result_label.place(x=10, y=250)

# プレイヤーが手を選択した後にじゃんけんをするためのボタンを作成
play_btn = tk.Button(root, text='じゃんけん', command=play_game)
play_btn.place(x=10, y=300)

root.geometry('300x500')
root.mainloop()

scssということで上記を提案してくれました。Radiobuttonを使用しています。
結果は下記のようになります。

ちょっとChatGTPとコミュニケーションが取れなくなってきた感じです。このプログラムはRadiobuttonに自分の手を設定してから「ジャンケン・ポン」といった感じでしょうか。それはそれでありかもしれませんが、事前に手を明かすというのはあまり感心しません。

修正をお願いして、次のコードを出してくれました。

import tkinter as tk
import random

# ボタンがクリックされたときの処理
def btn_click(hand):
    # プレイヤーの手を取得
    player_hand = hand
    # コンピュータの手をランダムに取得
    hands = ["グー", "チョキ", "パー"]
    computer_hand = random.choice(hands)
    
    # 勝敗の判定
    if player_hand == computer_hand:
        result_label.configure(text="引き分け", bg="yellow")
        player_btn.configure(bg="yellow")
        computer_btn.configure(bg="yellow")
    elif (player_hand == "グー" and computer_hand == "チョキ") or \
         (player_hand == "チョキ" and computer_hand == "パー") or \
         (player_hand == "パー" and computer_hand == "グー"):
        result_label.configure(text=f"あなたの勝ち!({player_hand} vs {computer_hand})", bg="blue")
        player_btn.configure(bg="blue")
        computer_btn.configure(bg="red")
    else:
        result_label.configure(text=f"コンピュータの勝ち…({computer_hand} vs {player_hand})", bg="red")
        player_btn.configure(bg="red")
        computer_btn.configure(bg="blue")
    
    # 相手の手を表示
    computer_btn.configure(text=computer_hand)

# ウィンドウを作成
window = tk.Tk()
window.title("じゃんけんゲーム")
window.geometry("300x500")

# ラベルを作成
result_label = tk.Label(window, text="相手の手", font=("Helvetica", 14), bg="white", width=20, height=2)
result_label.place(x=50, y=100)

# ボタンを作成
player_btn_rock = tk.Button(window, text="グー", font=("Helvetica", 14), command=lambda: btn_click("グー"), width=8, height=2)
player_btn_scissors = tk.Button(window, text="チョキ", font=("Helvetica", 14), command=lambda: btn_click("チョキ"), width=8, height=2)
player_btn_paper = tk.Button(window, text="パー", font=("Helvetica", 14), command=lambda: btn_click("パー"), width=8, height=2)
computer_btn = tk.Button(window, text="?", font=("Helvetica", 14), state="disabled", width=8, height=2)
player_btn_rock.place(x=20, y=400)
player_btn_scissors.place(x=120, y=400)
player_btn_paper.place(x=220, y=400)
computer_btn.place(x=146, y=200)

# ウィンドウを表示
window.mainloop()

結果がこれです。

初期状態
実行結果

いろいろやって次で自分の手に色がつくようになりました。

import tkinter as tk
import random

class Game:
    def __init__(self, master):
        self.master = master
        master.title("じゃんけんゲーム")
        master.geometry("300x500")

        self.result_label = tk.Label(master, text="相手の手", font=("Helvetica", 14), bg="white", width=20, height=2)
        self.result_label.place(x=50, y=100)

        self.player_hands = [
            HandButton(master, "グー", "player", 20),
            HandButton(master, "チョキ", "player", 120),
            HandButton(master, "パー", "player", 220)
        ]

        self.computer_hands = [
            HandButton(master, "?", "computer", 120),
        ]

        self.player_hands[0].button.invoke()

    def set_color(self, player_hand, computer_hand):
        for hand in self.player_hands:
            if hand.hand == player_hand:
                hand.button.configure(bg="blue")
            else:
                hand.button.configure(bg="white")

        for hand in self.computer_hands:
            if hand.hand == computer_hand:
                hand.button.configure(bg="red")
            else:
                hand.button.configure(bg="white")

    def btn_click(self, hand):
        player_hand = hand
        computer_hand = random.choice(["グー", "チョキ", "パー"])

        if player_hand == computer_hand:
            self.result_label.configure(text="引き分け", bg="yellow")
            self.set_color(player_hand, computer_hand)
        elif (player_hand == "グー" and computer_hand == "チョキ") or \
             (player_hand == "チョキ" and computer_hand == "パー") or \
             (player_hand == "パー" and computer_hand == "グー"):
            self.result_label.configure(text=f"あなたの勝ち!({player_hand} vs {computer_hand})", bg="blue")
            self.set_color(player_hand, computer_hand)
        else:
            self.result_label.configure(text=f"コンピュータの勝ち…({computer_hand} vs {player_hand})", bg="red")
            self.set_color(player_hand, computer_hand)

class HandButton:
    def __init__(self, master, hand, player_type, x):
        self.hand = hand
        self.button = tk.Button(master, text=hand, font=("Helvetica", 14), command=lambda: game.btn_click(hand), width=8, height=2)
        self.button.place(x=x, y=400)
        if player_type == "computer":
            self.button.configure(state="disabled")
            self.button.place(x=220, y=100)

root = tk.Tk()
game = Game(root)
root.mainloop()


自分の手とラベルに色がつくようになりました。

プログラムも大分難しくなって、僕の手には負えそうにないのでまた、なんかの機会にとしたいと思います

4. ジャンケンその2

気持ちを切り替えて、もう一度、仕様を考え直した。自分の手の入力方法をボタンでなく、キー入力にしてみると簡単な使用になることに気が付いた。

ジャンケンの仕様を変えてみます。
・ 画面の大きさを 300*400にします
・ 上方 y=100に相手の手を示すラベルをおきます
・ 中央 y=200に結果を示すラベルをおきます
・ 下方 y=300に自分の手を示すラベルをおきます
・ 上方の相手の手はランダムにグー、チョキ、パーを取り、それをラベルに書きます
・ 下方の自分の手はキー入力で決めます
・ キーAがグー、キーSがチョキ、キーDがパーに対応します
・ キー入力時がゲーム開始です
・ じゃんけんの結果を中央の結果ラベルに示します。
・ グー、チョキ、パーに応じて相手の手、自分の手ラベルにあか、黄色、青の色を塗ります
こんなプログラムを作ってください

ちょっと手直しもありますが、次のようなコードを作ってくれました。

import tkinter as tk
import random

class JankenGame:
    def __init__(self, master):
        self.master = master
        self.master.title("じゃんけんゲーム")
        self.master.geometry("300x400")

        self.result_label = tk.Label(self.master, text="じゃんけんを開始", font=("Helvetica", 20))
        self.result_label.place(relx=0.5, rely=0.4, anchor="center")

        self.cpu_label = tk.Label(self.master, text="相手の手", font=("Helvetica", 20))
        self.cpu_label.place(relx=0.5, rely=0.25, anchor="center")

        self.player_label = tk.Label(self.master, text="自分の手", font=("Helvetica", 20))
        self.player_label.place(relx=0.5, rely=0.6, anchor="center")

        self.master.bind("<Key>", self.play_janken)

    def play_janken(self, event):
        player_choice = ""
        if event.char == "a":
            player_choice = "グー"
        elif event.char == "s":
            player_choice = "チョキ"
        elif event.char == "d":
            player_choice = "パー"
        else:
            return

        cpu_choice = random.choice(["グー", "チョキ", "パー"])
        result = self.check_result(player_choice, cpu_choice)

        self.player_label.config(text=f"あなたの手:{player_choice}", bg=self.get_color(player_choice))
        self.cpu_label.config(text=f"相手の手:{cpu_choice}", bg=self.get_color(cpu_choice))
        self.result_label.config(text=result)

    def check_result(self, player_choice, cpu_choice):
        if player_choice == cpu_choice:
            return "引き分け"
        elif (player_choice == "グー" and cpu_choice == "チョキ") or \
             (player_choice == "チョキ" and cpu_choice == "パー") or \
             (player_choice == "パー" and cpu_choice == "グー"):
            return "あなたの勝ち"
        else:
            return "あなたの負け"

    def get_color(self, choice):
        if choice == "グー":
            return "red"
        elif choice == "チョキ":
            return "yellow"
        elif choice == "パー":
            return "blue"


if __name__ == "__main__":
    root = tk.Tk()
    app = JankenGame(root)
    root.mainloop()


初期状態
自分がグーを出してコンピュータもグーを出し引き分けとなった状態

あまり見栄えはよくないけれど、とりあえずは思ったものができた。見栄えをよくするのはまた次の段階にする。ここで使ったのはラベルだけである。

5. まとめ

tkinterモジュールの簡単な使い方を体験した。
簡単なボタン、ラベル、テキスト入力は理解できたかなと思う。ジャンケンのような簡単なモデルでも、ボタン、ラベルの使い方でいろいろと違ってくる。ラジオボタンを使って入力するのがいいか、テキストボックスを用いて一つのボタンで「ジャンケンポン」とやるのが今となってはいい気がする。
自分のイメージにこだわった気がする。最初にそれも相談した方がよかったかも。
まだ、いろいろとあるけれど。とりあえずはゲームのコード作成なのでpygameに次は進みたい。

この記事が気に入ったらサポートをしてみませんか?