INF.04-01-25.01-SGStyczeń 2025 · 30 kroków · ok. 87 min · 35 kryteriów z klucza
Rozwiązanie krok po kroku: Aplikacja konsolowa i galeria zdjęć (C# + React)
Rozwiązujemy arkusz z galerią zdjęć w wersji C# + React: w konsoli klasa z prywatną tablicą liczb losowych i metodami wypisującymi, wyszukującymi, wybierającymi liczby nieparzyste i liczącymi średnią, a w przeglądarce jednokomponentowa aplikacja React, w której przełączniki Bootstrapa filtrują zdjęcia według kategorii, a przycisk „Pobierz” zwiększa licznik pobrań. Do tego dokumentacja metody według wzoru z arkusza, zrzuty i plik egzamin.
Kroki idą zdanie po zdaniu za treścią arkusza - każdy zaczyna się cytatem wymagania, a kod pokazuje dokładnie te linie, które je spełniają. Najpierw piszemy najprostszą wersję, której wymaga klucz, a w etapie „Dobre praktyki” przerabiamy ją na kod z prawdziwych projektów.
Co widać w podglądzie. Wyniki programu konsolowego pochodzą z prawdziwego uruchomienia kodu z danego kroku (.NET 10) - tablica jest za każdym razem inna, a liczbę do wyszukania wpisujemy dopiero po zobaczeniu tablicy. Podgląd aplikacji webowej wykonuje kod komponentu z kroku w prawdziwym Reakcie z Bootstrapem i zdjęciami z archiwum pliki3.zip: możesz przełączać kategorie i klikać „Pobierz”.
Arkusz pozwala też na C++, Javę albo Pythona w konsoli i na Angulara w części webowej - ten wariant to jedna z możliwych dróg.
Jak liczą się punkty. Klucz ma 35 kryteriów (R.1.1 - R.4.8), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 27 punktów - 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.
Krok 1 · po tym etapie masz 0 z 35 kryteriów (pkt)
Krok 1: Folder zdającego i trzy podfoldery
ok. 3 min
Polecenie z arkusza
Utwórz folder i nazwij go numerem zdającego. W folderze utwórz podfoldery: konsolowa, webowa, dokumentacja.
Na pulpicie zakładamy folder o nazwie z numerem PESEL, a w nim konsolowa, webowa i dokumentacja. Archiwum pliki3.zip z pulpitu (hasło z arkusza: K@tegorie)) rozpakujemy przy aplikacji webowej - są w nim zdjęcia obraz1.jpg - obraz12.jpg i plik dane.txt z listą obiektów galerii.
Projekty możemy trzymać w domyślnych folderach środowisk - na koniec pakujemy je i kopiujemy do podfolderów.
Etap 2: Aplikacja konsolowa
Kroki 2-10 · po tym etapie masz 15 z 35 kryteriów (pkt)
Krok 2: Projekt konsola w C# i pusta klasa
ok. 3 min
Polecenie z arkusza
Zastosowany obiektowy język programowania zgodny z zainstalowanym na stanowisku egzaminacyjnym: C++ lub C#, lub Java, lub Python. Tablica oraz operacje na niej wykonywane są implementowane z wykorzystaniem klasy.
Wybieramy C#. W Visual Studio: Utwórz nowy projekt → Aplikacja konsolowa, nazwa konsola (stąd archiwum konsola.zip); w terminalu to samo daje dotnet new console -n konsola. W pliku Program.cs zastępujemy przykładowy kod dwiema klasami: Table - na tablicę i operacje na niej (arkusz dopuszcza dla klasy nazwę ogólną typu „tablica”) - oraz Program z metodą Main, od której zaczyna się program.
konsola/Program.csC#nowy plik
classTable{}classProgram{staticvoidMain() { }}
Krok 3: Dwa pola prywatne
Kryteria: ,
R.2.1 W programie zdefiniowana klasa z dwoma polami private, jedno jest typu tablicowego, drugie typu całkowitego, w programie głównym jest powołany obiekt klasy z liczbą > 20 (W języku Python, dla pola typu private, dwa podkreślniki przed nazwą )
R.1.5 Zastosowane typy pól pasujące do problemu, np. tablica przechowuje liczby całkowite, rozmiar i wartość szukana są liczbami całkowitymi, średnia jest liczbą. W przypadku Python, typ wynika z przypisanych danych
ok. 2 min
Polecenie z arkusza
Pola klasy: Tablica liczb całkowitych (ma być tradycyjną tablicą, a w Python listą). Liczba elementów tablicy zapisana jako liczba całkowita. Oba pola są dostępne tylko w tej klasie oraz niedostępne dla klas potomnych.
private - pole widać tylko wewnątrz klasy Table; klasy potomne go nie widzą (to odróżnia private od protected, który byłby dostępny w klasie potomnej).
Tablica nie ma jeszcze rozmiaru - utworzy ją konstruktor.
Typowe błędy
protected zamiast private - pole byłoby dostępne w klasach potomnych, a arkusz wprost tego zabrania (R.2.1).
R.2.2 Konstruktor klasy ma jeden parametr i wypełnia tablicę wartościami losowymi od 1 do 1000 (tolerancja +/-1 ) oraz ustawia pole liczby elementów tablicy wartością parametru konstruktora
R.2.4 Przynajmniej jedna pętla działająca na tablicy jest ograniczona do liczby wypełnionych elementów w tablicy
R.1.2 Kod zapisany z wcięciami dla zagnieżdżeń bloków
ok. 3 min
Polecenie z arkusza
Konstruktor klasy: Przyjmuje jako argument rozmiar tablicy. Ustawia wartość pola liczby elementów tablicy na wartość argumentu. Wypełnia tablicę, będącą polem klasy, pseudolosowymi liczbami całkowitymi z zakresu od 1 do 1000.
Konstruktor ma nazwę klasy i jeden parametr size:
count = size - zapamiętujemy liczbę elementów,
new int[size] - tworzymy tablicę tej wielkości,
random.Next(1, 1001) - liczba losowa od 1 do 1000: górna granica w Next nie jest wliczana, dlatego piszemy 1001.
Pętla for biegnie do count - wszystkie operacje mają być ograniczone tym polem (R.2.4).
Typowe błędy
random.Next(1, 1000) nigdy nie wylosuje 1000 - zakres byłby 1-999.
konsola/Program.csC#zmienione: 11 linii
classTable{privateint[] numbers;privateint count;publicTable(int size) { count = size; numbers = newint[size]; Random random = new Random();for (int i = 0; i < count; i++) { numbers[i] = random.Next(1, 1001); } }}classProgram{staticvoidMain() { }}
Krok 5: Metoda Display i pierwszy test w Main
Kryteria: , , ,
R.2.3 Metoda szukająca pobiera argument typu liczbowego. Metody są typu: wyświetlająca - bez typu (np. void) wyszukująca - typ całkowity wyświetlająca nieparzyste - typ całkowity licząca średnią - typ liczbowy (wystarczy, że zastosowane dla trzech metod)
R.2.7 Program wyświetla wszystkie elementy tablicy wg. wzoru "<index_tablicy>: <wartość>"
R.1.7 Program podejmuje komunikację z użytkownikiem, wyświetlane komunikaty są znaczące
R.1.3 Użyte znaczące polskie lub angielskie nazewnictwo metod
ok. 3 min
Polecenie z arkusza
Metoda wyświetlająca wszystkie elementy tablicy w postaci „<index_tablicy>: <wartość>”. Nie zwraca wartości. Wszystkie metody są dostępne poza klasą. Program główny tworzy obiekt klasy z rozmiarem tablicy większym od 20.
public - metoda dostępna poza klasą (pola zostają prywatne, a dostęp do nich daje właśnie metoda),
void - metoda niczego nie zwraca, tylko wypisuje,
$"{i}: {numbers[i]}" - napis interpolowany: w klamry trafiają indeks i wartość, jak we wzorze z arkusza.
W Main tworzymy obiekt z tablicą 50 elementów (więcej niż 20) i wywołujemy metodę. Wynik prawdziwego uruchomienia obok - każde uruchomienie losuje inne liczby.
konsola/Program.csC#zmienione: 11 linii
classTable{privateint[] numbers;privateint count;publicTable(int size) { count = size; numbers = newint[size]; Random random = new Random();for (int i = 0; i < count; i++) { numbers[i] = random.Next(1, 1001); } }publicvoidDisplay() {for (int i = 0; i < count; i++) { Console.WriteLine($"{i}: {numbers[i]}"); } }}classProgram{staticvoidMain() { Table table = new Table(50); Console.WriteLine("Elementy tablicy:"); table.Display(); }}
R.2.3 Metoda szukająca pobiera argument typu liczbowego. Metody są typu: wyświetlająca - bez typu (np. void) wyszukująca - typ całkowity wyświetlająca nieparzyste - typ całkowity licząca średnią - typ liczbowy (wystarczy, że zastosowane dla trzech metod)
R.2.5 W metodzie wyszukującej, gdy odnaleziono pierwsze wystąpienie wartości szukanej w tablicy, działanie pętli jest zakończone lub została zastosowana wbudowana metoda lub funkcja
ok. 3 min
Polecenie z arkusza
Metoda wyszukująca pierwsze wystąpienie wartości, przekazanej jako argument. Metoda zwraca indeks szukanego elementu lub liczbę -1, gdy elementu nie znaleziono.
Pętla porównuje kolejne elementy z szukaną wartością. Przy pierwszym trafieniu return i od razu kończy metodę - pętla nie szuka dalej, więc zwracamy indeks pierwszego wystąpienia (R.2.5). Jeśli pętla dobiegnie końca, nic nie znaleziono i zwracamy -1 - indeks, którego w tablicy nie ma. Typ zwracany to int (R.2.3).
Typowe błędy
Zapamiętanie indeksu w zmiennej bez przerwania pętli zwraca OSTATNIE wystąpienie, gdy liczba powtarza się w tablicy.
konsola/Program.csC#zmienione: 12 linii
classTable{privateint[] numbers;privateint count;publicTable(int size) { count = size; numbers = newint[size]; Random random = new Random();for (int i = 0; i < count; i++) { numbers[i] = random.Next(1, 1001); } }publicvoidDisplay() {for (int i = 0; i < count; i++) { Console.WriteLine($"{i}: {numbers[i]}"); } }publicintFind(intvalue) {for (int i = 0; i < count; i++) {if (numbers[i] == value) {return i; } }return -1; }}classProgram{staticvoidMain() { Table table = new Table(50); Console.WriteLine("Elementy tablicy:"); table.Display(); }}
R.2.8 Program wyszukuje liczbę w tablicy i wyświetla jej indeks, gdy nie odnaleziono liczby nic nie wyświetla
R.1.7 Program podejmuje komunikację z użytkownikiem, wyświetlane komunikaty są znaczące
R.1.4 Użyte polskie lub angielskie nazewnictwo zmiennych i obiektów. Nazewnictwo jest znaczące. Wyjątkami od reguły są zmienne: bufor, tmp, iteratory pętli. Kryterium nie jest spełnione tylko wtedy, gdy nazwy zmiennych nic nie znaczą, np.: x, foo (Uwaga! klasa i pole tablicowe klasy może mieć nazwę ogólną np tab, tablica itp )
ok. 3 min
Polecenie z arkusza
Program główny sprawdza działanie metody wyszukującej: jeżeli wartość została wyszukana, wyświetlany jest w programie głównym komunikat z wartością indeksu wyszukanej. W przeciwnym wypadku nic nie jest wyświetlane (taka sytuacja ma miejsce na obrazie 1).
Liczbę do wyszukania podaje użytkownik: Console.ReadLine() czyta wiersz, a int.Parse zamienia go na liczbę. Komunikat wypisujemy tylko wtedy, gdy metoda nie zwróciła -1 - przy braku liczby program nic nie mówi, jak na obrazie 1.
Podgląd to dwa prawdziwe uruchomienia: najpierw wpisana liczba, która jest w tablicy (spod indeksu 7 - wynik pokazuje indeks jej pierwszego wystąpienia), potem 1001 - spoza zakresu, więc bez komunikatu.
konsola/Program.csC#zmienione: 8 linii
classTable{privateint[] numbers;privateint count;publicTable(int size) { count = size; numbers = newint[size]; Random random = new Random();for (int i = 0; i < count; i++) { numbers[i] = random.Next(1, 1001); } }publicvoidDisplay() {for (int i = 0; i < count; i++) { Console.WriteLine($"{i}: {numbers[i]}"); } }publicintFind(intvalue) {for (int i = 0; i < count; i++) {if (numbers[i] == value) {return i; } }return -1; }}classProgram{staticvoidMain() { Table table = new Table(50); Console.WriteLine("Elementy tablicy:"); table.Display(); Console.Write("Podaj liczbę do wyszukania: ");int searched = int.Parse(Console.ReadLine());int index = table.Find(searched);if (index != -1) { Console.WriteLine($"Liczba {searched} jest pod indeksem {index}"); } }}
R.2.3 Metoda szukająca pobiera argument typu liczbowego. Metody są typu: wyświetlająca - bez typu (np. void) wyszukująca - typ całkowity wyświetlająca nieparzyste - typ całkowity licząca średnią - typ liczbowy (wystarczy, że zastosowane dla trzech metod)
R.2.9 Program wyświetla na ekranie liczby nieparzyste oraz liczbę ich wystąpień
ok. 3 min
Polecenie z arkusza
Metoda wyświetlająca wszystkie wartości nieparzyste z tablicy i zwracająca ich liczbę. Program główny wyświetla liczby nieparzyste oraz ich ilość z odpowiednim komentarzem.
Liczba jest nieparzysta, gdy reszta z dzielenia przez 2 (%) jest różna od zera. Metoda wypisuje każdą taką liczbę i zwiększa licznik oddCount, a na końcu go zwraca (int). Program główny poprzedza listę nagłówkiem i wypisuje zwróconą liczbę z komentarzem - jak na obrazie 1.
Typowe błędy
numbers[i] % 2 == 1 działa tylko dla liczb dodatnich - dla ujemnych reszta w C# wynosi -1. Tu liczby są od 1 do 1000, ale != 0 jest pewniejsze.
konsola/Program.csC#zmienione: 18 linii
classTable{privateint[] numbers;privateint count;publicTable(int size) { count = size; numbers = newint[size]; Random random = new Random();for (int i = 0; i < count; i++) { numbers[i] = random.Next(1, 1001); } }publicvoidDisplay() {for (int i = 0; i < count; i++) { Console.WriteLine($"{i}: {numbers[i]}"); } }publicintFind(intvalue) {for (int i = 0; i < count; i++) {if (numbers[i] == value) {return i; } }return -1; }publicintDisplayOdd() {int oddCount = 0;for (int i = 0; i < count; i++) {if (numbers[i] % 2 != 0) { Console.WriteLine(numbers[i]); oddCount++; } }return oddCount; }}classProgram{staticvoidMain() { Table table = new Table(50); Console.WriteLine("Elementy tablicy:"); table.Display(); Console.Write("Podaj liczbę do wyszukania: ");int searched = int.Parse(Console.ReadLine());int index = table.Find(searched);if (index != -1) { Console.WriteLine($"Liczba {searched} jest pod indeksem {index}"); } Console.WriteLine("Liczby nieparzyste:");int oddCount = table.DisplayOdd(); Console.WriteLine($"Razem nieparzystych: {oddCount}"); }}
R.2.3 Metoda szukająca pobiera argument typu liczbowego. Metody są typu: wyświetlająca - bez typu (np. void) wyszukująca - typ całkowity wyświetlająca nieparzyste - typ całkowity licząca średnią - typ liczbowy (wystarczy, że zastosowane dla trzech metod)
R.2.10 Program liczy średnią arytmetyczną (suma / rozmiar) oraz wyświetla ją na ekranie
R.1.5 Zastosowane typy pól pasujące do problemu, np. tablica przechowuje liczby całkowite, rozmiar i wartość szukana są liczbami całkowitymi, średnia jest liczbą. W przypadku Python, typ wynika z przypisanych danych
R.1.1 Kod źródłowy zapisany w sposób czytelny: instrukcje w osobnych liniach, stosowane spacje pomiędzy operatorami, konsekwentnie stosowana wybrana konwencja dla nawiasów klamrowych instrukcji blokowej
ok. 3 min
Polecenie z arkusza
Metoda licząca średnią arytmetyczną wartości w tablicy i zwracająca tą wartość. Program główny wyświetla wartość średniej.
Sumujemy elementy, a wynik dzielimy przez count. (double)sum zamienia sumę na liczbę rzeczywistą przed dzieleniem - inaczej C# podzieliłby dwie liczby całkowite i obciął część ułamkową. Metoda zwraca double. W Main formatem :F2 pokazujemy dwa miejsca po przecinku (przecinek zamiast kropki wynika z polskich ustawień systemu).
Typowe błędy
return sum / count; przy dwóch liczbach int daje dzielenie całkowite - średnia 525,73 wyjdzie jako 525.
konsola/Program.csC#zmienione: 13 linii
classTable{privateint[] numbers;privateint count;publicTable(int size) { count = size; numbers = newint[size]; Random random = new Random();for (int i = 0; i < count; i++) { numbers[i] = random.Next(1, 1001); } }publicvoidDisplay() {for (int i = 0; i < count; i++) { Console.WriteLine($"{i}: {numbers[i]}"); } }publicintFind(intvalue) {for (int i = 0; i < count; i++) {if (numbers[i] == value) {return i; } }return -1; }publicintDisplayOdd() {int oddCount = 0;for (int i = 0; i < count; i++) {if (numbers[i] % 2 != 0) { Console.WriteLine(numbers[i]); oddCount++; } }return oddCount; }publicdoubleAverage() {int sum = 0;for (int i = 0; i < count; i++) { sum += numbers[i]; }return (double)sum / count; }}classProgram{staticvoidMain() { Table table = new Table(50); Console.WriteLine("Elementy tablicy:"); table.Display(); Console.Write("Podaj liczbę do wyszukania: ");int searched = int.Parse(Console.ReadLine());int index = table.Find(searched);if (index != -1) { Console.WriteLine($"Liczba {searched} jest pod indeksem {index}"); } Console.WriteLine("Liczby nieparzyste:");int oddCount = table.DisplayOdd(); Console.WriteLine($"Razem nieparzystych: {oddCount}");double average = table.Average(); Console.WriteLine($"Średnia wszystkich elementów: {average:F2}"); }}
Krok 10: Folder konsolowa: archiwum, źródło i plik wykonywalny
ok. 2 min
Polecenie z arkusza
Kod aplikacji przygotuj do nagrania na płytę. W folderze konsolowa powinno znaleźć się archiwum całego projektu o nazwie konsola.zip, plik z kodem źródłowym programu oraz plik wykonywalny, jeżeli istnieje.
Folder projektu pakujemy do konsola.zip, obok kopiujemy Program.cs i plik wykonywalny konsola.exe z bin\Debug\net... (albo bin\Release\...). Archiwum robimy na samym końcu pracy, po dopisaniu komentarza z części III - inaczej w konsola.zip zostałaby wersja bez dokumentacji.
Kroki 11-23 · po tym etapie masz 24 z 35 kryteriów (pkt)
Krok 11: Nowy projekt React w Vite
ok. 4 min
Polecenie z arkusza
Z zastosowaniem dostępnego na stanowisku egzaminacyjnym frameworka Angular lub biblioteki React.js wykonaj aplikację internetową typu front-end realizującą funkcję kategoryzacji zdjęć w galerii.
Wybieramy React. Projekt zakładamy w terminalu narzędziem Vite:
npm create vite@latest web -- --template react
cd web
npm install
Nazwa web od razu pasuje do archiwum web.zip. Vite tworzy src/main.jsx (wstawia komponent App do strony) i src/App.jsx z przykładowym licznikiem, który zastąpimy. npm run dev uruchamia serwer pod http://localhost:5173.
R.3.2 Do projektu dołączona biblioteka Bootstrap oraz sformatowany obraz: margin: 5px; oraz zaokrąglone rogi (border-radius lub klasa rounded )
ok. 2 min
Polecenie z arkusza
Przyciski oraz pola switch są stylowane zgodnie z przykładami w tabeli 1. React.js: To use Bootstrap add: import 'bootstrap/dist/css/bootstrap.css'.
npm install bootstrap w folderze web, a potem import z tabeli 1 w main.jsx - w miejsceimport './index.css', bo arkusz stylów z szablonu Vite wyśrodkowuje stronę i ustawia ciemne tło, a galeria ma zaczynać się przy lewej krawędzi (obraz 2).
Typowe błędy
npm install bootstrap uruchomione poza folderem web instaluje pakiet gdzie indziej - import zgłosi błąd „Failed to resolve import”.
Do utworzenia aplikacji należy wykorzystać zdjęcia oraz plik dane.txt wypakowane z archiwum. Obrazy należy umieścić w folderze assets (egzamin/src/assets lub egzamin/public/assets).
Rozpakowujemy pliki3.zip (hasło K@tegorie)) i kopiujemy dwanaście zdjęć do web/public/assets. Pliki z folderu public Vite podaje pod adresem od korzenia strony, więc zdjęcie jest dostępne jako assets/obraz1.jpg - nazwę pliku z danych wystarczy dokleić do assets/. (Pliki z src/assets trzeba by importować w kodzie jeden po drugim.)
R.3.1 Zapisany w aplikacji jeden komponent, który zawiera nagłówek h1, trzy pola switch lub checkbox zaznaczone, przynajmniej jeden obraz, nagłówek h4 i przycisk "Pobierz" oraz tablicę obiektów z pliku dane.txt
ok. 2 min
Polecenie z arkusza
Aplikacja składa się z jednego komponentu. Komponent składa się z: Nagłówka pierwszego stopnia o treści: „Kategorie zdjęć”.
Zastępujemy całą zawartość App.jsx. Komponent w React to funkcja zwracająca JSX - zapis podobny do HTML wewnątrz JavaScriptu. Zwracamy jeden element div, a w nim <h1>. Plik App.css i przykładowe obrazki z szablonu nie są już potrzebne.
web/src/App.jsxJSXnowy plik
function App() {return ( <div> <h1>Kategorie zdjęć</h1> </div> )}exportdefault App
Podgląd strony po tym kroku
Krok 15: Tablica zdjęć z pliku dane.txt w stanie komponentu
Kryteria: ,
R.3.1 Zapisany w aplikacji jeden komponent, który zawiera nagłówek h1, trzy pola switch lub checkbox zaznaczone, przynajmniej jeden obraz, nagłówek h4 i przycisk "Pobierz" oraz tablicę obiektów z pliku dane.txt
R.1.5 Zastosowane typy pól pasujące do problemu, np. tablica przechowuje liczby całkowite, rozmiar i wartość szukana są liczbami całkowitymi, średnia jest liczbą. W przypadku Python, typ wynika z przypisanych danych
ok. 3 min
Polecenie z arkusza
Dokument dane.txt zawiera listę obiektów zdjęć, którą należy skopiować jako elementy tablicy. Każdy obiekt zdjęcia zawiera pola: id, alt (tekst alternatywny dla zdjęcia), filename (nazwa pliku ze zdjęciem), category (1 dla kategorii kwiaty, 2 zwierzęta, 3 samochody), downloads (liczba pobrań zdjęcia).
Kopiujemy obiekty z dane.txt do tablicy i trzymamy ją w stanie komponentu: useState(...) zwraca bieżącą tablicę photos i funkcję setPhotos, którą podmienimy ją po kliknięciu „Pobierz” - wtedy React narysuje widok od nowa z nową liczbą pobrań. useState importujemy z react na górze pliku.
Dane przepisujemy tak, jak są w pliku (także „Rotwailer” - to dane z arkusza, nie nasz tekst). Na stronie nic jeszcze nie widać.
Krok 16: Pierwszy przełącznik: Kwiaty, domyślnie włączony
Kryteria:
R.3.3 Przycisk ma przypisaną klasę btn btn-success oraz przynajmniej jedno pole switch zdefiniowane za pomocą biblioteki Bootstrap jako blok formatowany stylami form-check form-switch (form-check-inline opcjonalnie ) i złożony z kontrolki input i przypisanej do niej etykiety label, np.: <div class="form-check form-switch"> <input class="form-check-input" type="checkbox" id="chk3" ...> <label class="form-check-label" for="chk3">Samochody</label> </div> W React.js zastosowane className zamiast class oraz domknięte wszystkie znaczniki
ok. 3 min
Polecenie z arkusza
Komponent składa się z trzech pól switch (checkbox) domyślnie włączonych, o etykietach: Kwiaty, Zwierzęta, Samochody.
Stan przełączników to obiekt visible: kluczem jest numer kategorii, a wartością true/false - na start wszystkie true.
Przełącznik przenosimy z tabeli 1: div z klasami form-check form-switch (plus form-check-inline, żeby przełączniki stały w jednym wierszu jak na obrazie 2), w nim input typu checkbox i powiązana etykieta. W JSX class piszemy jako className, for jako htmlFor, a input musi być domknięty /> (R.3.3). checked={visible[1]} pokazuje stan kategorii 1.
React ostrzeże w konsoli przeglądarki, że pole ma checked bez onChange - obsługę dopiszemy za chwilę.
R.3.1 Zapisany w aplikacji jeden komponent, który zawiera nagłówek h1, trzy pola switch lub checkbox zaznaczone, przynajmniej jeden obraz, nagłówek h4 i przycisk "Pobierz" oraz tablicę obiektów z pliku dane.txt
ok. 2 min
Polecenie z arkusza
Trzech pól switch (checkbox) domyślnie włączonych, o etykietach: Kwiaty, Zwierzęta, Samochody.
Dwa kolejne przełączniki według tego samego wzoru, z innymi identyfikatorami (animals, cars) i kategoriami 2 i 3. Identyfikatory muszą być różne - inaczej etykiety wskazywałyby pierwszy przełącznik.
Krok 18: Zmiana stanu przełącznika: toggleCategory
Kryteria:
R.3.7 W kodzie zdefiniowana przynajmniej jedna funkcja (lub operacja), która jest uruchamiana po zmianie stanu switch np. (change)="flowersChanged() lub onChange={this.flowersChanged()}
ok. 3 min
Polecenie z arkusza
Wyświetlane są tylko zdjęcia z kategorii dla której jest włączone pole switch.
Funkcja toggleCategory(category) odwraca stan jednej kategorii. Zapis { ...visible, [category]: !visible[category] } tworzy nowy obiekt: kopię starego z jedną zmienioną wartością (React rysuje widok od nowa tylko wtedy, gdy dostanie nowy obiekt).
Każdy przełącznik wywołuje ją zdarzeniem onChange={() => toggleCategory(1)} - przekazujemy funkcję strzałkową, a nie wynik wywołania (R.3.7). Przełączniki w podglądzie już się przestawiają.
Typowe błędy
onChange={toggleCategory(1)} wywoła funkcję już przy rysowaniu komponentu i zapętli aplikację. Klucz wymienia też onchange="..." w stylu HTML jako powód niezaliczenia CAŁEGO rezultatu 3.
R.3.1 Zapisany w aplikacji jeden komponent, który zawiera nagłówek h1, trzy pola switch lub checkbox zaznaczone, przynajmniej jeden obraz, nagłówek h4 i przycisk "Pobierz" oraz tablicę obiektów z pliku dane.txt
R.3.3 Przycisk ma przypisaną klasę btn btn-success oraz przynajmniej jedno pole switch zdefiniowane za pomocą biblioteki Bootstrap jako blok formatowany stylami form-check form-switch (form-check-inline opcjonalnie ) i złożony z kontrolki input i przypisanej do niej etykiety label, np.: <div class="form-check form-switch"> <input class="form-check-input" type="checkbox" id="chk3" ...> <label class="form-check-label" for="chk3">Samochody</label> </div> W React.js zastosowane className zamiast class oraz domknięte wszystkie znaczniki
R.3.4 Do generowania obrazów zastosowana pętla działająca na elementach galerii np. Angular: *ngFor = "let zdjecie of galeria" React.js: {this.state.galeria.map(zdjecie => ... (lub bez this.state ) oraz pętla generuje obraz i nagłówek h4 z odwołaniem do pól obiektu np. <img src="assets/{{ zdjecie.filename }}" alt="{{ zdjecie.alt }}"> <h4>Pobrań: {{ zdjecie.downloads }}</h4> W React.js pojedyncze nawiasy klamrowe (uwaga! może poprawnie działać również bez nazwy katalogu assets )
ok. 4 min
Polecenie z arkusza
Bloków zdjęć. Bloki są wyświetlone jeden obok drugiego, zawierają zdjęcie, nagłówek 4 stopnia z liczbą pobrań oraz przycisk o treści „Pobierz”. W aplikacji zastosowano pętle do wyświetlenia bloków zdjęć. Aplikacja jest napisana uniwersalnie i działa poprawnie też dla innej liczby zdjęć.
Funkcja renderPhoto(photo) zwraca JSX jednego bloku: zdjęcie z adresem 'assets/' + photo.filename i tekstem alternatywnym z danych, <h4> z liczbą pobrań i zielony przycisk btn btn-success (R.3.3).
Pętlą jest photos.map(...) - dla każdego obiektu tablicy wywołuje renderPhoto i zwraca listę bloków. Działa dla dowolnej liczby zdjęć (R.3.4). Atrybut key to wymóg Reacta przy listach: unikalny identyfikator, po którym React rozpoznaje bloki przy ponownym rysowaniu. Klasa d-inline-block stawia bloki obok siebie - zawijają się do kolejnego wiersza, gdy zabraknie miejsca.
Krok 20: Zdjęcia: margines 5 px i zaokrąglone rogi
Kryteria:
R.3.2 Do projektu dołączona biblioteka Bootstrap oraz sformatowany obraz: margin: 5px; oraz zaokrąglone rogi (border-radius lub klasa rounded )
ok. 2 min
Polecenie z arkusza
Zdjęcia są formatowane stylem: marginesy zewnętrzne 5 px, zaokrąglone rogi.
Zaokrąglone rogi daje klasa Bootstrapa rounded. Margines 5 px piszemy stylem: w JSX atrybut style przyjmuje obiekt JavaScriptu, stąd podwójne klamry - zewnętrzne to wstawienie wyrażenia, wewnętrzne to obiekt { margin: '5px' }. (Klasy Bootstrapa m-1 i m-2 dają 4 i 8 px, nie 5.)
Typowe błędy
style="margin: 5px" jak w HTML to w JSX błąd - React wymaga obiektu.
Krok 21: Warunek: tylko kategorie włączone przełącznikiem
Kryteria: ,
R.3.5 Do sprawdzenia czy switch jest zaznaczony lub czy kategoria obrazu zgadza się z zaznaczeniem zastosowany warunek np.: Angular: *ngIf = "kwiaty == true", *ngIf = "zdjecie.category == 1" React.js: funkcja renderująca, w której znajduje się warunek i i instrukcja return, która odpowiednio zwraca kod wyświetlający obrazek
R.3.9 Zdjęcia wyświetlają się w zależności od stanu pól switch (w uruchomionej aplikacji lub na zrzucie oraz obowiązkowo w kodzie )
ok. 2 min
Polecenie z arkusza
Bloków zdjęć, które są wyświetlane warunkowo, w zależności od ustawień pól switch. W aplikacji zastosowano pętle oraz warunki do wyświetlenia bloków zdjęć.
Funkcja renderująca dostaje warunek: jeśli kategoria zdjęcia jest wyłączona (visible[photo.category] ma wartość false), zwraca null - a null React po prostu pomija. To dokładnie zapis z klucza: funkcja renderująca z warunkiem i return (R.3.5).
Wyłącz w podglądzie „Zwierzęta” - zostają kwiaty i samochody (obraz 3). Włączone same „Samochody” dają dwa zdjęcia.
R.3.6 Zapisane zdarzenie kliknięcia przycisku i obsłużone w kodzie np. Angular: <button ... (click)="onPobierzClick(i)">Pobierz</button> onPobierzClick(i: any):void ... zmienna i z przykładu jest indeksem w pętli (let i=index) React.js: onClick={this.OnPobierzClick(this.state.i)} OnPobierzClick() {... }
R.3.10 Po wciśnięciu przycisku zwiększany jest o jeden licznik pobrań, zmiany są widoczne na stronie, są modyfikowane w tablicy dla odpowiedniego obiektu (w uruchomionej aplikacji lub na zrzucie oraz obowiązkowo w kodzie )
ok. 3 min
Polecenie z arkusza
Gdy przycisk „Pobierz” zostanie kliknięty, wzrasta o jeden liczba pobrań dla danego zdjęcia. Liczba pobrań jest zapisywana w tablicy z obiektami zdjęć, co na bieżąco powoduje wyświetlenie tej modyfikacji na ekranie.
handleDownload(id) buduje nową tablicę: map przepisuje wszystkie zdjęcia, a to o klikniętym id zastępuje kopią z downloads większym o jeden. setPhotos(updated) zapisuje tablicę w stanie i React od razu pokazuje nową liczbę (R.3.10).
Przycisk przekazuje id swojego zdjęcia: onClick={() => handleDownload(photo.id)} (R.3.6). Kliknij kilka razy „Pobierz” przy drugim zdjęciu - liczba rośnie (obraz 4).
Typowe błędy
photo.downloads++ bez setPhotos zmienia liczbę w tablicy, ale React o tym nie wie - ekran się nie odświeży.
Kod aplikacji przygotuj do nagrania na płytę. W folderze webowa powinno znaleźć się archiwum całego folderu projektu o nazwie web.zip oraz pliki z kodem źródłowym, które były modyfikowane przez zdającego.
Pakujemy folder web do web.zip, ale bez node_modules - npm install odtwarza go z package.json, a z nim archiwum miałoby setki megabajtów. Obok kopiujemy zmienione pliki: App.jsx i main.jsx.
Kroki 24-27 · po tym etapie masz 35 z 35 kryteriów (pkt)
Krok 24: Komentarz dokumentujący metodę Find
Kryteria: , , , ,
R.4.1 Dla metody z aplikacji konsolowej zapisany 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 kilka jednoliniowy lub Docstrings (potrójny cudzysłów)
R.4.2 W komentarzu podana nazwa metody oraz opisane jej działanie
R.4.3 W komentarzu podane wszystkie argumenty metody: nazwy i krótki opis lub zapisane "brak"
R.4.4 W komentarzu podana nazwa typu zwracanego i krótki opis co jest zwracane lub zapisane "brak"
R.4.5 W komentarzu podany numer zdającego
ok. 4 min
Polecenie z arkusza
W kodzie źródłowym aplikacji konsolowej za pomocą komentarza utwórz nagłówek dowolnej metody, według wzoru z listingu 1. Komentarz powinien znaleźć się nad lub pod nazwą metody. W sekcji parametry należy umieścić opis wszystkich argumentów metody lub zapisać „brak” w przypadku metody bezparametrowej.
Komentarz wieloliniowy /* ... */ nad metodą Find, z polami z listingu 1: nazwa i opis działania (R.4.2), parametrvalue z opisem - metoda ma jeden argument, więc nie piszemy „brak” (R.4.3), zwracany typ int z opisem (R.4.4) i autor (R.4.5). Na egzaminie w polu autor wpisujemy numer PESEL - tu stoi Bartosz Bryniarski. Liczba gwiazdek jest dowolna.
Typowe błędy
Opis niezgodny z kodem (np. „parametry: brak” przy metodzie z argumentem) nie spełnia R.4.3 - klucz sprawdza zgodność ze stanem faktycznym.
konsola/Program.csC#zmienione: 9 linii
classTable{privateint[] numbers;privateint count;publicTable(int size) { count = size; numbers = newint[size]; Random random = new Random();for (int i = 0; i < count; i++) { numbers[i] = random.Next(1, 1001); } }publicvoidDisplay() {for (int i = 0; i < count; i++) { Console.WriteLine($"{i}: {numbers[i]}"); } }/* ********************************************** nazwa metody: Find opis metody: wyszukuje w tablicy pierwsze wystąpienie podanej wartości parametry: value - liczba całkowita, której szukamy w tablicy zwracany typ i opis: int - indeks pierwszego wystąpienia wartości albo -1, gdy jej nie ma autor: Bartosz Bryniarski ********************************************** */publicintFind(intvalue) {for (int i = 0; i < count; i++) {if (numbers[i] == value) {return i; } }return -1; }publicintDisplayOdd() {int oddCount = 0;for (int i = 0; i < count; i++) {if (numbers[i] % 2 != 0) { Console.WriteLine(numbers[i]); oddCount++; } }return oddCount; }publicdoubleAverage() {int sum = 0;for (int i = 0; i < count; i++) { sum += numbers[i]; }return (double)sum / count; }}classProgram{staticvoidMain() { Table table = new Table(50); Console.WriteLine("Elementy tablicy:"); table.Display(); Console.Write("Podaj liczbę do wyszukania: ");int searched = int.Parse(Console.ReadLine());int index = table.Find(searched);if (index != -1) { Console.WriteLine($"Liczba {searched} jest pod indeksem {index}"); } Console.WriteLine("Liczby nieparzyste:");int oddCount = table.DisplayOdd(); Console.WriteLine($"Razem nieparzystych: {oddCount}");double average = table.Average(); Console.WriteLine($"Średnia wszystkich elementów: {average:F2}"); }}
R.1.6 Podjęta próba skompilowania kodu, udokumentowana zrzutem przedstawiającym uruchomiony program lub jego kompilację
R.2.6 Program uruchamia się w konsoli, co jest udokumentowane zrzutem ekranu
R.4.6 Zapisany 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ń. Na zrzucie należy umieścić okno z wynikiem działania programu oraz otwarte środowisko programistyczne z projektem. Aplikacja konsolowa – dowolna liczba zrzutów nazwanych konsola1, konsola2, ...
Program pyta o liczbę, więc robimy dwa zrzuty całego ekranu z Visual Studio i oknem konsoli: konsola1.png - wyszukana liczba, która jest w tablicy (z komunikatem o indeksie), konsola2.png - liczba spoza tablicy (bez komunikatu, jak na obrazie 1). Zrzuty zapisujemy w folderze dokumentacja.
R.3.8 Aplikacja jest interpretowana bez błędów. W stanie początkowym są zaznaczone wszystkie pola switch i wyświetlone wszystkie obrazy co jest udokumentowane zrzutem ekranu
R.4.7 Zapisany przynajmniej jeden zrzut ekranu z uruchomienia aplikacji webowej lub z błędami interpretacji, na zrzucie widoczne jest środowisko, w którym powstała aplikacja
ok. 4 min
Polecenie z arkusza
Aplikacja webowa – dowolna liczba zrzutów nazwanych web1, web2, ... (stan początkowy, różne kombinacje dla pól switch, działanie przycisku „Pobierz”).
npm run dev i http://localhost:5173. Zrzuty całego ekranu z przeglądarką i środowiskiem albo terminalem:
web1.png - stan początkowy, wszystkie zdjęcia (obraz 2, R.3.8),
web2.png - wyłączone „Zwierzęta” (obraz 3),
web3.png - włączone same „Samochody”,
web4.png - po kilku kliknięciach „Pobierz” (obraz 4).
Podgląd obok to aplikacja z poprzedniego kroku - możesz w niej przejść te stany.
Typowe błędy
Zrzut samego okna przeglądarki, bez środowiska albo terminala, nie spełnia R.4.7.
Podgląd strony po tym kroku
Krok 27: Dokument egzamin
Kryteria:
R.4.8 Dokumentacja zawiera wykorzystane oprogramowanie: nazwa systemu operacyjnego, nazwy środowisk programistycznych, nazwy języków programowania
ok. 2 min
Polecenie z arkusza
W edytorze tekstu pakietu biurowego utwórz plik z dokumentacją i nazwij go egzamin. Dokument powinien zawierać zapisane informacje o wykorzystanych w czasie egzaminu narzędziach: Nazwę systemu operacyjnego, Nazwy środowisk programistycznych, Nazwy języków programowania. Zrzuty ekranu i dokument umieść w podfolderze dokumentacja.
W tym arkuszu dokument piszemy w edytorze tekstu pakietu biurowego (np. Word albo Writer) i zapisujemy jako egzamin w folderze dokumentacja. Trzy informacje: system, środowiska i języki - te, których naprawdę używaliśmy. Obok jest ta sama treść jako plik tekstowy.
egzamin.txtTekstnowy plik
System operacyjny: Windows 11Środowiska programistyczne: Visual Studio 2022 (aplikacja konsolowa), Visual Studio Code z Node.js i Vite (aplikacja webowa)Języki programowania: C# (aplikacja konsolowa), JavaScript z JSX - biblioteka React (aplikacja webowa)
Kroki 28-29 · po tym etapie masz 35 z 35 kryteriów (pkt)
Krok 28: Konsola: bezpieczne wczytanie liczby
ok. 3 min
Polecenie z arkusza
Komunikacja z użytkownikiem musi być zrozumiała.
int.Parse przy wpisaniu słowa, pustego wiersza albo liczby z przecinkiem przerywa program wyjątkiem - użytkownik widzi czerwony komunikat po angielsku. int.TryParse nie rzuca wyjątku: zwraca true, gdy udało się odczytać liczbę (i zapisuje ją w searched), a false w przeciwnym razie. Pętla while pyta więc do skutku. Stara linia zostaje w komentarzu.
Na egzaminie wystarczy wersja wcześniejsza - klucz tego nie sprawdza.
konsola/Program.csC#zmienione: 6 linii
classTable{privateint[] numbers;privateint count;publicTable(int size) { count = size; numbers = newint[size]; Random random = new Random();for (int i = 0; i < count; i++) { numbers[i] = random.Next(1, 1001); } }publicvoidDisplay() {for (int i = 0; i < count; i++) { Console.WriteLine($"{i}: {numbers[i]}"); } }/* ********************************************** nazwa metody: Find opis metody: wyszukuje w tablicy pierwsze wystąpienie podanej wartości parametry: value - liczba całkowita, której szukamy w tablicy zwracany typ i opis: int - indeks pierwszego wystąpienia wartości albo -1, gdy jej nie ma autor: Bartosz Bryniarski ********************************************** */publicintFind(intvalue) {for (int i = 0; i < count; i++) {if (numbers[i] == value) {return i; } }return -1; }publicintDisplayOdd() {int oddCount = 0;for (int i = 0; i < count; i++) {if (numbers[i] % 2 != 0) { Console.WriteLine(numbers[i]); oddCount++; } }return oddCount; }publicdoubleAverage() {int sum = 0;for (int i = 0; i < count; i++) { sum += numbers[i]; }return (double)sum / count; }}classProgram{staticvoidMain() { Table table = new Table(50); Console.WriteLine("Elementy tablicy:"); table.Display(); Console.Write("Podaj liczbę do wyszukania: ");int searched;while (!int.TryParse(Console.ReadLine(), out searched)) { Console.Write("To nie jest liczba całkowita. Podaj liczbę do wyszukania: "); }// int searched = int.Parse(Console.ReadLine());int index = table.Find(searched);if (index != -1) { Console.WriteLine($"Liczba {searched} jest pod indeksem {index}"); } Console.WriteLine("Liczby nieparzyste:");int oddCount = table.DisplayOdd(); Console.WriteLine($"Razem nieparzystych: {oddCount}");double average = table.Average(); Console.WriteLine($"Średnia wszystkich elementów: {average:F2}"); }}
Krok 29: React: filter zamiast null w funkcji renderującej
ok. 3 min
Polecenie z arkusza
Wyświetlane są tylko zdjęcia z kategorii dla której jest włączone pole switch.
Wersja z kluczem wybiera zdjęcia w trakcie rysowania: funkcja renderująca zwraca null dla ukrytych. Czytelniej jest oddzielić wybór danych od rysowania: photos.filter(...) tworzy listę samych widocznych zdjęć, a renderPhoto tylko rysuje blok. Taką listę łatwo też policzyć albo posortować (np. „widocznych zdjęć: 5”). Działanie aplikacji jest takie samo.
Na egzaminie zostań przy wersji wcześniejszej - klucz (R.3.5) opisuje właśnie funkcję renderującą z warunkiem.
Krok 30 · po tym etapie masz 35 z 35 kryteriów (pkt)
Krok 30: 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 dokumentacja, konsolowa, webowa. W folderze dokumentacja: pliki ze zrzutami oraz plik egzamin. W folderze konsolowa: spakowany cały projekt aplikacji konsolowej, źródło, opcjonalnie plik wykonywalny. W folderze webowa: spakowany cały projekt aplikacji web, pliki ze źródłami komponentu, html, css i inne modyfikowane pliki.
Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.
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: Bartosz Bryniarski. 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). Treść arkusza i klucza oceniania pochodzi z CKE.