Quiz - Flet UI, interaktywność, zapis do json file.

Quiz - Flet UI, interaktywność, zapis do json file.
Kreator pytan do quiz napisany w Python z Flet do interfejsu, w pełni funkcjonalny.
import flet as ft
import os
import json

# Ścieżki plików
sciezka_folderu = "data"
os.makedirs(sciezka_folderu, exist_ok=True)
sciezka_pliku = os.path.join(sciezka_folderu, "kwestionariusz.json")

# Wczytanie / utworzenie pliku
if os.path.exists(sciezka_pliku):
    with open(sciezka_pliku, "r", encoding="utf-8") as plik_json:
        kwestionariusz = json.load(plik_json)
else:
    kwestionariusz = {}
    with open(sciezka_pliku, "w", encoding="utf-8") as plik_json:
        json.dump(kwestionariusz, plik_json, indent=4, ensure_ascii=False)
        

vsc-quiz-json.png


def strona_glowna(page: ft.Page):

    lista_pytan_view = ft.Column()
    def odswiez_liste():
        lista_pytan_view.controls.clear()

        for i, (pyt, odp) in enumerate(kwestionariusz.items(), start=1):
            lista_pytan_view.controls.append(ft.Text(f"{i}. {pyt} '\n{odp}", color=ft.ColorScheme.primary))
            page.update()


    def zapisz_do_json():
        with open(sciezka_pliku, "w", encoding="utf-8") as plik_json:
            json.dump(kwestionariusz, plik_json, indent=4, ensure_ascii=False)
            
    komunikat_tekst = ft.Text("")

    def validate():
        errors = []

        if not pytanie_field.value.strip():
            errors.append("Pole 'Pytanie' jest obowiążkowe.")
        if not odp1_field.value.strip() and  not odp2_field.value.strip() and not odp3_field.value.strip():
            errors.append("Wszystkie pola odpowiedzi muszą być wypełnione.")
        if poprawna_odp.value is None:
            errors.append("Musisz wybrać poprawną odpowiedż.")
            # print(errors)
        return errors

komunikat-o-bledach.png

    
    def zapisz_pytanie(e):
        niewypelnione_pola_info = validate()
        if niewypelnione_pola_info:
            page.open(ft.SnackBar(ft.Text("\n".join(niewypelnione_pola_info), color=ft.ColorScheme.on_error), bgcolor=ft.ColorScheme.error))
            
            page.update()
            return

        odpowiedzi = []
        for i, txt in enumerate([odp1_field.value, odp2_field.value, odp3_field.value], start=1):
            punkt = 1 if str(i) == poprawna_odp.value else -0.5
            odpowiedzi.append((f"{chr(96+i)}) {txt}", punkt))
        kwestionariusz[pytanie_field.value] = odpowiedzi
        zapisz_do_json()
        odswiez_liste()
        page.update()

        pytanie_field.value = ''
        odp1_field.value = ''
        odp2_field.value = ''
        odp3_field.value = ''
        poprawna_odp.value = None
           
    

    status_text = ft.Text()    
    status_text_error = ft.Text(size=20, color=ft.Colors.ERROR)  
    def on_radio_group_change(e):
        if not e.control.value:
            status_text_error.value = "Poprawna odpowiedź nie została zaznaczona."
            status_text.value = ''
        else:
            status_text_error.value = ''
            status_text.value = f"Prawidłowa odpowiedź to: {int(e.control.value) + 1}"
        page.update()

    # sekcja ustawien okna
    page.title = "Kreator kwestionariuszy!"
    page.theme = ft.Theme(color_scheme_seed=ft.Colors.PURPLE)
    page.padding = 100
    page.window.width = 1440
    # page.height = 
    page.scroll = ft.ScrollMode.AUTO
    page.vertical_alignment = ft.MainAxisAlignment.CENTER
    
    # page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

    # sekcja komponentów

    instrukcja = ft.Text("Kreator pytań do quizu", size=40, weight="bold", color=ft.ColorScheme.primary)
    pytanie_field = ft.TextField(label="Tutaj wpisz pytanie...", hint_text="Jakie miasto jest stolicą Francji?",
            color=ft.Colors.ON_PRIMARY_CONTAINER,
            bgcolor=ft.Colors.PRIMARY_CONTAINER, width=500)
    odp1_field = ft.TextField(label="Tutaj wpisz jedna z opcji odpowiedzi", hint_text="Paryż", text_size=16, color=ft.Colors.ON_SECONDARY_CONTAINER,
            bgcolor=ft.Colors.SECONDARY_CONTAINER, width=400)
    odp2_field = ft.TextField(label="Tutaj wpisz druga z opcji odpowiedzi", hint_text="Barcelona", text_size=16, color=ft.Colors.ON_SECONDARY_CONTAINER,
            bgcolor=ft.Colors.SECONDARY_CONTAINER, width=400)
    odp3_field = ft.TextField(label="Tutaj wpisz trzecia z opcji odpowiedzi", hint_text="Madryt", text_size=16, color=ft.Colors.ON_SECONDARY_CONTAINER, bgcolor=ft.Colors.SECONDARY_CONTAINER, width=400)

    poprawna_odp_info = ft.Text("Ktora odpowiedź jest poprawna?")
    poprawna_odp = ft.RadioGroup(content=ft.Row([
        ft.Radio(value=str(i), label=f"Odpowiedź {i+1}") for i in range(0, 3)
    ]), on_change=on_radio_group_change)
    

    wyslij_przycisk = ft.ElevatedButton(
        text="Dodaj pytanie do bazy",
        on_click=zapisz_pytanie,
        style=ft.ButtonStyle(padding=25, bgcolor=ft.ColorScheme.primary, color=ft.ColorScheme.on_primary),
    )

    

    lista_pytan = ft.Text("Lista pytań", size=20, weight="bold", color=ft.ColorScheme.primary)
    
    

    # sekcja umieszczenia komponentów w oknie
    page.add(
        # form_container,
        instrukcja,
        pytanie_field,
        odp1_field,
        odp2_field,
        odp3_field,
        poprawna_odp_info,
        poprawna_odp,
        wyslij_przycisk,
        komunikat_tekst,
        status_text_error,
        status_text,
        ft.Divider(),
        lista_pytan,
        lista_pytan_view,
    )
    odswiez_liste()
    page.update()


ft.app(strona_glowna)