Przejdź do głównej treści
INF.04-01-23.01-SGStyczeń 2023 · 33 kroki · ok. 87 min · 32 kryteria z klucza

Rozwiązanie krok po kroku: NWD i generator hasła (Python + Tkinter)

Rozwiązanie dotyczy arkusza z algorytmem Euklidesa i oknem „Dodaj pracownika” w wersji Python + Tkinter - cały egzamin w jednym języku. W konsoli funkcja wyznaczająca największy wspólny dzielnik dokładnie według schematu blokowego z arkusza (odejmowanie, nie reszta z dzielenia), a w okienku zbudowanym biblioteką Tkinter dwie ramki z kontrolkami: dane pracownika i generowanie hasła o podanej długości (z wielką literą, cyfrą i znakiem specjalnym zależnie od pól wyboru), a po „Zatwierdź” - komunikat z danymi pracownika. Do tego dokumentacja funkcji według wzoru z arkusza, zrzuty i dokument egzamin.

Kroki idą zdanie po zdaniu za treścią arkusza - każdy zaczyna się poleceniem z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Kod od początku jest pisany tak, jak w prawdziwych projektach; na końcu dwa kroki porównują ten wariant z rozwiązaniem w C# i WPF (druga zakładka).

Co widać w podglądzie. Program konsolowy z każdego kroku jest naprawdę uruchamiany (Python 3.12) z liczbami wpisanymi z klawiatury. Okno to symulacja w przeglądarce, ale jej układ pochodzi z prawdziwego Tkintera: program z kroku uruchomiłem i odczytałem położenie, rozmiar, kolor i tekst każdej kontrolki. Przyciski działają tak, jak metody z kodu kroku - scenariusz z obrazów 2 i 3 sprawdziłem w symulacji i w prawdziwym oknie Tk (hasło jest losowe, więc porównywany jest jego układ: na której pozycji jest wielka litera, cyfra, znak specjalny).

Jak liczą się punkty. Klucz ma 32 kryteria (R.1.1 - R.4.8), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 24 punkty - można stracić 8 kryteriów i nadal zdać. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  1. Etap 1: Przygotowanie

    Krok 1 · po tym etapie masz 0 z 32 kryteriów (pkt)

    Krok 1: Folder zdającego i trzy podfoldery

    ok. 2 min
    Polecenie z arkusza

    Utwórz folder i nazwij go numerem zdającego. W folderze utwórz podfoldery: konsola, desktopowa, dokumentacja.

    Na pulpicie zakładasz folder o nazwie z numerem PESEL, a w nim konsola, desktopowa i dokumentacja (w tym arkuszu folder nazywa się konsola, nie konsolowa). Arkusz nie ma archiwum z materiałami - wszystko piszesz od zera. Oba projekty zakładasz w PyCharm, a na koniec pakujesz je i kopiujesz do podfolderów razem z plikami main.py.

  2. Etap 2: Aplikacja konsolowa

    Kroki 2-9 · po tym etapie masz 12 z 32 kryteriów (pkt)

    Krok 2: Projekt konsola w PyCharm i funkcja main

    ok. 3 min
    Polecenie z arkusza

    Obiektowy język programowania zgodny z zainstalowanym na stanowisku egzaminacyjnym: C++ lub C#, lub Java, lub Python.

    Wybierasz Pythona. W PyCharm: New Project, nazwa konsola, interpreter ze stanowiska, bez skryptu powitalnego; w projekcie plik main.py. Program główny od razu zamykasz w funkcji main() wywoływanej pod warunkiem if __name__ == "__main__": - zmienne programu głównego będą lokalne, a plik da się zaimportować (np. do testów) bez uruchamiania programu. pass to pusta instrukcja - za chwilę ją zastąpisz.

    konsola/main.py Python nowy plik
    def main():
        pass
    
    
    if __name__ == "__main__":
        main()
    Wynik programu po tym kroku
    > python main.py
  3. Krok 3: Funkcja nwd z dwoma argumentami

    Kryteria: ,

    R.2.1 W programie zdefiniowano funkcję, która pobiera jako argumenty dwie liczby, typem funkcji jest liczba całkowita bez znaku (dopuszcza się w języku Java ze znakiem, w Python bez typu) oraz zastosowano instrukcję return

    R.1.3 Użyto znaczące nazewnictwo funkcji

    ok. 2 min
    Polecenie z arkusza

    Szukanie NWD zaimplementowane w funkcji o dwóch argumentach i zwracanej wartości największego wspólnego dzielnika. Funkcja nie może zawierać operacji wejścia - wyjścia.

    Szkielet funkcji: dwa parametry o nazwach a i b - dokładnie jak na schemacie (klucz tego wymaga, R.1.4) - i instrukcja return, która zwraca wynik (R.2.1). Funkcja tylko liczy: żadnego input ani print w środku, jak każe arkusz - wczytywanie i wypisywanie zrobi program główny.

    Python nie ma typu „liczba bez znaku” - typ wynika z wartości, którą dostanie funkcja. O to, żeby trafiały do niej tylko liczby dodatnie, zadba program główny.

    konsola/main.py Python zmienione: 4 linie
    def nwd(a, b):
        return a
    
    
    def main():
        pass
    
    
    if __name__ == "__main__":
        main()

    Wynik programu bez zmian - taki jak po kroku 2.

  4. Krok 4: Pętla: dopóki a ≠ b

    Kryteria: ,

    R.2.2 Funkcja stosuje pętlę while lub do while, która jest wykonywana tak długo jak a i b są różne

    R.2.4 Funkcja zwraca wartość równą a (możliwe też za pomocą innej zmiennej równej a)

    ok. 2 min
    Polecenie z arkusza

    Implementacja algorytmu w pełni zgodna z przedstawionym na schemacie algorytmem.

    Pierwszy romb schematu („czy a ≠ b”) ze strzałką wracającą nad niego to pętla while: warunek sprawdzany przed każdym obrotem (R.2.2). != to w Pythonie „różne”. Gałąź „Nie” prowadzi do „Wyświetl a” - w tym rozwiązaniu wyjście z pętli i return a (R.2.4). Ciało pętli jest jeszcze puste (pass) - dla różnych liczb kręciłaby się bez końca, więc funkcji na razie nie wywołujesz.

    konsola/main.py Python zmienione: 2 linie
    def nwd(a, b):
        while a != b:
            pass
        return a
    
    
    def main():
        pass
    
    
    if __name__ == "__main__":
        main()

    Wynik programu bez zmian - taki jak po kroku 2.

  5. Krok 5: Odejmowanie mniejszej liczby od większej

    Kryteria: ,

    R.2.3 W pętli znajduje się porównanie a i b, jeżeli a > b jest wykonywane a = a - b w przeciwnym wypadku b = b - a (warunki mogą być odwrócone)

    R.1.2 Kod zapisano z wcięciami dla zagłębień bloków

    ok. 3 min
    Polecenie z arkusza

    Implementacja algorytmu w pełni zgodna z przedstawionym na schemacie algorytmem: czy a > b - Tak: a := a-b, Nie: b := b-a.

    Drugi romb to if ... else: gdy a jest większe, odejmujesz od niego b, w przeciwnym razie odejmujesz a od b (R.2.3). Każde odjęcie zmniejsza jedną z liczb, więc w końcu się zrównają - i ta wspólna wartość to NWD (48 i 18: 30, 18 → 12, 18 → 12, 6 → 6, 6). Zapis a := a - b ze schematu to w Pythonie zwykłe przypisanie a = a - b. Bloki wyznaczają wcięcia - każdy poziom zagnieżdżenia o 4 spacje dalej (R.1.2).

    Typowe błędy

    Algorytm z resztą z dzielenia (a % b) albo gotowe math.gcd(a, b) dają ten sam wynik, ale to INNY algorytm niż na schemacie - R.2.2 i R.2.3 wymagają pętli z porównaniem a i b i odejmowaniem.

    konsola/main.py Python zmienione: 4 linie
    def nwd(a, b):
        while a != b:
            if a > b:
                a = a - b
            else:
                b = b - a
        return a
    
    
    def main():
        pass
    
    
    if __name__ == "__main__":
        main()

    Wynik programu bez zmian - taki jak po kroku 2.

  6. Krok 6: Program główny: wczytanie a i b

    Kryteria: , ,

    R.2.5 W programie głównym następuje wczytanie dwóch liczb z klawiatury

    R.1.5 Zastosowano typy pól pasujące do problemu, typ zmiennych a i b jest naturalny - w zależności od języka programowania kwalifikator unsigned lub typ bez znaku np. uint (jeżeli nie zastosowano typu bez znaku, sprawdzono warunkiem czy obie zmienne są dodatnie) w przypadku Python zastosowano jawną konwersję do odpowiednich typów

    R.1.4 Użyto nazewnictwo dwóch zmiennych algorytmu: a i b. Dla Pozostałych zmiennych (jeśli istnieją) nazewnictwo jest znaczące. Wyjątkami od reguły są zmienne bufor, tmp, iteratory pętli itp. Kryterium nie jest spełnione tylko wtedy, gdy nazwy zmiennych nic nie znaczą, np. x, tab, tablica

    ok. 2 min
    Polecenie z arkusza

    Program główny testuje działanie funkcji i zawiera operacje wejścia - wyjścia. Należy stosować nazwy zmiennych zgodne z nazwami zastosowanymi na schemacie blokowym.

    input(...) wypisuje pytanie i zwraca wpisany tekst. Klucz wymaga w Pythonie jawnej konwersji do właściwego typu (R.1.5), więc tekst zamieniasz na liczbę całkowitą funkcją int(...) - bez niej a > b porównywałoby napisy („9” > „18”, bo porównanie idzie znak po znaku). Zmienne nazywasz a i b, jak na schemacie (R.2.5).

    konsola/main.py Python zmienione: 2 linie
    def nwd(a, b):
        while a != b:
            if a > b:
                a = a - b
            else:
                b = b - a
        return a
    
    
    def main():
        a = int(input("Podaj liczbę a: "))
        b = int(input("Podaj liczbę b: "))
    
    
    if __name__ == "__main__":
        main()
    Wynik programu po tym kroku
    > python main.py
    Podaj liczbę a: 48
    Podaj liczbę b: 18
  7. Krok 7: Kontrola: tylko liczby dodatnie

    Kryteria: ,

    R.1.5 Zastosowano typy pól pasujące do problemu, typ zmiennych a i b jest naturalny - w zależności od języka programowania kwalifikator unsigned lub typ bez znaku np. uint (jeżeli nie zastosowano typu bez znaku, sprawdzono warunkiem czy obie zmienne są dodatnie) w przypadku Python zastosowano jawną konwersję do odpowiednich typów

    R.1.3 Użyto znaczące nazewnictwo funkcji

    ok. 3 min
    Polecenie z arkusza

    Liczby a i b należą do zbioru liczb całkowitych dodatnich (odpowiedni typ lub kontrola poprawności wpisanej liczby).

    Python nie ma typu bez znaku, więc wybierasz drugą drogę z arkusza: kontrolę poprawności. Funkcja read_positive(prompt) wczytuje liczbę i dopóki nie jest większa od zera, pyta ponownie. To ważne nie tylko formalnie: dla a = 0 pętla w nwd kręciłaby się bez końca (b - 0 to wciąż b), a dla liczby ujemnej - też.

    Podgląd to dwa prawdziwe uruchomienia: 48 i 18, a potem najpierw -4 (odrzucone) i 4 dla a, 6 dla b.

    Typowe błędy

    Sprawdzenie samym if (zamiast while) zapyta ponownie tylko raz - przy drugiej złej liczbie program pójdzie dalej z zerem albo liczbą ujemną.

    konsola/main.py Python zmienione: 9 linii
    def nwd(a, b):
        while a != b:
            if a > b:
                a = a - b
            else:
                b = b - a
        return a
    
    
    def read_positive(prompt):
        number = int(input(prompt))
        while number <= 0:
            number = int(input("Liczba musi być dodatnia. " + prompt))
        return number
    
    
    def main():
        a = read_positive("Podaj liczbę a: ")
        b = read_positive("Podaj liczbę b: ")
    
    
    if __name__ == "__main__":
        main()
    Wynik programu po tym kroku
    > python main.py
    Podaj liczbę a: 48
    Podaj liczbę b: 18
    
    > python main.py
    Podaj liczbę a: -4
    Liczba musi być dodatnia. Podaj liczbę a: 4
    Podaj liczbę b: 6
  8. Krok 8: Program główny: wynik w konsoli

    Kryteria: , ,

    R.2.6 W konsoli wyświetlona jest obliczona wartość

    R.1.6 Program podejmuje komunikację z użytkownikiem, np. monit o wprowadzenie danych jest znaczący albo wyświetlenie danych opatrzone znaczącym komunikatem

    R.1.1 Kod źródłowy zapisano w sposób czytelny: instrukcje w osobnych liniach, stosowane spacje pomiędzy operatorami, konsekwentnie stosowana wybrana konwencja dla nawiasów klamrowych instrukcji blokowej

    ok. 2 min
    Polecenie z arkusza

    Program główny testuje działanie funkcji i zawiera operacje wejścia - wyjścia. Program powinien być zapisany czytelnie, z zachowaniem zasad czystego formatowania kodu.

    Wywołujesz nwd(a, b) i wypisujesz wynik z komunikatem (R.2.6, R.1.6). f-napis wstawia w klamry wartości zmiennych i wynik funkcji: „NWD(48, 18) = 6”. Funkcja dostaje kopie wartości - zmiany a i b wewnątrz nwd nie psują zmiennych w main, dlatego w komunikacie widać liczby wpisane przez użytkownika, a nie te z końca pętli.

    Podgląd: 48 i 18 dają 6, a 17 i 5 (liczby względnie pierwsze) - 1.

    konsola/main.py Python zmienione: 1 linia
    def nwd(a, b):
        while a != b:
            if a > b:
                a = a - b
            else:
                b = b - a
        return a
    
    
    def read_positive(prompt):
        number = int(input(prompt))
        while number <= 0:
            number = int(input("Liczba musi być dodatnia. " + prompt))
        return number
    
    
    def main():
        a = read_positive("Podaj liczbę a: ")
        b = read_positive("Podaj liczbę b: ")
        print(f"NWD({a}, {b}) = {nwd(a, b)}")
    
    
    if __name__ == "__main__":
        main()
    Wynik programu po tym kroku
    > python main.py
    Podaj liczbę a: 48
    Podaj liczbę b: 18
    NWD(48, 18) = 6
    
    > python main.py
    Podaj liczbę a: 17
    Podaj liczbę b: 5
    NWD(17, 5) = 1
  9. Krok 9: Folder konsola: archiwum i źródło

    ok. 2 min
    Polecenie z arkusza

    Kod aplikacji przygotuj do nagrania na płytę. W folderze konsola powinno znaleźć się archiwum całego projektu o nazwie konsola.zip, plik z kodem źródłowym programu oraz plik uruchomieniowy, jeżeli istnieje.

    Folder projektu pakujesz do konsola.zip, obok kopiujesz main.py. Python nie tworzy pliku uruchomieniowego, więc go nie ma. Archiwum robisz na końcu pracy, po dopisaniu dokumentacji funkcji z części III.

    Wynik programu bez zmian - taki jak po kroku 8.

  10. Etap 3: Aplikacja desktopowa

    Kroki 10-26 · po tym etapie masz 22 z 32 kryteriów (pkt)

    Krok 10: Projekt desktopowa: okno w klasie EmployeeWindow

    ok. 3 min
    Polecenie z arkusza

    Za pomocą dostępnego na stanowisku egzaminacyjnym środowiska programistycznego wykonaj aplikację desktopową do wprowadzania danych pracownika z możliwością generowania hasła.

    Okno budujesz biblioteką Tkinter (jest w standardowej instalacji Pythona). W PyCharm drugi projekt, desktopowa, z plikiem main.py.

    Tym razem kontrolki i dane okna trzymasz w klasie EmployeeWindow. Powód podaje sam arkusz: hasło wygenerowane jednym przyciskiem ma być widoczne w obsłudze drugiego. Pole obiektu (self.password) widzą wszystkie metody klasy - bez zmiennych globalnych i słowa global. Konstruktor __init__(self, root) dostaje okno główne i (w kolejnych krokach) zbuduje w nim kontrolki.

    Na dole pliku: root = tk.Tk() tworzy okno główne, EmployeeWindow(root) - obiekt tej klasy, a root.mainloop() uruchamia pętlę zdarzeń, która czeka na kliknięcia aż do zamknięcia okna.

    desktopowa/main.py Python nowy plik
    import tkinter as tk
    
    
    class EmployeeWindow:
        def __init__(self, root):
            pass
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  11. Krok 11: Tytuł i rozmiar okna

    Kryteria:

    R.3.1 Nadano dla okna aplikacji nazwę "Dodaj pracownika" wraz z numerem zdającego oraz umieszczono w nim dwie grupy „Dane Pracownika” oraz „Generowanie hasła”

    ok. 2 min
    Polecenie z arkusza

    Okno o nazwie „Dodaj pracownika”. W nazwie okna należy wstawić także numer zdającego.

    title ustawia napis na pasku okna - na egzaminie po „Dodaj pracownika” wpisujesz numer PESEL, tu stoi autor rozwiązania, Bartosz Bryniarski. geometry("650x300") nadaje rozmiar zbliżony do obrazu 1 (szerokość × wysokość w pikselach) - domyślne puste okno ma 200 × 200 i tytuł by się w nim nie zmieścił.

    desktopowa/main.py Python zmienione: 2 linie
    import tkinter as tk
    
    
    class EmployeeWindow:
        def __init__(self, root):
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  12. Krok 12: Tło okna LightSteelBlue

    Kryteria:

    R.3.3 Nadano dla okna tło koloru LightSteelBlue (#B0C4DE) oraz dla przycisków tło koloru SteelBlue (#4682B4) i biały kolor czcionki

    ok. 1 min
    Polecenie z arkusza

    Okno ma tło koloru LightSteelBlue (#B0C4DE).

    Kolor tła wpiszesz jeszcze w kilkunastu kontrolkach - w Tkinterze każda etykieta, ramka i pole wyboru ma własne tło (domyślnie szare), więc musi dostać kolor okna. Zapisujesz go raz, w stałej BACKGROUND na górze pliku (wielkie litery to w Pythonie umowny zapis stałej), i używasz tej nazwy. configure(bg=BACKGROUND) ustawia tło okna (R.3.3).

    desktopowa/main.py Python zmienione: 3 linie
    import tkinter as tk
    
    BACKGROUND = "#B0C4DE"
    
    
    class EmployeeWindow:
        def __init__(self, root):
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  13. Krok 13: Układ: dwie grupy obok siebie

    Kryteria:

    R.3.1 Nadano dla okna aplikacji nazwę "Dodaj pracownika" wraz z numerem zdającego oraz umieszczono w nim dwie grupy „Dane Pracownika” oraz „Generowanie hasła”

    ok. 3 min
    Polecenie z arkusza

    Kontrolki rozmieszczone zgodnie z obrazem 1. Kontrolki w grupie „Dane Pracownika”. Kontrolki w grupie „Generowanie hasła”.

    Grupa z podpisem i ramką to w Tkinterze tk.LabelFrame (odpowiednik GroupBox) - podpis w text, a padx/pady to wewnętrzny odstęp kontrolek od ramki.

    Obie grupy układasz menedżerem grid - siatką wierszy i kolumn liczonych od 0: pierwsza w kolumnie 0, druga w kolumnie 1. columnconfigure(..., weight=1) dla obu kolumn dzieli szerokość okna po równo, rowconfigure(0, weight=1) oddaje wierszowi 0 wolną wysokość, a sticky="nsew" rozciąga ramkę na całą komórkę (n, s, e, w - strony świata). Zewnętrzne odstępy padx/pady w grid odsuwają ramki od krawędzi okna i od siebie (R.3.1).

    desktopowa/main.py Python zmienione: 9 linii
    import tkinter as tk
    
    BACKGROUND = "#B0C4DE"
    
    
    class EmployeeWindow:
        def __init__(self, root):
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  14. Krok 14: Etykieta Imię i pole edycyjne

    Kryteria:

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, przycisk zatwierdzenia jest dłuższy niż przycisk do generowania hasła

    ok. 3 min
    Polecenie z arkusza

    Kontrolki w grupie „Dane Pracownika”: pole edycyjne poprzedzone etykietą o treści „Imię”.

    Wewnątrz ramki własna siatka: etykieta tk.Label w kolumnie 0, pole edycyjne tk.Entry w kolumnie 1. Pierwszy argument kontrolki to jej rodzic - employee_frame, nie root - więc wiersze i kolumny liczą się wewnątrz ramki. sticky="w" przykleja etykietę do lewej, width=22 to szerokość pola w znakach.

    Pole zapisujesz w polu obiektu self.first_name_entry - jego treść odczytasz w metodzie przycisku „Zatwierdź”. Etykiety nic nie zmienia, więc nie zapisujesz jej nigdzie.

    desktopowa/main.py Python zmienione: 3 linie
    import tkinter as tk
    
    BACKGROUND = "#B0C4DE"
    
    
    class EmployeeWindow:
        def __init__(self, root):
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
            tk.Label(employee_frame, text="Imię", bg=BACKGROUND).grid(row=0, column=0, sticky="w", pady=5)
            self.first_name_entry = tk.Entry(employee_frame, width=22)
            self.first_name_entry.grid(row=0, column=1, pady=5)
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  15. Krok 15: Etykieta Nazwisko i pole edycyjne

    Kryteria:

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, przycisk zatwierdzenia jest dłuższy niż przycisk do generowania hasła

    ok. 1 min
    Polecenie z arkusza

    Pole edycyjne poprzedzone etykietą o treści „Nazwisko”.

    To samo w wierszu 1, z polem self.last_name_entry.

    desktopowa/main.py Python zmienione: 3 linie
    import tkinter as tk
    
    BACKGROUND = "#B0C4DE"
    
    
    class EmployeeWindow:
        def __init__(self, root):
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
            tk.Label(employee_frame, text="Imię", bg=BACKGROUND).grid(row=0, column=0, sticky="w", pady=5)
            self.first_name_entry = tk.Entry(employee_frame, width=22)
            self.first_name_entry.grid(row=0, column=1, pady=5)
            tk.Label(employee_frame, text="Nazwisko", bg=BACKGROUND).grid(row=1, column=0, sticky="w", pady=5)
            self.last_name_entry = tk.Entry(employee_frame, width=22)
            self.last_name_entry.grid(row=1, column=1, pady=5)
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  16. Krok 16: Lista rozwijana Stanowisko

    Kryteria: ,

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, przycisk zatwierdzenia jest dłuższy niż przycisk do generowania hasła

    R.3.4 Zdefiniowano cztery elementy dla listy rozwijalnej: Kierownik, Starszy programista, Młodszy programista, Tester

    ok. 3 min
    Polecenie z arkusza

    Lista rozwijalna podpisana „Stanowisko” z elementami: Kierownik, Starszy programista, Młodszy programista, Tester.

    Lista rozwijana jest w module ttk (nowsze kontrolki Tkintera w stylu systemu) - importujesz go from tkinter import ttk. ttk.Combobox dostaje pozycje w parametrze values jako listę napisów (R.3.4). state="readonly" pozwala tylko wybierać z listy - bez tego w polu dałoby się wpisać dowolny tekst. Na obrazie 1 nic nie jest wybrane, więc nie ustawiasz pozycji początkowej. Rozwiń listę w podglądzie.

    desktopowa/main.py Python zmienione: 5 linii
    import tkinter as tk
    from tkinter import ttk
    
    BACKGROUND = "#B0C4DE"
    
    
    class EmployeeWindow:
        def __init__(self, root):
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
            tk.Label(employee_frame, text="Imię", bg=BACKGROUND).grid(row=0, column=0, sticky="w", pady=5)
            self.first_name_entry = tk.Entry(employee_frame, width=22)
            self.first_name_entry.grid(row=0, column=1, pady=5)
            tk.Label(employee_frame, text="Nazwisko", bg=BACKGROUND).grid(row=1, column=0, sticky="w", pady=5)
            self.last_name_entry = tk.Entry(employee_frame, width=22)
            self.last_name_entry.grid(row=1, column=1, pady=5)
            tk.Label(employee_frame, text="Stanowisko", bg=BACKGROUND).grid(row=2, column=0, sticky="w", padx=(0, 10), pady=5)
            self.position_combobox = ttk.Combobox(employee_frame, width=19, state="readonly",
                                                  values=["Kierownik", "Starszy programista", "Młodszy programista", "Tester"])
            self.position_combobox.grid(row=2, column=1, pady=5)
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  17. Krok 17: Etykieta Ile znaków? i pole edycyjne

    Kryteria:

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, przycisk zatwierdzenia jest dłuższy niż przycisk do generowania hasła

    ok. 1 min
    Polecenie z arkusza

    Kontrolki w grupie „Generowanie hasła”: pole edycyjne poprzedzone etykietą o treści „Ile znaków?”.

    Druga ramka ma własną siatkę - numeracja wierszy zaczyna się w niej znowu od 0. Pole self.length_entry odczyta metoda generowania hasła.

    desktopowa/main.py Python zmienione: 3 linie
    import tkinter as tk
    from tkinter import ttk
    
    BACKGROUND = "#B0C4DE"
    
    
    class EmployeeWindow:
        def __init__(self, root):
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
            tk.Label(employee_frame, text="Imię", bg=BACKGROUND).grid(row=0, column=0, sticky="w", pady=5)
            self.first_name_entry = tk.Entry(employee_frame, width=22)
            self.first_name_entry.grid(row=0, column=1, pady=5)
            tk.Label(employee_frame, text="Nazwisko", bg=BACKGROUND).grid(row=1, column=0, sticky="w", pady=5)
            self.last_name_entry = tk.Entry(employee_frame, width=22)
            self.last_name_entry.grid(row=1, column=1, pady=5)
            tk.Label(employee_frame, text="Stanowisko", bg=BACKGROUND).grid(row=2, column=0, sticky="w", padx=(0, 10), pady=5)
            self.position_combobox = ttk.Combobox(employee_frame, width=19, state="readonly",
                                                  values=["Kierownik", "Starszy programista", "Młodszy programista", "Tester"])
            self.position_combobox.grid(row=2, column=1, pady=5)
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
            tk.Label(password_frame, text="Ile znaków?", bg=BACKGROUND).grid(row=0, column=0, sticky="w", padx=(0, 10))
            self.length_entry = tk.Entry(password_frame, width=12)
            self.length_entry.grid(row=0, column=1)
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  18. Krok 18: Trzy pola wyboru, pierwsze zaznaczone

    Kryteria: ,

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, przycisk zatwierdzenia jest dłuższy niż przycisk do generowania hasła

    R.3.10 Aplikacja kompiluje się i uruchamia, co udokumentowano zrzutem ekranu, jej układ po uruchomieniu jest zgodny z obrazem 1 w arkuszu egzaminacyjnym. Zaznaczone jest pierwsze pole wyboru

    ok. 3 min
    Polecenie z arkusza

    Trzy pola wyboru podpisane: „Małe i wielkie litery”, „Cyfry”, „Znaki specjalne”. Pierwsze pole jest domyślnie zaznaczone.

    Pole wyboru to tk.Checkbutton. Jego stan Tkinter trzyma w osobnej zmiennej kontrolki - tk.BooleanVar - podanej w parametrze variable: zaznaczenie ustawia ją na True, odznaczenie na False, a odczytasz ją metodą get(). BooleanVar(value=True) sprawia, że pierwsze pole jest zaznaczone od startu (R.3.10).

    columnspan=2 - pole wyboru zajmuje obie kolumny siatki (jest szersze niż etykieta „Ile znaków?”). activebackground to tło pod kursorem myszy - bez niego pole po najechaniu robiłoby się szare.

    Typowe błędy

    tk.BooleanVar(True) bez nazwy parametru nie zaznacza pola - pierwszym parametrem pozycyjnym jest okno-rodzic, nie wartość. Pisz value=True.

    desktopowa/main.py Python zmienione: 12 linii
    import tkinter as tk
    from tkinter import ttk
    
    BACKGROUND = "#B0C4DE"
    
    
    class EmployeeWindow:
        def __init__(self, root):
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
            tk.Label(employee_frame, text="Imię", bg=BACKGROUND).grid(row=0, column=0, sticky="w", pady=5)
            self.first_name_entry = tk.Entry(employee_frame, width=22)
            self.first_name_entry.grid(row=0, column=1, pady=5)
            tk.Label(employee_frame, text="Nazwisko", bg=BACKGROUND).grid(row=1, column=0, sticky="w", pady=5)
            self.last_name_entry = tk.Entry(employee_frame, width=22)
            self.last_name_entry.grid(row=1, column=1, pady=5)
            tk.Label(employee_frame, text="Stanowisko", bg=BACKGROUND).grid(row=2, column=0, sticky="w", padx=(0, 10), pady=5)
            self.position_combobox = ttk.Combobox(employee_frame, width=19, state="readonly",
                                                  values=["Kierownik", "Starszy programista", "Młodszy programista", "Tester"])
            self.position_combobox.grid(row=2, column=1, pady=5)
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
            tk.Label(password_frame, text="Ile znaków?", bg=BACKGROUND).grid(row=0, column=0, sticky="w", padx=(0, 10))
            self.length_entry = tk.Entry(password_frame, width=12)
            self.length_entry.grid(row=0, column=1)
            self.letters_var = tk.BooleanVar(value=True)
            self.letters_check = tk.Checkbutton(password_frame, text="Małe i wielkie litery", variable=self.letters_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.letters_check.grid(row=1, column=0, columnspan=2, sticky="w", pady=(8, 0))
            self.digits_var = tk.BooleanVar()
            self.digits_check = tk.Checkbutton(password_frame, text="Cyfry", variable=self.digits_var,
                                               bg=BACKGROUND, activebackground=BACKGROUND)
            self.digits_check.grid(row=2, column=0, columnspan=2, sticky="w")
            self.special_var = tk.BooleanVar()
            self.special_check = tk.Checkbutton(password_frame, text="Znaki specjalne", variable=self.special_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.special_check.grid(row=3, column=0, columnspan=2, sticky="w")
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  19. Krok 19: Przycisk Generuj hasło

    Kryteria: ,

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, przycisk zatwierdzenia jest dłuższy niż przycisk do generowania hasła

    R.3.3 Nadano dla okna tło koloru LightSteelBlue (#B0C4DE) oraz dla przycisków tło koloru SteelBlue (#4682B4) i biały kolor czcionki

    ok. 2 min
    Polecenie z arkusza

    Przycisk o treści „Generuj hasło”. Przyciski mają tło koloru SteelBlue (#4682B4) i biały kolor czcionki.

    tk.Button z napisem w text, tłem bg i kolorem tekstu fg (foreground) - biały (R.3.3). Kolor przycisków też trafia do stałej BUTTON_COLOR, bo użyjesz go dwa razy. Obsługi kliknięcia jeszcze nie ma.

    desktopowa/main.py Python zmienione: 3 linie
    import tkinter as tk
    from tkinter import ttk
    
    BACKGROUND = "#B0C4DE"
    BUTTON_COLOR = "#4682B4"
    
    
    class EmployeeWindow:
        def __init__(self, root):
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
            tk.Label(employee_frame, text="Imię", bg=BACKGROUND).grid(row=0, column=0, sticky="w", pady=5)
            self.first_name_entry = tk.Entry(employee_frame, width=22)
            self.first_name_entry.grid(row=0, column=1, pady=5)
            tk.Label(employee_frame, text="Nazwisko", bg=BACKGROUND).grid(row=1, column=0, sticky="w", pady=5)
            self.last_name_entry = tk.Entry(employee_frame, width=22)
            self.last_name_entry.grid(row=1, column=1, pady=5)
            tk.Label(employee_frame, text="Stanowisko", bg=BACKGROUND).grid(row=2, column=0, sticky="w", padx=(0, 10), pady=5)
            self.position_combobox = ttk.Combobox(employee_frame, width=19, state="readonly",
                                                  values=["Kierownik", "Starszy programista", "Młodszy programista", "Tester"])
            self.position_combobox.grid(row=2, column=1, pady=5)
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
            tk.Label(password_frame, text="Ile znaków?", bg=BACKGROUND).grid(row=0, column=0, sticky="w", padx=(0, 10))
            self.length_entry = tk.Entry(password_frame, width=12)
            self.length_entry.grid(row=0, column=1)
            self.letters_var = tk.BooleanVar(value=True)
            self.letters_check = tk.Checkbutton(password_frame, text="Małe i wielkie litery", variable=self.letters_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.letters_check.grid(row=1, column=0, columnspan=2, sticky="w", pady=(8, 0))
            self.digits_var = tk.BooleanVar()
            self.digits_check = tk.Checkbutton(password_frame, text="Cyfry", variable=self.digits_var,
                                               bg=BACKGROUND, activebackground=BACKGROUND)
            self.digits_check.grid(row=2, column=0, columnspan=2, sticky="w")
            self.special_var = tk.BooleanVar()
            self.special_check = tk.Checkbutton(password_frame, text="Znaki specjalne", variable=self.special_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.special_check.grid(row=3, column=0, columnspan=2, sticky="w")
            self.generate_button = tk.Button(password_frame, text="Generuj hasło", bg=BUTTON_COLOR, fg="white")
            self.generate_button.grid(row=4, column=0, columnspan=2, pady=(5, 0))
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  20. Krok 20: Przycisk Zatwierdź pod grupami

    Kryteria: ,

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, przycisk zatwierdzenia jest dłuższy niż przycisk do generowania hasła

    R.3.3 Nadano dla okna tło koloru LightSteelBlue (#B0C4DE) oraz dla przycisków tło koloru SteelBlue (#4682B4) i biały kolor czcionki

    ok. 2 min
    Polecenie z arkusza

    Na dole okna znajduje się przycisk „Zatwierdź”, jest on wyraźnie dłuższy niż przycisk do generowania hasła.

    Przycisk w wierszu 1 siatki okna, rozciągnięty na obie kolumny (columnspan=2) - dzięki temu stoi na środku. width=30 to szerokość w znakach: ponad dwa razy więcej niż „Generuj hasło”, którego szerokość wynika z napisu (R.3.2). Wygląd okna jest kompletny - porównaj z obrazem 1.

    desktopowa/main.py Python zmienione: 3 linie
    import tkinter as tk
    from tkinter import ttk
    
    BACKGROUND = "#B0C4DE"
    BUTTON_COLOR = "#4682B4"
    
    
    class EmployeeWindow:
        def __init__(self, root):
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
            tk.Label(employee_frame, text="Imię", bg=BACKGROUND).grid(row=0, column=0, sticky="w", pady=5)
            self.first_name_entry = tk.Entry(employee_frame, width=22)
            self.first_name_entry.grid(row=0, column=1, pady=5)
            tk.Label(employee_frame, text="Nazwisko", bg=BACKGROUND).grid(row=1, column=0, sticky="w", pady=5)
            self.last_name_entry = tk.Entry(employee_frame, width=22)
            self.last_name_entry.grid(row=1, column=1, pady=5)
            tk.Label(employee_frame, text="Stanowisko", bg=BACKGROUND).grid(row=2, column=0, sticky="w", padx=(0, 10), pady=5)
            self.position_combobox = ttk.Combobox(employee_frame, width=19, state="readonly",
                                                  values=["Kierownik", "Starszy programista", "Młodszy programista", "Tester"])
            self.position_combobox.grid(row=2, column=1, pady=5)
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
            tk.Label(password_frame, text="Ile znaków?", bg=BACKGROUND).grid(row=0, column=0, sticky="w", padx=(0, 10))
            self.length_entry = tk.Entry(password_frame, width=12)
            self.length_entry.grid(row=0, column=1)
            self.letters_var = tk.BooleanVar(value=True)
            self.letters_check = tk.Checkbutton(password_frame, text="Małe i wielkie litery", variable=self.letters_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.letters_check.grid(row=1, column=0, columnspan=2, sticky="w", pady=(8, 0))
            self.digits_var = tk.BooleanVar()
            self.digits_check = tk.Checkbutton(password_frame, text="Cyfry", variable=self.digits_var,
                                               bg=BACKGROUND, activebackground=BACKGROUND)
            self.digits_check.grid(row=2, column=0, columnspan=2, sticky="w")
            self.special_var = tk.BooleanVar()
            self.special_check = tk.Checkbutton(password_frame, text="Znaki specjalne", variable=self.special_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.special_check.grid(row=3, column=0, columnspan=2, sticky="w")
            self.generate_button = tk.Button(password_frame, text="Generuj hasło", bg=BUTTON_COLOR, fg="white")
            self.generate_button.grid(row=4, column=0, columnspan=2, pady=(5, 0))
    
            self.confirm_button = tk.Button(root, text="Zatwierdź", bg=BUTTON_COLOR, fg="white", width=30)
            self.confirm_button.grid(row=1, column=0, columnspan=2, pady=(0, 20))
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  21. Krok 21: Pole password i metoda obsługi przycisku

    Kryteria: ,

    R.3.5 Po wciśnięciu przynajmniej jednego przycisku wywoływana jest funkcja

    R.3.8 Hasło oraz zestawy znaków są przechowywane w zmiennych typu napisowego, hasło jest widoczne dla obu metod obsługujących kliknięcia przycisków

    ok. 3 min
    Polecenie z arkusza

    Hasło oraz zestawy znaków są przechowywane w zmiennych typu napisowego.

    • self.password = "" - pole obiektu z pustym napisem; ustawi je metoda generowania, a odczyta metoda „Zatwierdź” (R.3.8),
    • def generate_password(self) - metoda klasy; self to obiekt okna, przez niego metoda sięga do kontrolek i pól,
    • command=self.generate_password w przycisku - bez nawiasów: przekazujesz metodę, a Tkinter wywoła ją przy kliknięciu (R.3.5).

    Metoda jest jeszcze pusta - kliknięcie niczego nie robi.

    Typowe błędy

    command=self.generate_password() z nawiasami wywoła metodę raz, przy budowaniu okna, a przyciskowi przekaże jej wynik (None) - kliknięcie nie będzie nic robić.

    desktopowa/main.py Python zmienione: 7 linii
    import tkinter as tk
    from tkinter import ttk
    
    BACKGROUND = "#B0C4DE"
    BUTTON_COLOR = "#4682B4"
    
    
    class EmployeeWindow:
        def __init__(self, root):
            self.password = ""
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
            tk.Label(employee_frame, text="Imię", bg=BACKGROUND).grid(row=0, column=0, sticky="w", pady=5)
            self.first_name_entry = tk.Entry(employee_frame, width=22)
            self.first_name_entry.grid(row=0, column=1, pady=5)
            tk.Label(employee_frame, text="Nazwisko", bg=BACKGROUND).grid(row=1, column=0, sticky="w", pady=5)
            self.last_name_entry = tk.Entry(employee_frame, width=22)
            self.last_name_entry.grid(row=1, column=1, pady=5)
            tk.Label(employee_frame, text="Stanowisko", bg=BACKGROUND).grid(row=2, column=0, sticky="w", padx=(0, 10), pady=5)
            self.position_combobox = ttk.Combobox(employee_frame, width=19, state="readonly",
                                                  values=["Kierownik", "Starszy programista", "Młodszy programista", "Tester"])
            self.position_combobox.grid(row=2, column=1, pady=5)
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
            tk.Label(password_frame, text="Ile znaków?", bg=BACKGROUND).grid(row=0, column=0, sticky="w", padx=(0, 10))
            self.length_entry = tk.Entry(password_frame, width=12)
            self.length_entry.grid(row=0, column=1)
            self.letters_var = tk.BooleanVar(value=True)
            self.letters_check = tk.Checkbutton(password_frame, text="Małe i wielkie litery", variable=self.letters_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.letters_check.grid(row=1, column=0, columnspan=2, sticky="w", pady=(8, 0))
            self.digits_var = tk.BooleanVar()
            self.digits_check = tk.Checkbutton(password_frame, text="Cyfry", variable=self.digits_var,
                                               bg=BACKGROUND, activebackground=BACKGROUND)
            self.digits_check.grid(row=2, column=0, columnspan=2, sticky="w")
            self.special_var = tk.BooleanVar()
            self.special_check = tk.Checkbutton(password_frame, text="Znaki specjalne", variable=self.special_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.special_check.grid(row=3, column=0, columnspan=2, sticky="w")
            self.generate_button = tk.Button(password_frame, text="Generuj hasło", bg=BUTTON_COLOR, fg="white",
                                             command=self.generate_password)
            self.generate_button.grid(row=4, column=0, columnspan=2, pady=(5, 0))
    
            self.confirm_button = tk.Button(root, text="Zatwierdź", bg=BUTTON_COLOR, fg="white", width=30)
            self.confirm_button.grid(row=1, column=0, columnspan=2, pady=(0, 20))
    
    
        def generate_password(self):
            pass
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  22. Krok 22: Hasło z małych liter i okno komunikatu

    Kryteria: ,

    R.3.6 Po wybraniu przycisku "Generuj hasło" wyświetla się komunikat z wygenerowanym hasłem, liczba znaków hasła jest zgodna z liczbą podaną w polu edycyjnym. Generowanie hasła należy sprawdzić dwa razy (hasła muszą być różne)

    R.3.7 Znaki do hasła są wybierane przy wykorzystaniu generatora liczb pseudolosowych z zestawu znaków, który zawsze obejmuje małe litery a..z oraz gdy wybrano pierwszy checkbox wielkie litery A..Z i gdy wybrano drugi checkbox cyfry: 0..9 oraz gdy wybrano trzeci checkbox znaki specjalne ze zbioru !@#$%^&*()_+-=

    ok. 3 min
    Polecenie z arkusza

    Generowane jest hasło o liczbie znaków określonej w polu edycyjnym. Poszczególne znaki hasła są wybierane losowo z zestawu małych liter. Hasło jest wypisywane jako komunikat zgodnie z obrazem 2 po wybraniu przycisku „Generuj hasło”.

    • self.length_entry.get() zwraca tekst z pola, a int(...) zamienia go na liczbę znaków,
    • self.password = "" - każde kliknięcie zaczyna nowe hasło,
    • random.choice(LOWERCASE) losuje jeden znak z napisu (generator liczb pseudolosowych, R.3.7), a += dokleja go do hasła,
    • messagebox.showinfo(tytuł, treść) z modułu tkinter.messagebox pokazuje okno komunikatu z hasłem i przyciskiem OK, jak na obrazie 2 (R.3.6).

    Wpisz w podglądzie 5 i kliknij „Generuj hasło” dwa razy - dwa różne hasła z pięciu małych liter.

    Typowe błędy

    Puste pole „Ile znaków?” daje int("") - błąd ValueError. Tkinter nie zamyka wtedy okna, tylko wypisuje błąd w konsoli PyCharm, a kliknięcie „nic nie robi”. Arkusz zwalnia z tego sprawdzania, ale przy testowaniu pamiętaj, żeby wpisać liczbę.

    desktopowa/main.py Python zmienione: 8 linii
    import random
    import tkinter as tk
    from tkinter import messagebox, ttk
    
    BACKGROUND = "#B0C4DE"
    BUTTON_COLOR = "#4682B4"
    LOWERCASE = "abcdefghijklmnopqrstuvwxyz"
    
    
    class EmployeeWindow:
        def __init__(self, root):
            self.password = ""
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
            tk.Label(employee_frame, text="Imię", bg=BACKGROUND).grid(row=0, column=0, sticky="w", pady=5)
            self.first_name_entry = tk.Entry(employee_frame, width=22)
            self.first_name_entry.grid(row=0, column=1, pady=5)
            tk.Label(employee_frame, text="Nazwisko", bg=BACKGROUND).grid(row=1, column=0, sticky="w", pady=5)
            self.last_name_entry = tk.Entry(employee_frame, width=22)
            self.last_name_entry.grid(row=1, column=1, pady=5)
            tk.Label(employee_frame, text="Stanowisko", bg=BACKGROUND).grid(row=2, column=0, sticky="w", padx=(0, 10), pady=5)
            self.position_combobox = ttk.Combobox(employee_frame, width=19, state="readonly",
                                                  values=["Kierownik", "Starszy programista", "Młodszy programista", "Tester"])
            self.position_combobox.grid(row=2, column=1, pady=5)
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
            tk.Label(password_frame, text="Ile znaków?", bg=BACKGROUND).grid(row=0, column=0, sticky="w", padx=(0, 10))
            self.length_entry = tk.Entry(password_frame, width=12)
            self.length_entry.grid(row=0, column=1)
            self.letters_var = tk.BooleanVar(value=True)
            self.letters_check = tk.Checkbutton(password_frame, text="Małe i wielkie litery", variable=self.letters_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.letters_check.grid(row=1, column=0, columnspan=2, sticky="w", pady=(8, 0))
            self.digits_var = tk.BooleanVar()
            self.digits_check = tk.Checkbutton(password_frame, text="Cyfry", variable=self.digits_var,
                                               bg=BACKGROUND, activebackground=BACKGROUND)
            self.digits_check.grid(row=2, column=0, columnspan=2, sticky="w")
            self.special_var = tk.BooleanVar()
            self.special_check = tk.Checkbutton(password_frame, text="Znaki specjalne", variable=self.special_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.special_check.grid(row=3, column=0, columnspan=2, sticky="w")
            self.generate_button = tk.Button(password_frame, text="Generuj hasło", bg=BUTTON_COLOR, fg="white",
                                             command=self.generate_password)
            self.generate_button.grid(row=4, column=0, columnspan=2, pady=(5, 0))
    
            self.confirm_button = tk.Button(root, text="Zatwierdź", bg=BUTTON_COLOR, fg="white", width=30)
            self.confirm_button.grid(row=1, column=0, columnspan=2, pady=(0, 20))
    
    
        def generate_password(self):
            length = int(self.length_entry.get())
            self.password = ""
            for i in range(length):
                self.password += random.choice(LOWERCASE)
            messagebox.showinfo("Hasło", self.password)
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  23. Krok 23: Wielka litera, gdy zaznaczono pierwsze pole

    Kryteria:

    R.3.7 Znaki do hasła są wybierane przy wykorzystaniu generatora liczb pseudolosowych z zestawu znaków, który zawsze obejmuje małe litery a..z oraz gdy wybrano pierwszy checkbox wielkie litery A..Z i gdy wybrano drugi checkbox cyfry: 0..9 oraz gdy wybrano trzeci checkbox znaki specjalne ze zbioru !@#$%^&*()_+-=

    ok. 3 min
    Polecenie z arkusza

    Jeżeli zaznaczono checkbox „Małe i wielkie litery”, jeden ze znaków jest losowany z zestawu wielkich liter. Za zestaw liter przyjmuje się wszystkie litery z klawiatury małe i wielkie alfabetu łacińskiego.

    Arkusz upraszcza zadanie: można przyjąć, że określony znak losujesz z innego zestawu. W tym rozwiązaniu pierwszy znak (i == 0) jest wielką literą, gdy pole wyboru jest zaznaczone - stan odczytujesz ze zmiennej kontrolki: self.letters_var.get() zwraca True albo False (R.3.7).

    desktopowa/main.py Python zmienione: 5 linii
    import random
    import tkinter as tk
    from tkinter import messagebox, ttk
    
    BACKGROUND = "#B0C4DE"
    BUTTON_COLOR = "#4682B4"
    LOWERCASE = "abcdefghijklmnopqrstuvwxyz"
    UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    
    
    class EmployeeWindow:
        def __init__(self, root):
            self.password = ""
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
            tk.Label(employee_frame, text="Imię", bg=BACKGROUND).grid(row=0, column=0, sticky="w", pady=5)
            self.first_name_entry = tk.Entry(employee_frame, width=22)
            self.first_name_entry.grid(row=0, column=1, pady=5)
            tk.Label(employee_frame, text="Nazwisko", bg=BACKGROUND).grid(row=1, column=0, sticky="w", pady=5)
            self.last_name_entry = tk.Entry(employee_frame, width=22)
            self.last_name_entry.grid(row=1, column=1, pady=5)
            tk.Label(employee_frame, text="Stanowisko", bg=BACKGROUND).grid(row=2, column=0, sticky="w", padx=(0, 10), pady=5)
            self.position_combobox = ttk.Combobox(employee_frame, width=19, state="readonly",
                                                  values=["Kierownik", "Starszy programista", "Młodszy programista", "Tester"])
            self.position_combobox.grid(row=2, column=1, pady=5)
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
            tk.Label(password_frame, text="Ile znaków?", bg=BACKGROUND).grid(row=0, column=0, sticky="w", padx=(0, 10))
            self.length_entry = tk.Entry(password_frame, width=12)
            self.length_entry.grid(row=0, column=1)
            self.letters_var = tk.BooleanVar(value=True)
            self.letters_check = tk.Checkbutton(password_frame, text="Małe i wielkie litery", variable=self.letters_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.letters_check.grid(row=1, column=0, columnspan=2, sticky="w", pady=(8, 0))
            self.digits_var = tk.BooleanVar()
            self.digits_check = tk.Checkbutton(password_frame, text="Cyfry", variable=self.digits_var,
                                               bg=BACKGROUND, activebackground=BACKGROUND)
            self.digits_check.grid(row=2, column=0, columnspan=2, sticky="w")
            self.special_var = tk.BooleanVar()
            self.special_check = tk.Checkbutton(password_frame, text="Znaki specjalne", variable=self.special_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.special_check.grid(row=3, column=0, columnspan=2, sticky="w")
            self.generate_button = tk.Button(password_frame, text="Generuj hasło", bg=BUTTON_COLOR, fg="white",
                                             command=self.generate_password)
            self.generate_button.grid(row=4, column=0, columnspan=2, pady=(5, 0))
    
            self.confirm_button = tk.Button(root, text="Zatwierdź", bg=BUTTON_COLOR, fg="white", width=30)
            self.confirm_button.grid(row=1, column=0, columnspan=2, pady=(0, 20))
    
    
        def generate_password(self):
            length = int(self.length_entry.get())
            self.password = ""
            for i in range(length):
                if i == 0 and self.letters_var.get():
                    self.password += random.choice(UPPERCASE)
                else:
                    self.password += random.choice(LOWERCASE)
            messagebox.showinfo("Hasło", self.password)
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  24. Krok 24: Cyfra i znak specjalny

    Kryteria: ,

    R.3.7 Znaki do hasła są wybierane przy wykorzystaniu generatora liczb pseudolosowych z zestawu znaków, który zawsze obejmuje małe litery a..z oraz gdy wybrano pierwszy checkbox wielkie litery A..Z i gdy wybrano drugi checkbox cyfry: 0..9 oraz gdy wybrano trzeci checkbox znaki specjalne ze zbioru !@#$%^&*()_+-=

    R.3.8 Hasło oraz zestawy znaków są przechowywane w zmiennych typu napisowego, hasło jest widoczne dla obu metod obsługujących kliknięcia przycisków

    ok. 3 min
    Polecenie z arkusza

    Jeżeli zaznaczono checkbox „Cyfry”, jeden ze znaków hasła jest losowany z zestawu cyfr. Jeżeli zaznaczono checkbox „Znaki specjalne”, jeden ze znaków hasła jest losowany z zestawu znaków specjalnych. Dla uproszczenia zadania można przyjąć zawsze określony znak (np. pierwszy), który jest losowany z cyfr. Za zestaw znaków specjalnych przyjmuje się znaki !@#$%^&*()_+-=.

    Drugi znak (i == 1) to cyfra, trzeci (i == 2) - znak specjalny, każdy tylko przy zaznaczonym polu; pozostałe są małymi literami. Łańcuch if ... elif ... else gwarantuje, że na każdej pozycji losujesz dokładnie raz. Zestawy znaków to stałe napisowe na górze pliku (R.3.8).

    Zaznacz w podglądzie „Cyfry”, wpisz 5 i wygeneruj hasło - wynik w rodzaju „Mb7ri”, jak na obrazie 2.

    desktopowa/main.py Python zmienione: 6 linii
    import random
    import tkinter as tk
    from tkinter import messagebox, ttk
    
    BACKGROUND = "#B0C4DE"
    BUTTON_COLOR = "#4682B4"
    LOWERCASE = "abcdefghijklmnopqrstuvwxyz"
    UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    DIGITS = "0123456789"
    SPECIAL = "!@#$%^&*()_+-="
    
    
    class EmployeeWindow:
        def __init__(self, root):
            self.password = ""
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
            tk.Label(employee_frame, text="Imię", bg=BACKGROUND).grid(row=0, column=0, sticky="w", pady=5)
            self.first_name_entry = tk.Entry(employee_frame, width=22)
            self.first_name_entry.grid(row=0, column=1, pady=5)
            tk.Label(employee_frame, text="Nazwisko", bg=BACKGROUND).grid(row=1, column=0, sticky="w", pady=5)
            self.last_name_entry = tk.Entry(employee_frame, width=22)
            self.last_name_entry.grid(row=1, column=1, pady=5)
            tk.Label(employee_frame, text="Stanowisko", bg=BACKGROUND).grid(row=2, column=0, sticky="w", padx=(0, 10), pady=5)
            self.position_combobox = ttk.Combobox(employee_frame, width=19, state="readonly",
                                                  values=["Kierownik", "Starszy programista", "Młodszy programista", "Tester"])
            self.position_combobox.grid(row=2, column=1, pady=5)
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
            tk.Label(password_frame, text="Ile znaków?", bg=BACKGROUND).grid(row=0, column=0, sticky="w", padx=(0, 10))
            self.length_entry = tk.Entry(password_frame, width=12)
            self.length_entry.grid(row=0, column=1)
            self.letters_var = tk.BooleanVar(value=True)
            self.letters_check = tk.Checkbutton(password_frame, text="Małe i wielkie litery", variable=self.letters_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.letters_check.grid(row=1, column=0, columnspan=2, sticky="w", pady=(8, 0))
            self.digits_var = tk.BooleanVar()
            self.digits_check = tk.Checkbutton(password_frame, text="Cyfry", variable=self.digits_var,
                                               bg=BACKGROUND, activebackground=BACKGROUND)
            self.digits_check.grid(row=2, column=0, columnspan=2, sticky="w")
            self.special_var = tk.BooleanVar()
            self.special_check = tk.Checkbutton(password_frame, text="Znaki specjalne", variable=self.special_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.special_check.grid(row=3, column=0, columnspan=2, sticky="w")
            self.generate_button = tk.Button(password_frame, text="Generuj hasło", bg=BUTTON_COLOR, fg="white",
                                             command=self.generate_password)
            self.generate_button.grid(row=4, column=0, columnspan=2, pady=(5, 0))
    
            self.confirm_button = tk.Button(root, text="Zatwierdź", bg=BUTTON_COLOR, fg="white", width=30)
            self.confirm_button.grid(row=1, column=0, columnspan=2, pady=(0, 20))
    
    
        def generate_password(self):
            length = int(self.length_entry.get())
            self.password = ""
            for i in range(length):
                if i == 0 and self.letters_var.get():
                    self.password += random.choice(UPPERCASE)
                elif i == 1 and self.digits_var.get():
                    self.password += random.choice(DIGITS)
                elif i == 2 and self.special_var.get():
                    self.password += random.choice(SPECIAL)
                else:
                    self.password += random.choice(LOWERCASE)
            messagebox.showinfo("Hasło", self.password)
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  25. Krok 25: Zatwierdź: dane pracownika w komunikacie

    Kryteria: , ,

    R.3.5 Po wciśnięciu przynajmniej jednego przycisku wywoływana jest funkcja

    R.3.8 Hasło oraz zestawy znaków są przechowywane w zmiennych typu napisowego, hasło jest widoczne dla obu metod obsługujących kliknięcia przycisków

    R.3.9 Po wybraniu przycisku „Zatwierdź” wyświetla się komunikat w nowym oknie w którym ujęte są: imię, nazwisko, wartość wybrana z listy rozwijalnej oraz wygenerowane wcześniej hasło

    ok. 3 min
    Polecenie z arkusza

    Działanie aplikacji po wybraniu przycisku „Zatwierdź”: wyświetlany jest komunikat z wypełnionymi danymi pracownika oraz wygenerowanym wcześniej hasłem, zgodnie z obrazem 3.

    Druga metoda odczytuje tekst pól (get() pola Entry), wybraną pozycję listy (get() listy Combobox zwraca jej tekst) i pole self.password - to samo, które ustawiła metoda generowania. f-napis składa komunikat jak na obrazie 3. Przycisk wiąże z nią command=self.confirm (R.3.9).

    Przejdź w podglądzie scenariusz z obrazu 3: Zofia, Kowalska, Tester, 5 znaków z cyframi, „Generuj hasło”, potem „Zatwierdź” - w komunikacie jest to samo hasło.

    Typowe błędy

    Hasło w zmiennej lokalnej metody (password = "" zamiast self.password = "") znika po jej zakończeniu - metoda confirm go nie zobaczy (błąd NameError) albo pokaże puste pole z konstruktora.

    desktopowa/main.py Python zmienione: 8 linii
    import random
    import tkinter as tk
    from tkinter import messagebox, ttk
    
    BACKGROUND = "#B0C4DE"
    BUTTON_COLOR = "#4682B4"
    LOWERCASE = "abcdefghijklmnopqrstuvwxyz"
    UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    DIGITS = "0123456789"
    SPECIAL = "!@#$%^&*()_+-="
    
    
    class EmployeeWindow:
        def __init__(self, root):
            self.password = ""
            root.title("Dodaj pracownika - Bartosz Bryniarski")
            root.geometry("650x300")
            root.configure(bg=BACKGROUND)
            root.columnconfigure(0, weight=1)
            root.columnconfigure(1, weight=1)
            root.rowconfigure(0, weight=1)
    
            employee_frame = tk.LabelFrame(root, text="Dane pracownika", bg=BACKGROUND, padx=10, pady=10)
            employee_frame.grid(row=0, column=0, padx=(20, 10), pady=20, sticky="nsew")
            tk.Label(employee_frame, text="Imię", bg=BACKGROUND).grid(row=0, column=0, sticky="w", pady=5)
            self.first_name_entry = tk.Entry(employee_frame, width=22)
            self.first_name_entry.grid(row=0, column=1, pady=5)
            tk.Label(employee_frame, text="Nazwisko", bg=BACKGROUND).grid(row=1, column=0, sticky="w", pady=5)
            self.last_name_entry = tk.Entry(employee_frame, width=22)
            self.last_name_entry.grid(row=1, column=1, pady=5)
            tk.Label(employee_frame, text="Stanowisko", bg=BACKGROUND).grid(row=2, column=0, sticky="w", padx=(0, 10), pady=5)
            self.position_combobox = ttk.Combobox(employee_frame, width=19, state="readonly",
                                                  values=["Kierownik", "Starszy programista", "Młodszy programista", "Tester"])
            self.position_combobox.grid(row=2, column=1, pady=5)
    
            password_frame = tk.LabelFrame(root, text="Generowanie hasła", bg=BACKGROUND, padx=10, pady=10)
            password_frame.grid(row=0, column=1, padx=(10, 20), pady=20, sticky="nsew")
            tk.Label(password_frame, text="Ile znaków?", bg=BACKGROUND).grid(row=0, column=0, sticky="w", padx=(0, 10))
            self.length_entry = tk.Entry(password_frame, width=12)
            self.length_entry.grid(row=0, column=1)
            self.letters_var = tk.BooleanVar(value=True)
            self.letters_check = tk.Checkbutton(password_frame, text="Małe i wielkie litery", variable=self.letters_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.letters_check.grid(row=1, column=0, columnspan=2, sticky="w", pady=(8, 0))
            self.digits_var = tk.BooleanVar()
            self.digits_check = tk.Checkbutton(password_frame, text="Cyfry", variable=self.digits_var,
                                               bg=BACKGROUND, activebackground=BACKGROUND)
            self.digits_check.grid(row=2, column=0, columnspan=2, sticky="w")
            self.special_var = tk.BooleanVar()
            self.special_check = tk.Checkbutton(password_frame, text="Znaki specjalne", variable=self.special_var,
                                                bg=BACKGROUND, activebackground=BACKGROUND)
            self.special_check.grid(row=3, column=0, columnspan=2, sticky="w")
            self.generate_button = tk.Button(password_frame, text="Generuj hasło", bg=BUTTON_COLOR, fg="white",
                                             command=self.generate_password)
            self.generate_button.grid(row=4, column=0, columnspan=2, pady=(5, 0))
    
            self.confirm_button = tk.Button(root, text="Zatwierdź", bg=BUTTON_COLOR, fg="white", width=30,
                                            command=self.confirm)
            self.confirm_button.grid(row=1, column=0, columnspan=2, pady=(0, 20))
    
    
        def generate_password(self):
            length = int(self.length_entry.get())
            self.password = ""
            for i in range(length):
                if i == 0 and self.letters_var.get():
                    self.password += random.choice(UPPERCASE)
                elif i == 1 and self.digits_var.get():
                    self.password += random.choice(DIGITS)
                elif i == 2 and self.special_var.get():
                    self.password += random.choice(SPECIAL)
                else:
                    self.password += random.choice(LOWERCASE)
            messagebox.showinfo("Hasło", self.password)
    
        def confirm(self):
            first_name = self.first_name_entry.get()
            last_name = self.last_name_entry.get()
            position = self.position_combobox.get()
            messagebox.showinfo("Dane pracownika", f"Dane pracownika: {first_name} {last_name} {position} Hasło: {self.password}")
    
    
    root = tk.Tk()
    app = EmployeeWindow(root)
    root.mainloop()
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  26. Krok 26: Folder desktopowa: archiwum i źródło

    ok. 2 min
    Polecenie z arkusza

    Kod aplikacji przygotuj do nagrania na płytę. W folderze desktopowa powinno znaleźć się archiwum całego projektu o nazwie desktopowa.zip, pliki źródłowe, które były modyfikowane (interfejs użytkownika i logika aplikacji) oraz plik uruchomieniowy, jeżeli to możliwe.

    Pakujesz folder projektu do desktopowa.zip, obok kopiujesz main.py - w Tkinterze interfejs i logika są w tym samym pliku. Pliku uruchomieniowego nie ma.

    Okno aplikacji bez zmian - taki jak po kroku 25.

  27. Etap 4: Dokumentacja aplikacji

    Kroki 27-30 · po tym etapie masz 32 z 32 kryteriów (pkt)

    Krok 27: Dokumentacja funkcji nwd

    Kryteria: , , , ,

    R.4.1 Dla funkcji wyznaczającej NWD z aplikacji konsolowej zapisano nagłówek w postaci komentarza zgodny z Listingiem 1 z arkusza egzaminacyjnego (nie należy brać pod uwagę liczby gwiazdek), komentarz może być wieloliniowy lub kilku jednoliniowy

    R.4.2 W komentarzu ujęto nazwę funkcji oraz opisano jej działanie, np. "funkcja wyznacza NWD metodą Euklidesa"

    R.4.3 W komentarzu ujęto wszystkie argumenty funkcji: nazwy i krótki opis

    R.4.4 W komentarzu ujęto nazwę typu zwracanego i krótki opis co jest zwracane

    R.4.5 W komentarzu ujęto numer zdającego

    ok. 4 min
    Polecenie z arkusza

    W kodzie źródłowym aplikacji konsolowej utwórz nagłówek funkcji wyznaczającej NWD, według wzoru. Nagłówek powinien znaleźć się nad definicją funkcji. W miejscu nawiasów <> należy podać odpowiednie opisy. W miejscu autor należy podać numer zdającego.

    W Pythonie opis funkcji to docstring - napis w potrójnym cudzysłowie zaraz pod linią def; gwiazdki z listingu 1 przepisujesz do środka. Arkusz każe umieścić nagłówek „nad definicją”, a klucz (R.4.1) przyjmuje „komentarz wieloliniowy lub kilka jednoliniowych”. Docstring jest w Pythonie właśnie wieloliniowym komentarzem dokumentującym - ale jeśli chcesz trzymać się słowa „nad”, ten sam tekst możesz napisać liniami zaczynanymi od # nad def nwd.

    Pola wypełniasz zgodnie z kodem: nazwa i działanie (R.4.2), oba parametry z opisem (R.4.3), zwracany typ int z opisem (R.4.4), autor (R.4.5) - na egzaminie numer PESEL, tu Bartosz Bryniarski.

    konsola/main.py Python zmienione: 11 linii
    def nwd(a, b):
        """
        ******************************************************
        nazwa funkcji:       nwd
        opis funkcji:        wyznacza największy wspólny dzielnik dwóch liczb algorytmem Euklidesa
                             (odejmuje mniejszą liczbę od większej, aż obie będą równe)
        parametry:           a - pierwsza liczba całkowita dodatnia
                             b - druga liczba całkowita dodatnia
        zwracany typ i opis: int - największy wspólny dzielnik liczb a i b
        autor:               Bartosz Bryniarski
        ******************************************************
        """
        while a != b:
            if a > b:
                a = a - b
            else:
                b = b - a
        return a
    
    
    def read_positive(prompt):
        number = int(input(prompt))
        while number <= 0:
            number = int(input("Liczba musi być dodatnia. " + prompt))
        return number
    
    
    def main():
        a = read_positive("Podaj liczbę a: ")
        b = read_positive("Podaj liczbę b: ")
        print(f"NWD({a}, {b}) = {nwd(a, b)}")
    
    
    if __name__ == "__main__":
        main()
    Wynik programu po tym kroku
    > python main.py
    Podaj liczbę a: 48
    Podaj liczbę b: 18
    NWD(48, 18) = 6
  28. Krok 28: Zrzuty konsola1 i konsola2

    Kryteria: , ,

    R.1.7 Podjęto próbę skompilowania kodu, co udokumentowano obrazem przedstawiającym uruchomiony program lub jego kompilację

    R.2.7 Program kompiluje się i uruchamia w konsoli, co udokumentowano zrzutem ekranu

    R.4.6 Zapisano przynajmniej jeden zrzut ekranu z uruchomienia lub kompilacji aplikacji konsolowej, na zrzucie widoczne jest środowisko, w którym powstała aplikacja

    ok. 3 min
    Polecenie z arkusza

    Wykonaj zrzuty ekranu dokumentujące uruchomienie aplikacji. Zrzuty powinny obejmować cały obszar ekranu monitora z widocznym paskiem zadań. Aplikacja konsolowa – zrzuty nazwane: konsola1, konsola2, ...

    Dwa uruchomienia w PyCharm z różnymi liczbami i zrzut całego ekranu z oknem PyCharm, panelem Run i paskiem zadań: konsola1.png (48 i 18 → 6), konsola2.png (17 i 5 → 1). Zrzuty idą do folderu dokumentacja.

    Wynik programu po tym kroku
    > python main.py
    Podaj liczbę a: 48
    Podaj liczbę b: 18
    NWD(48, 18) = 6
    
    > python main.py
    Podaj liczbę a: 17
    Podaj liczbę b: 5
    NWD(17, 5) = 1
  29. Krok 29: Zrzuty desktop1 - desktop3

    Kryteria: ,

    R.3.10 Aplikacja kompiluje się i uruchamia, co udokumentowano zrzutem ekranu, jej układ po uruchomieniu jest zgodny z obrazem 1 w arkuszu egzaminacyjnym. Zaznaczone jest pierwsze pole wyboru

    R.4.7 Zapisano przynajmniej jeden zrzut ekranu z uruchomienia lub kompilacji aplikacji desktopowej, na zrzucie widoczne jest środowisko, w którym powstała aplikacja

    ok. 4 min
    Polecenie z arkusza

    Wykonać należy co najmniej tyle zrzutów, ile interakcji podejmuje aplikacja (np. stan początkowy, po wypisaniu hasła, po wyświetleniu danych itd.). Aplikacja desktopowa – zrzuty nazwane: desktop1, desktop2, ...

    Uruchamiasz aplikację w PyCharm i robisz zrzuty całego ekranu z PyCharm w tle:

    • desktop1.png - stan początkowy (obraz 1, R.3.10),
    • desktop2.png - komunikat z wygenerowanym hasłem (obraz 2) - klucz sprawdza generowanie dwa razy, więc warto dorobić drugi zrzut z innym hasłem,
    • desktop3.png - komunikat po „Zatwierdź” (obraz 3).

    Podgląd to prawdziwy zrzut obszaru okna tej aplikacji (Tkinter, Windows 11) z danymi z obrazu 3, tuż przed kliknięciem „Generuj hasło”.

    Okno aplikacji po tym kroku
    Okno aplikacji po kroku 29
  30. Krok 30: Dokument egzamin z podpisanymi zrzutami

    Kryteria:

    R.4.8 Dokumentacja zawiera: nazwę systemu operacyjnego, nazwy środowisk, nazwy języków programowania

    ok. 4 min
    Polecenie z arkusza

    W edytorze tekstu pakietu biurowego utwórz plik z dokumentacją i nazwij go egzamin. Dokument powinien zawierać podpisane zrzuty ekranu oraz zapisane informacje: nazwę systemu operacyjnego, nazwy środowisk programistycznych, nazwy języków programowania, opcjonalnie komentarz do wykonanej pracy.

    W Wordzie albo Writerze dokument egzamin w folderze dokumentacja: trzy informacje o narzędziach i - w tym arkuszu - także wklejone zrzuty z podpisami (np. „konsola1 - NWD(48, 18)”). W plikach kroku jest tekst tej części jako plik tekstowy.

    egzamin.txt Tekst nowy plik
    System operacyjny: Windows 11
    Środowisko programistyczne: PyCharm Community (aplikacja konsolowa i desktopowa)
    Języki programowania: Python (obie aplikacje), biblioteka Tkinter (aplikacja desktopowa)
    Zrzuty: konsola1, konsola2, desktop1, desktop2, desktop3 - wklejone poniżej z podpisami.
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  31. Etap 5: Porównanie z wariantem C# + WPF

    Kroki 31-32 · po tym etapie masz 32 z 32 kryteriów (pkt)

    Krok 31: Konsola: kontrola liczb w Pythonie, typ uint w C#

    ok. 2 min
    Polecenie z arkusza

    Liczby a i b należą do zbioru liczb całkowitych dodatnich (odpowiedni typ lub kontrola poprawności wpisanej liczby).

    Algorytm jest w obu językach identyczny - pętla while, if/else, odejmowanie - bo tego wymaga schemat. Różnica jest w typach:

    • C# ma typ bez znaku uint: liczba ujemna nie przejdzie przez uint.Parse, więc klucz uznaje sam typ za „odpowiedni” (ale zero wciąż przechodzi - dlatego wariant C# poprawia to w dobrych praktykach),
    • Python takiego typu nie ma, a klucz wymaga od niego jawnej konwersji (int(...)) - więc zakres sprawdzasz samodzielnie funkcją read_positive, od razu odrzucając także zero.

    W Pythonie nie piszesz typów przy parametrach, więc w dokumentacji funkcji typ zwracany (int) trzeba wpisać z głowy - musi zgadzać się z tym, co funkcja naprawdę zwraca.

    Okno aplikacji bez zmian - taki jak po kroku 30.

  32. Krok 32: Okno: Tkinter a WPF - odpowiedniki kontrolek

    ok. 3 min
    Polecenie z arkusza

    Za pomocą dostępnego na stanowisku egzaminacyjnym środowiska programistycznego wykonaj aplikację desktopową do wprowadzania danych pracownika z możliwością generowania hasła.

    Te same kontrolki pod innymi nazwami:

    • GroupBox w WPF → LabelFrame, TextBox → Entry, ComboBox → ttk.Combobox, CheckBox → Checkbutton ze zmienną BooleanVar, MessageBox.Show(...) → messagebox.showinfo(...),
    • IsChecked == true (typ bool? w WPF) → self.letters_var.get() (zwykłe True/False),
    • pole klasy okna private string password → self.password - w obu wariantach hasło żyje w obiekcie okna, żeby widziały je obie metody przycisków.

    Największa różnica to tło: w WPF etykiety i pola wyboru są przezroczyste i widać pod nimi tło okna, w Tkinterze każda kontrolka maluje własne tło - stąd bg=BACKGROUND przy każdej z nich. Na egzaminie wybierz to, co znasz lepiej: punkty za aplikację desktopową (R.3) są w obu technologiach te same.

    Okno aplikacji bez zmian - taki jak po kroku 30.

  33. Etap 6: Oddanie pracy

    Krok 33 · po tym etapie masz 32 z 32 kryteriów (pkt)

    Krok 33: Nagranie płyty

    ok. 5 min
    Polecenie z arkusza

    UWAGA: Nagraj płytę z rezultatami pracy. W folderze z numerem zdającego powinny się znajdować podfoldery desktopowa, dokumentacja, konsola. W folderze dokumentacja: pliki ze zrzutami oraz plik egzamin. W folderze konsola: spakowany cały projekt aplikacji konsolowej, źródło, opcjonalnie plik uruchomieniowy. W folderze desktopowa: spakowany cały projekt aplikacji desktopowej, pliki ze źródłami interfejsu i logiki, opcjonalnie plik uruchomieniowy.

    Sprawdzasz zawartość przed nagraniem:

    • dokumentacja: konsola1.png, konsola2.png, desktop1.png - desktop3.png, egzamin,
    • konsola: konsola.zip (spakowany po dopisaniu dokumentacji), main.py,
    • desktopowa: desktopowa.zip, main.py.

    Nagrywasz, sprawdzasz odczyt płyty, opisujesz ją numerem zdającego.

    Okno aplikacji bez zmian - taki jak po kroku 30.

Podgląd

To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.

Autor rozwiązania: . Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.