230918 知識ゼロで始めるアプリ開発 #7

生命科学系の大学院生、いつしか休みに休んではいけないことを体で覚えてしまったので、意図的に休もうとすると罪悪感が生まれます。

 

ということで、昨日の続き、tkinterのボタンサイズをピクセルで指定します。

www.delftstack.com

 

このサイトでのコード例が

pixelVirtual = tk.PhotoImage(width=1, height=1)
    
buttonExample1 = tk.Button(app,
                           text="Increase",
                           image=pixelVirtual,
                           width=100,
                           height=100,
                           compound="c")

ということなので、自分のコードでは

        self.pixelVirtual = tk.PhotoImage(width=20, height=20)

        self.wakaru_button = tk.Button(root, text="わかる", command=self.show_random_word, image=pixelVirtual)
        self.wakaru_button.place(x=100,y=150)

という具合でいかがでしょう。
早速走らせてみます。

ダメかー。ちゃんとself.pixelVirtual = tk.PhotoImage(width=20, height=20)で定義したつもりだったんだけどな。

...

...

...

とまぁこの手の方法で色々と試してみたのですが、埒が明かなかったので他の手段を考えてみます。office54.net

padx、padyを利用すると、ウィジェット外側のパディング(間隔)を指定できます。

x方向の外部間隔はpadx、y方向の外部間隔はpadyで指定します。

以下に使用例を記します。

とのこと。これなら中心位置を計算しなくても直接ボタンの位置を指定することができる? 

いやでもこれだと.placeの方が使い勝手が良さそうだ。
やっぱり.placeで指定するしか無いか…

        self.wakaru_button = tk.Button(root, text="わかる", command=self.show_random_word)
        self.wakaru_button.place(x=80,y=150)

        self.wakaranai_button = tk.Button(root, text="わからない", command=self.show_random_word)
        self.wakaranai_button.place(x=230,y=150)

 

結局、トライアンドエラーを繰り返して、まぁこんな感じかって位置にボタンを手作業で持ってきました。あんまりここに時間を食ってても仕方ないのでね。

あとはtkinterでボタンサイズ指定すると四角くてダサいので、Apple純正(?)の丸っこいボタンがいいですね。

次回は「わかる」単語を表示しないようにして、「わからない」単語を表示し続けるような仕様にしていきます。
これ結構難しそうだし、Pythonの基礎を復習しなきゃだな。

230917 知識ゼロで始めるアプリ開発 #6

前回は不甲斐ない結果でしたね。
知識がゼロなので仕方がないと思いつつも、知識がゼロとは言っていないので、もう少し基本的なところに立ち返ってなんとか自力で解決できなかったのかと思うばかりです。

ということで今日は軽微な修正でなんとかしていきます。

 

次の目標としては

単語を表示する

「わかる」「わからない」ボタンから単語を覚えたかどうかをユーザーが入力

「わかる」単語はもう表示せず、「わからない」単語については再度表示を続ける

という機能を追加していきたいです。そうするとやることは以下の2つ

①「わかる」「わからない」ボタンの追加
②ボタン操作後の単語のふるい分け

とりあえず、①から解決していきましょう。
まずはソースコードのおさらい

import tkinter as tk

import random

 

class WordApp:

    def __init__(self, root):

        self.root = root

        self.root.title("英単語ランダム表示アプリ")

 

        self.word_label = tk.Label(root, text="", font=("Helvetica", 24))

        self.word_label.pack(padx=20, pady=20, expand=True, fill="both")

 

        self.load_words()

        self.show_random_word()

 

        self.next_button = tk.Button(root, text="次の単語", command=self.show_random_word)

        self.next_button.pack()

 

    def load_words(self):

        try:

            with open("words.txt", "r") as file:

                self.words = [line.strip() for line in file.readlines()]

        except FileNotFoundError:

            self.words = ["No words found"]

 

    def show_random_word(self):

        random_word = random.choice(self.words)

        font_size = max(20, min(48, 400 // len(random_word)))  

        self.word_label.config(text=random_word, font=("Helvetica", font_size))

 

if __name__ == "__main__":

    root = tk.Tk()

    app = WordApp(root)

    root.geometry("400x200")  

    root.mainloop()

まぁ多分、self.next_buttonのところで定義がされているんでしょうね。

単純にボタンを2つにする(とりあえずどちらを押しても次の単語が表示されるような設定)なら、これでどうでしょう。

        self.wakaru_button = tk.Button(root, text="わかる", command=self.show_random_word)
        self.wakaru_button.pack()

        self.wakaranai_button = tk.Button(root, text="わからない", command=self.show_random_word)
        self.wakaranai_button.pack()

 

まぁ単純すぎましたがうまくいきました。
でも想像してたのは、縦並びじゃなくて横並びなんだよな。
ということでもうひと手間加えます。

qiita.com

元のコードはpack()で記載されていることがわかります。
今回はウィンドウサイズが400(横)×200(縦)なので、横は100と300の位置に、縦は150の位置に来るようにplace()メゾットで改良してみましょう。

        self.wakaru_button = tk.Button(root, text="わかる", command=self.show_random_word)
        self.wakaru_button.place(x=100,y=150)

        self.wakaranai_button = tk.Button(root, text="わからない", command=self.show_random_word)
        self.wakaranai_button.place(x=300,y=150)

ありゃー。この座標ってボタンの中心位置ではなく、左端だったんですね。
ということは、ボタンサイズおよびボタンの中心がどこに来るかからボタンの左端の位置を定義すれば良いですね。でもボタンサイズが定義されている感じではないので、定義してあげましょう。

flytech.work

 

とりあえず、20×20の正方形にしてみますか。

        self.wakaru_button = tk.Button(root, text="わかる", command=self.show_random_word, width= 20, height = 20)
        self.wakaru_button.place(x=100,y=150)

        self.wakaranai_button = tk.Button(root, text="わからない", command=self.show_random_word, width= 20, height = 20)
        self.wakaranai_button.place(x=300,y=150)

なんかイヤな予感…

やっぱり。20×20は大きすぎましたね。
いやでも、ウィンドウサイズは400×200だしこんなにはみ出さなくても良いのでは?
もしかしてボタンのサイズ定義はピクセルとは違うのか?

違うのね。(余談だけどこういう時こそChatGPTの正しい使い方だと思うわ)

とりあえず、ちょうどいいサイズ感を探してヨコタテ5×2で指定しました。

 

でもよく考えたら、ボタンを自然な位置に配置するためには、ピクセルサイズでボタンサイズを指定してやらないと、中心位置がどこかとかそういうのがわかんないですね。
次はそこを改良しなければ。

230917 知識ゼロで始めるアプリ開発 #5

今回は

ウィンドウのサイズを一定に保ったまま、表示文は長さに併せてフォントサイズが変化する

という感じに改良を施していきます。
早速、ChatGPTに聞いてみました。

ってこら! あんまり聞いてばっかりでは何も成長せんやろうがい。
ということで、回答をチラ見してはしまいましたが、一旦記憶をリセットして自力でソースコードを解読していきます。

import tkinter as tk
import random

class WordApp:
    def __init__(self, root):
        self.root = root
        self.root.title("英単語ランダム表示アプリ")

        self.word_label = tk.Label(root, text="", font=("Helvetica", 24))
        self.word_label.pack(padx=20, pady=20)

        self.load_words()
        self.show_random_word()

        self.next_button = tk.Button(root, text="次の単語", command=self.show_random_word)
        self.next_button.pack()

    def load_words(self):
        try:
            with open("words.txt", "r") as file:
                self.words = [line.strip() for line in file.readlines()]
        except FileNotFoundError:
            self.words = ["No words found"]

    def show_random_word(self):
        random_word = random.choice(self.words)
        self.word_label.config(text=random_word)

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

 

素人が見てわかることとしては、ここでフォントサイズが24で固定になってますね、

        self.word_label = tk.Label(root, text="", font=("Helvetica", 24))

ここが変動できるような設定にしたいですね。

でもウィンドウサイズを指定しているようなコードは無さそうですね…? 強いて言えば、

        self.word_label.pack(padx=20, pady=20)

padx, padyがなんとなくウィンドウサイズに関係していそうな感じはしています…?
いやでもわかんないです。

 

じゃあ一旦、フォントサイズを変動できるような設定を調べてみますか

office54.net

いきなり自動調整の設定を入れるのは難しそうなので、10pt-30ptの可変という感じで、試しにこうしてみましょう

        self.word_label = tk.Label(root, text="", font=("Helvetica", "10-30"))



エラーで動きませんでした。いやー、わかってはいたけどそんなに簡単ではなかった。
ということで再度ggり直し。

www.school.ctc-g.co.jp

一括で設定するには次のように書いてあげればいいそうです。

>>> import tkinter.font

>>> font = tkinter.font.Font

tkinter.fontをインポートしてあげた後、tkinter.font.Fontを使って、一括で指定したいフォントの情報を書くということになります。(詳しい書き方のイメージは後であげる図をご確認下さい。)


とのこと。おっけ、フォントを指定してあげよう。
ということで以下のように書き換えました。

import tkinter as tk
import random
import tkinter.font #変更

class WordApp:
    def __init__(self, root):
        self.root = root
        self.root.title("英単語ランダム表示アプリ")

        self.word_label = tk.Label(root, text="",  font = tkinter.font.Font) #変更
        self.word_label.pack(padx=20, pady=20)

        self.load_words()
        self.show_random_word()

        self.next_button = tk.Button(root, text="次の単語", command=self.show_random_word)
        self.next_button.pack()

    def load_words(self):
        try:
            with open("words.txt", "r") as file:
                self.words = [line.strip() for line in file.readlines()]
        except FileNotFoundError:
            self.words = ["No words found"]

    def show_random_word(self):
        random_word = random.choice(self.words)
        self.word_label.config(text=random_word)

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

もうおわかりですね。
私、tkinter.font.Fontをどこの位置で定義すればよいかが全くわかっていないことに気づきました。

 


ちょっと心折れてしまったので、chatGPT が教えてくれた解答をカンニングします。
心折れるの早すぎでは()

ウィンドウサイズを固定し、単語の長さに応じてフォントサイズを変更するには、テキストのフォントサイズを調整する方法があります。以下のコードは、ウィンドウサイズを固定し、テキストのフォントサイズを単語の長さに応じて調整する方法を示しています。

```python
import tkinter as tk
import random

class WordApp:
    def __init__(self, root):
        self.root = root
        self.root.title("英単語ランダム表示アプリ")

        self.word_label = tk.Label(root, text="", font=("Helvetica", 24))
        self.word_label.pack(padx=20, pady=20, expand=True, fill="both")

        self.load_words()
        self.show_random_word()

        self.next_button = tk.Button(root, text="次の単語", command=self.show_random_word)
        self.next_button.pack()

    def load_words(self):
        try:
            with open("words.txt", "r") as file:
                self.words = [line.strip() for line in file.readlines()]
        except FileNotFoundError:
            self.words = ["No words found"]

    def show_random_word(self):
        random_word = random.choice(self.words)
        font_size = max(10, min(48, 400 // len(random_word)))  # 調整可能なフォントサイズの範囲を設定
        self.word_label.config(text=random_word, font=("Helvetica", font_size))

if __name__ == "__main__":
    root = tk.Tk()
    app = WordApp(root)
    root.geometry("400x200")  # ウィンドウのサイズを固定
    root.mainloop()
```

このコードでは、`self.word_label`の`font`オプションを設定する部分で、単語の長さに応じてフォントサイズが調整されます。フォントサイズはウィンドウの幅(400ピクセル)と単語の長さに比例して変更され、最小値は10、最大値は48に制限されています。ウィンドウサイズ自体は`root.geometry`で固定されています。

 

とのこと。いやぁ我ながら情けない。
ということで、とりあえず、このこのソースコードを保存して動かしてみます。

 

ウィンドウサイズに対してフォントが小さくなってしまいましたね…
これはこれでまだまだ修正の余地がありそうです。
とりあえず今日はここまで。

230910 知識ゼロで始めるアプリ開発 #4

ということで、前回ので箱(英単語アプリの原型)が出来上がったので、今度は英単語を詰め込んでみます。

ということなので、word.txtを同じディレクトリに作成し、単語を入れていきます。

ところで、単語間の区切りってどうなるんだろ? 改行? スペース?と思ったのでシンプルな一文というのも試して入れてみます。そして、word.txtを保存し、英単語アプリを起動してみます。

おー! 動いた!
前回も言いましたけど、やっぱり自分で書いたものがちゃんと動くってこんなにも嬉しいんですねー

こちらも出てきました。単語間の区切りは半角スペースじゃなくて改行ですね。
これで安心してword.txtにいろんなものを入力できますね。

ただ気になったのは、文の長さによってウィンドウが大きくなったり小さくなったりを繰り返すので、気が散る感じがしますね。

次の改良点は
◯ウィンドウのサイズを一定に保ったまま、表示文は長さに併せてフォントサイズが変化する

というUIをつけてみたいですね。できるかな?

230907 知識ゼロで始めるアプリ開発 #3

ということで、前回出てきたソースコードを噛み砕いていきます。

そもそもTkinterとはなんぞや

qiita.com

 

端的に言うと、GUIを組めるツールキットとのことです。

...

と言ってもソースコードを噛み砕くと時間がかかるので(めんどくさくなっただけ)、まず、つべこべ言わず走らせてみます。



とりあえず、tango.pyとして保存して、走らせました。

お、ちゃんと走った。
ただ、words.txtは用意していないので、

except FileNotFoundError:
            self.words = ["No words found"]

でちゃんと分岐されるようになってます。
エラーになっても走らないとかそういうのではなく、ちゃんとエラーと言ってくれるのはとても安心しますね。


という具合で、#3にして想定していた通りのGUIは用意できたので、次はwords.txtの中身を用意して理想の形に近づけていこうと思います。

 

230905 知識ゼロで始めるアプリ開発 #2

ということでとりあえず聞いてみた。

出てきたソースコードは以下。

import tkinter as tk
import random

class WordApp:
    def __init__(self, root):
        self.root = root
        self.root.title("英単語ランダム表示アプリ")

        self.word_label = tk.Label(root, text="", font=("Helvetica", 24))
        self.word_label.pack(padx=20, pady=20)

        self.load_words()
        self.show_random_word()

        self.next_button = tk.Button(root, text="次の単語", command=self.show_random_word)
        self.next_button.pack()

    def load_words(self):
        try:
            with open("words.txt", "r") as file:
                self.words = [line.strip() for line in file.readlines()]
        except FileNotFoundError:
            self.words = ["No words found"]

    def show_random_word(self):
        random_word = random.choice(self.words)
        self.word_label.config(text=random_word)

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

まずtkinterとはなんぞやという話。
次回はこのソースコードを噛み砕いていきます。