1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python×Streamlit】チェコ旅行に向けて作ったチェコ語学習アプリ」

Last updated at Posted at 2025-10-27

来年のGW、初めてのチェコ旅行を計画中です 🇨🇿
現地で少しでも会話できたら楽しいだろうなと思い、
Pythonの学習を兼ねて、Streamlitで“チェコ語クイズアプリ”を作ってみました。

Streamlitとは

PythonでデータアプリやWebツールを超かんたんに作れるオープンソースのフレームワークです。
通常、Webアプリを作るには「HTML / CSS / JavaScript / Flask / Django」 などが必要ですが、Streamlitでは Pythonのスクリプトを書くだけで、即Webアプリになります。

早速使ってみた

  1. Streamlitのインストール
pip install streamlit
  1. 全体的なコード
import streamlit as st
import sqlite3
import random


class LearnCzechWordManager:
    def __init__(self):
        self.db_path = 'path/to/your/directory/word.db'

    def connect_db(self):
        return sqlite3.connect(self.db_path)

    def fetch_random_question_words(self):
        random_id = random.randint(1, 20)
        conn = self.connect_db()
        cursor = conn.execute('SELECT * FROM phrases WHERE id = ?;', (random_id,))
        question_word = cursor.fetchone()
        conn.close()
        return question_word
    
    def fetch_wrong_choices(self, question_word, count=3):
        numbers = [i for i in range(1, 21) if i != question_word]
        random_ids = random.sample(numbers, count)

        conn = self.connect_db()
        cursor = conn.cursor()

        placeholders = ','.join(['?'] * len(random_ids))
        query = f'SELECT * FROM phrases WHERE id IN ({placeholders});'
        
        cursor.execute(query, random_ids)
        wrong_choices = cursor.fetchall()
        conn.close()

        return wrong_choices


    def screen_component(self):
        st.title('🇨🇿 チェコ語単語マスター')

        if "question_word" not in st.session_state:
            st.session_state.question_word = self.fetch_random_question_words()
            st.session_state.wrong_choices = self.fetch_wrong_choices(st.session_state.question_word[0])
            st.session_state.answered = False

            correct_answer = st.session_state.question_word[2]
            fetch_wrong_choices = st.session_state.wrong_choices
            choices = [correct_answer] + [w[2] for w in fetch_wrong_choices]
            random.shuffle(choices)
            st.session_state.choices = choices
            st.session_state.correct_answer = correct_answer
        
        question_word = st.session_state.question_word
        phrase = question_word[1]
        correct_answer = st.session_state.correct_answer
        choices = st.session_state.choices

        answer = st.radio(f'次の単語の意味を回答しなさい : {phrase}', choices)

        if st.button("回答する") and not st.session_state.answered:
            #st.session_state.answered = True
            if answer == correct_answer:
                st.success("✅ 正解!")
            else:
                st.error(f"❌ 不正解。正解は『{correct_answer}』です。")
        
        #if st.session_state.answered:
        if st.button('次の問題へ'):
            st.session_state.clear()
            st.rerun()

    def main(self):
        self.screen_component()
        

if __name__ == "__main__":
    learn_czech_manager = LearnCzechWordManager()
    learn_czech_manager.main()

SQLiteに挿入するデータ

-- DBとテーブル作成
CREATE TABLE IF NOT EXISTS phrases (
  id INTEGER PRIMARY KEY,
  phrase TEXT NOT NULL,
  meaning_ja TEXT NOT NULL
);

-- データ投入
INSERT OR REPLACE INTO phrases (id, phrase, meaning_ja) VALUES
(1,  'Dobrý den',                 'こんにちは'),
(2,  'Ahoj',                      'やあ/こんにちは'),
(3,  'Děkuji / Díky',             'ありがとう/どうもありがとう'),
(4,  'Prosím',                    'どうぞ/すみません/お願いします'),
(5,  'Ano / Ne',                  'はい/いいえ'),
(6,  'Promiňte',                  'すみません(呼びかけ・謝罪)'),
(7,  'Nerozumím',                 'わかりません'),
(8,  'Kolik to stojí?',           'いくらですか?'),
(9,  'Účet, prosím',              'お会計をお願いします'),
(10, 'Kde je toaleta?',           'トイレはどこですか?'),
(11, 'Mluvíte anglicky?',         '英語を話せますか?'),
(12, 'Jídelní lístek, prosím',    'メニューをお願いします'),
(13, 'Pivo, prosím',              'ビールをお願いします'),
(14, 'Na zdraví!',                '乾杯!'),
(15, 'Pomoc!',                    '助けて!'),
(16, 'Zavolejte policii!',        '警察を呼んでください!'),
(17, 'Mám alergii na …',          '私は…にアレルギーがあります'),
(18, 'Můžete to napsat?',         '書いてくれますか?'),
(19, 'Můžete to zopakovat?',      'もう一度言ってくれますか?'),
(20, 'Je to daleko?',             '遠いですか?');

sqlite3 words.db < init_phrases.sql

sqlite3 words.db "SELECT * FROM phrases;"

使ってみた感想、技術的ポイント

st.session_state.question_word = self.fetch_random_question_words()
st.session_state.wrong_choices = self.fetch_wrong_choices(st.session_state.question_word[0])
st.session_state.answered = False

Streamlitは「イベント駆動」ではなく「再実行モデル」です。

  1. ページを開いた瞬間、app.py が上から下まで一度実行される
  2. ボタンを押す、入力を変えるなどすると → スクリプトが最初から再実行される
  3. そのときに、以前の変数はすべてリセットされる(通常の変数では記憶されない)

という挙動になる為、以前の変数は全て破棄されてしまいます。
そのため、ボタンを押す度に問題の内容が変わってしまうという不具合が発生します。
そこで出てくるのが、そこで登場するのが st.session_stateです。
st.session_state は、Streamlit が提供する「永続的な辞書」みたいなものです。
ページ再描画後も、中身が保持されます。
つまり再実行が起こっても、同じ問題データを保持しておくための初期化処理です。

最後に

Streamlitは、簡単で個人が使用するような軽量Webアプリを作るのには向いているのかなと思いました。
非情に簡単で数行書くだけで、良い感じのWebアプリを作ってくれます。
もしよければ皆さんも使用してみてください!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?