commit 2017e7a4c5a27c3b9eb3f59b60413d0c69e62438 Author: baiobelfer Date: Sat Feb 1 18:16:23 2025 +0100 init diff --git a/__ b/__ new file mode 100644 index 0000000..c6403a6 --- /dev/null +++ b/__ @@ -0,0 +1 @@ +QT_QPA_PLATFORM=xcb python main.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/commands.py b/commands.py new file mode 100644 index 0000000..e269f5a --- /dev/null +++ b/commands.py @@ -0,0 +1,116 @@ +# commands.py + +from PySide6.QtGui import QUndoCommand +from PySide6.QtWidgets import QTreeWidgetItem +from PySide6.QtCore import Qt + +def clone_item_recursive(source_item: QTreeWidgetItem) -> QTreeWidgetItem: + """ + Rekurencyjnie klonuje QTreeWidgetItem wraz z jego dziećmi, + zachowując tekst i ewentualne dane w kolumnach. + """ + cloned = QTreeWidgetItem([ + source_item.text(0), + source_item.text(1), + source_item.text(2), + source_item.text(3), + ]) + # Ewentualnie można kopiować Qt.UserRole itp.: + # for col in range(source_item.columnCount()): + # data_val = source_item.data(col, Qt.UserRole) + # cloned.setData(col, Qt.UserRole, data_val) + + for i in range(source_item.childCount()): + child = source_item.child(i) + cloned_child = clone_item_recursive(child) + cloned.addChild(cloned_child) + return cloned + + +class AddItemCommand(QUndoCommand): + """ + Dodaje nowy (już utworzony) węzeł (cloned_item) do tree: + - jeśli parent_item is None -> top-level + - w przeciwnym razie child w parent_item + """ + def __init__(self, tree, parent_item, index: int, cloned_item, description="Add Item"): + super().__init__(description) + self.tree = tree + self.parent_item = parent_item + self.index = index + self.cloned_item = cloned_item + + def redo(self): + if self.parent_item is None: + self.tree.insertTopLevelItem(self.index, self.cloned_item) + else: + self.parent_item.insertChild(self.index, self.cloned_item) + self.tree.expandItem(self.cloned_item) + + def undo(self): + if self.parent_item: + self.parent_item.removeChild(self.cloned_item) + else: + idx = self.tree.indexOfTopLevelItem(self.cloned_item) + if idx >= 0: + self.tree.takeTopLevelItem(idx) + + +class RemoveItemCommand(QUndoCommand): + """ + Usuwa wybrany węzeł z drzewa i zapamiętuje jego kopię, + by móc cofnąć (undo) i przywrócić go w to samo miejsce. + """ + def __init__(self, tree, item, description="Remove Item"): + super().__init__(description) + self.tree = tree + self.item_original = item + self.parent_item = item.parent() + + if self.parent_item: + self.index = self.parent_item.indexOfChild(item) + else: + self.index = self.tree.indexOfTopLevelItem(item) + + # Tworzymy kopię rekurencyjną (z dziećmi) + self.item_clone = clone_item_recursive(item) + + def redo(self): + # usuwamy oryginalny węzeł z widoku + if self.parent_item: + self.parent_item.removeChild(self.item_original) + else: + idx = self.tree.indexOfTopLevelItem(self.item_original) + if idx >= 0: + self.tree.takeTopLevelItem(idx) + + def undo(self): + # przywracamy klon w to samo miejsce + if self.parent_item: + self.parent_item.insertChild(self.index, self.item_clone) + else: + self.tree.insertTopLevelItem(self.index, self.item_clone) + self.tree.expandItem(self.item_clone) + + +class EditPunktyCommand(QUndoCommand): + """ + Cofalne/ponawialne ustawienie nowej wartości punktów w kolumnie 0. + W 'redo()' dajemy new_val, w 'undo()' old_val. + """ + def __init__(self, item: QTreeWidgetItem, old_val: str, new_val: str, description="Edit punkty"): + super().__init__(description) + self.item = item + self.old_val = old_val + self.new_val = new_val + + def redo(self): + self.item.setText(0, self.new_val) + # Ewentualnie odśwież kolor: + # from utils.colors import color_item_by_title + # color_item_by_title(self.item) + + def undo(self): + self.item.setText(0, self.old_val) + # from utils.colors import color_item_by_title + # color_item_by_title(self.item) diff --git a/data/pp-math-latex.json b/data/pp-math-latex.json new file mode 100644 index 0000000..68b1fa6 --- /dev/null +++ b/data/pp-math-latex.json @@ -0,0 +1,515 @@ +{ + "podstawa_programowa": { + "tytul": "ROZPORZĄDZENIE MINISTRA EDUKACJI", + "data": "z dnia 28 czerwca 2024 r.", + "poz": "1019", + "opis": "Zmieniające rozporządzenie w sprawie podstawy programowej kształcenia ogólnego dla liceum ogólnokształcącego, technikum oraz branżowej szkoły II stopnia.", + "miejsce": "Warszawa, dnia 10 lipca 2024 r.", + "przedmiot": "Matematyka" + }, + + "autor": "Mateusz Pabiszczak", + "data": "2025-01-27", + + "wymagania_ogolne": [ + { + "nr": 1, + "opis": "Sprawność rachunkowa.", + "atrybuty": { + "punkty": -1, + "obowiązkowe": 1 + }, + "wymagania": [ + { + "nr": 1, + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + { + "nr": 2, + "opis": "Wykorzystanie i tworzenie informacji.", + "atrybuty": { + "punkty": -1, + "obowiązkowe": 1 + }, + "wymagania": [ + { + "nr": 1, + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + { + "nr": 3, + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "atrybuty": { + "punkty": -1, + "obowiązkowe": 1 + }, + "wymagania": [ + { + "nr": 1, + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + { + "nr": 4, + "opis": "Rozumowanie i argumentacja.", + "atrybuty": { + "punkty": -1, + "obowiązkowe": 1 + }, + "wymagania": [ + { + "nr": 1, + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + } + ], + + "wymagania_szczegolowe": [ + + { + "nr": 1, + "opis": "Liczby rzeczywiste", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "zakres_podstawowy": { + "nr": -1, + "opis": "Zakres podstawowy", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Wykonuje działania (dodawanie, odejmowanie, mnożenie, dzielenie, potęgowanie, pierwiastkowanie, logarytmowanie) w zbiorze liczb rzeczywistych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Przeprowadza proste dowody dotyczące podzielności liczb całkowitych i reszt z dzielenia.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + }, + "podpunkty": [ + { + "nr": "a", + "opis": "Dowód podzielności przez 24 iloczynu czterech kolejnych liczb naturalnych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": "b", + "opis": "Dowód własności: jeśli liczba przy dzieleniu przez 4 daje resztę 3, to nie jest kwadratem liczby całkowitej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + { + "nr": 3, + "opis": "Stosuje własności pierwiastków dowolnego stopnia, w tym pierwiastków stopnia nieparzystego z liczb ujemnych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Stosuje związek pierwiastkowania z potęgowaniem oraz prawa działań na potęgach i pierwiastkach.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 5, + "opis": "Stosuje monotoniczność potęgowania, w szczególności własności: jeśli $x < y$ oraz $a > 1$, to $a^x < a^y$, zaś gdy $x < y$ i $0 < a < 1$, to $a^x > a^y$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 6, + "opis": "Posługuje się pojęciem przedziału liczbowego, zaznacza przedziały na osi liczbowej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 7, + "opis": "Stosuje interpretację geometryczną i algebraiczną wartości bezwzględnej, rozwiązuje równania typu: $|x + 4| = 5$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 8, + "opis": "Wykorzystuje własności potęgowania i pierwiastkowania w sytuacjach praktycznych, w tym do obliczania procentów składanych, zysków z lokat i kosztów kredytów.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 9, + "opis": "Stosuje związek logarytmowania z potęgowaniem, posługuje się wzorami na logarytm iloczynu, logarytm ilorazu i logarytm potęgi.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + "zakres_rozszerzony": { + "nr": -1, + "opis": "Zakres rozszerzony", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Spełnia wymagania określone dla zakresu podstawowego.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Stosuje wzór na zamianę podstawy logarytmu.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + } + }, + + { + "nr": 2, + "opis": "Wyrażenia algebraiczne", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "zakres_podstawowy": { + "nr": -1, + "opis": "Zakres podstawowy", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Stosuje wzory skróconego mnożenia na: $(a + b)^2$, $(a - b)^2$, $a^2 - b^2$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Dodaje, odejmuje i mnoży wielomiany jednej i wielu zmiennych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Wyłącza poza nawias jednomian z sumy algebraicznej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Mnoży i dzieli wyrażenia wymierne.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + "zakres_rozszerzony": { + "nr": -1, + "opis": "Zakres rozszerzony", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Dzieli wielomian jednej zmiennej $W(x)$ przez dwumian postaci $x - a$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Rozkłada wielomiany na czynniki metodą wyłączania wspólnego czynnika przed nawias oraz metodą grupowania wyrazów.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Znajduje pierwiastki całkowite wielomianu o współczynnikach całkowitych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Stosuje podstawowe własności trójkąta Pascala oraz własności symbolu Newtona, w tym: $\\binom{n}{0} = 1$, $\\binom{n}{1} = n$, $\\binom{n}{n-1} = n$, $\\binom{n}{k} = \\binom{n}{n-k}$, $\\binom{n}{k} + \\binom{n}{k+1} = \\binom{n+1}{k+1}$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 5, + "opis": "Korzysta ze wzorów na: $a^3 + b^3$, $a^3 - b^3$, $a^n - b^n$, $(a + b)^n$ i $(a - b)^n$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 6, + "opis": "Dodaje i odejmuje wyrażenia wymierne, np.: $\\frac{1}{x + 1} - \\frac{1}{x}$, $\\frac{1}{x} + \\frac{1}{x^2} + \\frac{1}{x^3}$, $\\frac{x + 1}{x + 2} + \\frac{x - 1}{x + 1}$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + } + }, + { + "nr": 3, + "opis": "Równania i nierówności", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "zakres_podstawowy": { + "nr": -1, + "opis": "Zakres podstawowy", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Przekształca równania i nierówności w sposób równoważny, w tym np. przekształca równoważnie równanie $5x + 1 = \\frac{x + 3}{2x - 1}$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Interpretuje równania i nierówności liniowe sprzeczne oraz tożsamościowe.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Rozwiązuje nierówności liniowe z jedną niewiadomą.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Rozwiązuje równania i nierówności kwadratowe.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 5, + "opis": "Rozwiązuje równania wielomianowe postaci $W(x) = 0$ dla wielomianów doprowadzonych do postaci iloczynowej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + "zakres_rozszerzony": { + "nr": -1, + "opis": "Zakres rozszerzony", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Rozwiązuje równania wielomianowe postaci $W(x) = 0$ oraz nierówności wielomianowe typu: $W(x) > 0$, $W(x) \\geq 0$, $W(x) < 0$, $W(x) \\leq 0$ dla wielomianów doprowadzonych do postaci iloczynowej lub takich, które dają się doprowadzić do tej postaci metodą wyłączania wspólnego czynnika przed nawias lub metodą grupowania.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Rozwiązuje równania i nierówności wymierne, które dają się sprowadzić do równania lub nierówności liniowej lub kwadratowej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Stosuje wzory Viète’a dla równań kwadratowych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Rozwiązuje równania i nierówności z wartością bezwzględną.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 5, + "opis": "Analizuje równania i nierówności liniowe z parametrami oraz równania i nierówności kwadratowe z parametrami, w szczególności: wyznacza liczbę rozwiązań w zależności od parametrów, podaje warunki, przy których rozwiązania mają określone znaki bądź należą do określonego przedziału, wyznacza rozwiązania w zależności od parametrów.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 6, + "opis": "Rozwiązuje równania wielomianowe, które dają się doprowadzić do równania kwadratowego, w szczególności równania dwukwadratowe.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 7, + "opis": "Rozwiązuje równania wymierne postaci $\\frac{V(x)}{W(x)} = 0$, gdzie wielomiany $V(x)$ i $W(x)$ są zapisane w postaci iloczynowej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + } + } + ] +} diff --git a/data/pp-math-schema.json b/data/pp-math-schema.json new file mode 100644 index 0000000..9ff7801 --- /dev/null +++ b/data/pp-math-schema.json @@ -0,0 +1,168 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "podstawa_programowa": { + "type": "object", + "properties": { + "tytul": { "type": "string" }, + "data": { "type": "string", "format": "date" }, + "poz": { "type": "string" }, + "opis": { "type": "string" }, + "miejsce": { "type": "string" }, + "przedmiot": { "type": "string" } + }, + "required": ["tytul", "data", "poz", "opis", "miejsce", "przedmiot"] + }, + "autor": { "type": "string" }, + "data": { "type": "string", "format": "date" }, + "wymagania_ogolne": { + "type": "array", + "items": { + "type": "object", + "properties": { + "nr": { "type": "integer" }, + "opis": { "type": "string" }, + "atrybuty": { + "type": "object", + "properties": { + "punkty": { "type": "integer" }, + "obowiązkowe": { "type": "integer" } + }, + "required": ["punkty", "obowiązkowe"] + }, + "wymagania": { + "type": "array", + "items": { + "type": "object", + "properties": { + "nr": { "type": "integer" }, + "opis": { "type": "string" }, + "atrybuty": { + "type": "object", + "properties": { + "punkty": { "type": "integer" }, + "obowiązkowe": { "type": "integer" } + }, + "required": ["punkty", "obowiązkowe"] + } + }, + "required": ["nr", "opis", "atrybuty"] + } + } + }, + "required": ["nr", "opis", "atrybuty", "wymagania"] + } + }, + "wymagania_szczegolowe": { + "type": "array", + "items": { + "type": "object", + "properties": { + "nr": { "type": "integer" }, + "opis": { "type": "string" }, + "atrybuty": { + "type": "object", + "properties": { + "punkty": { "type": "integer" }, + "obowiązkowe": { "type": "integer" } + }, + "required": ["punkty", "obowiązkowe"] + }, + "zakres_podstawowy": { + "type": "object", + "properties": { + "nr": { "type": "integer" }, + "opis": { "type": "string" }, + "atrybuty": { + "type": "object", + "properties": { + "punkty": { "type": "integer" }, + "obowiązkowe": { "type": "integer" } + }, + "required": ["punkty", "obowiązkowe"] + }, + "uczen": { + "type": "array", + "items": { + "type": "object", + "properties": { + "nr": { "type": "integer" }, + "opis": { "type": "string" }, + "atrybuty": { + "type": "object", + "properties": { + "punkty": { "type": "integer" }, + "obowiązkowe": { "type": "integer" } + }, + "required": ["punkty", "obowiązkowe"] + }, + "podpunkty": { + "type": "array", + "items": { + "type": "object", + "properties": { + "nr": { "type": "string" }, + "opis": { "type": "string" }, + "atrybuty": { + "type": "object", + "properties": { + "punkty": { "type": "integer" }, + "obowiązkowe": { "type": "integer" } + }, + "required": ["punkty", "obowiązkowe"] + } + }, + "required": ["nr", "opis", "atrybuty"] + } + } + }, + "required": ["nr", "opis", "atrybuty"] + } + } + }, + "required": ["nr", "opis", "atrybuty", "uczen"] + }, + "zakres_rozszerzony": { + "type": "object", + "properties": { + "nr": { "type": "integer" }, + "opis": { "type": "string" }, + "atrybuty": { + "type": "object", + "properties": { + "punkty": { "type": "integer" }, + "obowiązkowe": { "type": "integer" } + }, + "required": ["punkty", "obowiązkowe"] + }, + "uczen": { + "type": "array", + "items": { + "type": "object", + "properties": { + "nr": { "type": "integer" }, + "opis": { "type": "string" }, + "atrybuty": { + "type": "object", + "properties": { + "punkty": { "type": "integer" }, + "obowiązkowe": { "type": "integer" } + }, + "required": ["punkty", "obowiązkowe"] + } + }, + "required": ["nr", "opis", "atrybuty"] + } + } + }, + "required": ["nr", "opis", "atrybuty", "uczen"] + } + }, + "required": ["nr", "opis", "atrybuty", "zakres_podstawowy", "zakres_rozszerzony"] + } + } + }, + "required": ["podstawa_programowa", "autor", "data", "wymagania_ogolne", "wymagania_szczegolowe"] +} + diff --git a/data/pp-math.json b/data/pp-math.json new file mode 100644 index 0000000..365f877 --- /dev/null +++ b/data/pp-math.json @@ -0,0 +1,519 @@ +{ + "podstawa_programowa": { + "tytul": "ROZPORZĄDZENIE MINISTRA EDUKACJI", + "data": "z dnia 28 czerwca 2024 r.", + "poz": "1019", + "opis": "Zmieniające rozporządzenie w sprawie podstawy programowej kształcenia ogólnego dla liceum ogólnokształcącego, technikum oraz branżowej szkoły II stopnia.", + "miejsce": "Warszawa, dnia 10 lipca 2024 r.", + "przedmiot": "Matematyka" + }, + + "autor": "Mateusz Pabiszczak", + "data": "2025-01-27", + + "wymagania_ogolne": [ + { + "nr": 1, + "opis": "Sprawność rachunkowa.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + }, + "wymagania": [ + { + "nr": 1, + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + { + "nr": 2, + "opis": "Wykorzystanie i tworzenie informacji.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + }, + "wymagania": [ + { + "nr": 1, + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + { + "nr": 3, + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + }, + "wymagania": [ + { + "nr": 1, + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + { + "nr": 4, + "opis": "Rozumowanie i argumentacja.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + }, + "wymagania": [ + { + "nr": 1, + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + } + ], + + "wymagania_szczegolowe": [ + + { + "nr": 1, + "opis": "Liczby rzeczywiste", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "zakres_podstawowy": { + "nr": -1, + "opis": "Zakres podstawowy", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Wykonuje działania (dodawanie, odejmowanie, mnożenie, dzielenie, potęgowanie, pierwiastkowanie, logarytmowanie) w zbiorze liczb rzeczywistych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Przeprowadza proste dowody dotyczące podzielności liczb całkowitych i reszt z dzielenia.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + }, + "podpunkty": [ + { + "nr": "a", + "opis": "Dowód podzielności przez 24 iloczynu czterech kolejnych liczb naturalnych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": "b", + "opis": "Dowód własności: jeśli liczba przy dzieleniu przez 4 daje resztę 3, to nie jest kwadratem liczby całkowitej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + { + "nr": 3, + "opis": "Stosuje własności pierwiastków dowolnego stopnia, w tym pierwiastków stopnia nieparzystego z liczb ujemnych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Stosuje związek pierwiastkowania z potęgowaniem oraz prawa działań na potęgach i pierwiastkach.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 5, + "opis": "Stosuje monotoniczność potęgowania, w szczególności własności: jeśli x < y oraz a > 1, to a^x < a^y, zaś gdy x < y i 0 < a < 1, to a^x > a^y.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 6, + "opis": "Posługuje się pojęciem przedziału liczbowego, zaznacza przedziały na osi liczbowej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 7, + "opis": "Stosuje interpretację geometryczną i algebraiczną wartości bezwzględnej, rozwiązuje równania typu: |x + 4| = 5.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 8, + "opis": "Wykorzystuje własności potęgowania i pierwiastkowania w sytuacjach praktycznych, w tym do obliczania procentów składanych, zysków z lokat i kosztów kredytów.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 9, + "opis": "Stosuje związek logarytmowania z potęgowaniem, posługuje się wzorami na logarytm iloczynu, logarytm ilorazu i logarytm potęgi.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + "zakres_rozszerzony": { + "nr": -1, + "opis": "Zakres rozszerzony", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Spełnia wymagania określone dla zakresu podstawowego.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Stosuje wzór na zamianę podstawy logarytmu.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + } + }, + + { + "nr": 2, + "opis": "Wyrażenia algebraiczne", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "zakres_podstawowy": { + "nr": -1, + "opis": "Zakres podstawowy", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Stosuje wzory skróconego mnożenia na: (a + b)^2, (a − b)^2, a^2 − b^2.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Dodaje, odejmuje i mnoży wielomiany jednej i wielu zmiennych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Wyłącza poza nawias jednomian z sumy algebraicznej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Mnoży i dzieli wyrażenia wymierne.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + "zakres_rozszerzony": { + "nr": -1, + "opis": "Zakres rozszerzony", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Dzieli wielomian jednej zmiennej W(x) przez dwumian postaci x − a.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Rozkłada wielomiany na czynniki metodą wyłączania wspólnego czynnika przed nawias oraz metodą grupowania wyrazów.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Znajduje pierwiastki całkowite wielomianu o współczynnikach całkowitych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Stosuje podstawowe własności trójkąta Pascala oraz własności symbolu Newtona, w tym: (n 0) = 1, (n 1) = n, (n n−1) = n, (n k) = (n n−k), (n k) + (n k+1) = (n+1 k+1).", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 5, + "opis": "Korzysta ze wzorów na: a^3 + b^3, a^3 − b^3, a^n − b^n, (a + b)^n i (a − b)^n.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 6, + "opis": "Dodaje i odejmuje wyrażenia wymierne, np.: 1/(x + 1) − 1/x, 1/x + 1/x^2 + 1/x^3, (x + 1)/(x + 2) + (x − 1)/(x + 1).", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + } + }, + + { + "nr": 3, + "opis": "Równania i nierówności", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "zakres_podstawowy": { + "nr": -1, + "opis": "Zakres podstawowy", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Przekształca równania i nierówności w sposób równoważny, w tym np. przekształca równoważnie równanie 5x + 1 = (x + 3) / (2x − 1).", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Interpretuje równania i nierówności liniowe sprzeczne oraz tożsamościowe.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Rozwiązuje nierówności liniowe z jedną niewiadomą.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Rozwiązuje równania i nierówności kwadratowe.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 5, + "opis": "Rozwiązuje równania wielomianowe postaci W(x) = 0 dla wielomianów doprowadzonych do postaci iloczynowej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + "zakres_rozszerzony": { + "nr": -1, + "opis": "Zakres rozszerzony", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Rozwiązuje równania wielomianowe postaci W(x) = 0 oraz nierówności wielomianowe typu: W(x) > 0, W(x) ≥ 0, W(x) < 0, W(x) ≤ 0 dla wielomianów doprowadzonych do postaci iloczynowej lub takich, które dają się doprowadzić do tej postaci metodą wyłączania wspólnego czynnika przed nawias lub metodą grupowania.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Rozwiązuje równania i nierówności wymierne, które dają się sprowadzić do równania lub nierówności liniowej lub kwadratowej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Stosuje wzory Viète’a dla równań kwadratowych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Rozwiązuje równania i nierówności z wartością bezwzględną.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 5, + "opis": "Analizuje równania i nierówności liniowe z parametrami oraz równania i nierówności kwadratowe z parametrami, w szczególności: wyznacza liczbę rozwiązań w zależności od parametrów, podaje warunki, przy których rozwiązania mają określone znaki bądź należą do określonego przedziału, wyznacza rozwiązania w zależności od parametrów.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 6, + "opis": "Rozwiązuje równania wielomianowe, które dają się doprowadzić do równania kwadratowego, w szczególności równania dwukwadratowe.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 7, + "opis": "Rozwiązuje równania wymierne postaci V(x)/W(x) = 0, gdzie wielomiany V(x) i W(x) są zapisane w postaci iloczynowej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + } + } + + ] + +} + diff --git a/data/pp.json b/data/pp.json new file mode 100644 index 0000000..68b1fa6 --- /dev/null +++ b/data/pp.json @@ -0,0 +1,515 @@ +{ + "podstawa_programowa": { + "tytul": "ROZPORZĄDZENIE MINISTRA EDUKACJI", + "data": "z dnia 28 czerwca 2024 r.", + "poz": "1019", + "opis": "Zmieniające rozporządzenie w sprawie podstawy programowej kształcenia ogólnego dla liceum ogólnokształcącego, technikum oraz branżowej szkoły II stopnia.", + "miejsce": "Warszawa, dnia 10 lipca 2024 r.", + "przedmiot": "Matematyka" + }, + + "autor": "Mateusz Pabiszczak", + "data": "2025-01-27", + + "wymagania_ogolne": [ + { + "nr": 1, + "opis": "Sprawność rachunkowa.", + "atrybuty": { + "punkty": -1, + "obowiązkowe": 1 + }, + "wymagania": [ + { + "nr": 1, + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + { + "nr": 2, + "opis": "Wykorzystanie i tworzenie informacji.", + "atrybuty": { + "punkty": -1, + "obowiązkowe": 1 + }, + "wymagania": [ + { + "nr": 1, + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + { + "nr": 3, + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "atrybuty": { + "punkty": -1, + "obowiązkowe": 1 + }, + "wymagania": [ + { + "nr": 1, + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + { + "nr": 4, + "opis": "Rozumowanie i argumentacja.", + "atrybuty": { + "punkty": -1, + "obowiązkowe": 1 + }, + "wymagania": [ + { + "nr": 1, + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + } + ], + + "wymagania_szczegolowe": [ + + { + "nr": 1, + "opis": "Liczby rzeczywiste", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "zakres_podstawowy": { + "nr": -1, + "opis": "Zakres podstawowy", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Wykonuje działania (dodawanie, odejmowanie, mnożenie, dzielenie, potęgowanie, pierwiastkowanie, logarytmowanie) w zbiorze liczb rzeczywistych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Przeprowadza proste dowody dotyczące podzielności liczb całkowitych i reszt z dzielenia.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + }, + "podpunkty": [ + { + "nr": "a", + "opis": "Dowód podzielności przez 24 iloczynu czterech kolejnych liczb naturalnych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": "b", + "opis": "Dowód własności: jeśli liczba przy dzieleniu przez 4 daje resztę 3, to nie jest kwadratem liczby całkowitej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + { + "nr": 3, + "opis": "Stosuje własności pierwiastków dowolnego stopnia, w tym pierwiastków stopnia nieparzystego z liczb ujemnych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Stosuje związek pierwiastkowania z potęgowaniem oraz prawa działań na potęgach i pierwiastkach.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 5, + "opis": "Stosuje monotoniczność potęgowania, w szczególności własności: jeśli $x < y$ oraz $a > 1$, to $a^x < a^y$, zaś gdy $x < y$ i $0 < a < 1$, to $a^x > a^y$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 6, + "opis": "Posługuje się pojęciem przedziału liczbowego, zaznacza przedziały na osi liczbowej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 7, + "opis": "Stosuje interpretację geometryczną i algebraiczną wartości bezwzględnej, rozwiązuje równania typu: $|x + 4| = 5$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 8, + "opis": "Wykorzystuje własności potęgowania i pierwiastkowania w sytuacjach praktycznych, w tym do obliczania procentów składanych, zysków z lokat i kosztów kredytów.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 9, + "opis": "Stosuje związek logarytmowania z potęgowaniem, posługuje się wzorami na logarytm iloczynu, logarytm ilorazu i logarytm potęgi.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + "zakres_rozszerzony": { + "nr": -1, + "opis": "Zakres rozszerzony", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Spełnia wymagania określone dla zakresu podstawowego.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Stosuje wzór na zamianę podstawy logarytmu.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + } + }, + + { + "nr": 2, + "opis": "Wyrażenia algebraiczne", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "zakres_podstawowy": { + "nr": -1, + "opis": "Zakres podstawowy", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Stosuje wzory skróconego mnożenia na: $(a + b)^2$, $(a - b)^2$, $a^2 - b^2$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Dodaje, odejmuje i mnoży wielomiany jednej i wielu zmiennych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Wyłącza poza nawias jednomian z sumy algebraicznej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Mnoży i dzieli wyrażenia wymierne.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + "zakres_rozszerzony": { + "nr": -1, + "opis": "Zakres rozszerzony", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Dzieli wielomian jednej zmiennej $W(x)$ przez dwumian postaci $x - a$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Rozkłada wielomiany na czynniki metodą wyłączania wspólnego czynnika przed nawias oraz metodą grupowania wyrazów.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Znajduje pierwiastki całkowite wielomianu o współczynnikach całkowitych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Stosuje podstawowe własności trójkąta Pascala oraz własności symbolu Newtona, w tym: $\\binom{n}{0} = 1$, $\\binom{n}{1} = n$, $\\binom{n}{n-1} = n$, $\\binom{n}{k} = \\binom{n}{n-k}$, $\\binom{n}{k} + \\binom{n}{k+1} = \\binom{n+1}{k+1}$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 5, + "opis": "Korzysta ze wzorów na: $a^3 + b^3$, $a^3 - b^3$, $a^n - b^n$, $(a + b)^n$ i $(a - b)^n$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 6, + "opis": "Dodaje i odejmuje wyrażenia wymierne, np.: $\\frac{1}{x + 1} - \\frac{1}{x}$, $\\frac{1}{x} + \\frac{1}{x^2} + \\frac{1}{x^3}$, $\\frac{x + 1}{x + 2} + \\frac{x - 1}{x + 1}$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + } + }, + { + "nr": 3, + "opis": "Równania i nierówności", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "zakres_podstawowy": { + "nr": -1, + "opis": "Zakres podstawowy", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Przekształca równania i nierówności w sposób równoważny, w tym np. przekształca równoważnie równanie $5x + 1 = \\frac{x + 3}{2x - 1}$.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Interpretuje równania i nierówności liniowe sprzeczne oraz tożsamościowe.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Rozwiązuje nierówności liniowe z jedną niewiadomą.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Rozwiązuje równania i nierówności kwadratowe.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 5, + "opis": "Rozwiązuje równania wielomianowe postaci $W(x) = 0$ dla wielomianów doprowadzonych do postaci iloczynowej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + }, + "zakres_rozszerzony": { + "nr": -1, + "opis": "Zakres rozszerzony", + "atrybuty": { + "punkty": -1, + "obowiązkowe": -1 + }, + "uczen": [ + { + "nr": 1, + "opis": "Rozwiązuje równania wielomianowe postaci $W(x) = 0$ oraz nierówności wielomianowe typu: $W(x) > 0$, $W(x) \\geq 0$, $W(x) < 0$, $W(x) \\leq 0$ dla wielomianów doprowadzonych do postaci iloczynowej lub takich, które dają się doprowadzić do tej postaci metodą wyłączania wspólnego czynnika przed nawias lub metodą grupowania.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 2, + "opis": "Rozwiązuje równania i nierówności wymierne, które dają się sprowadzić do równania lub nierówności liniowej lub kwadratowej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 3, + "opis": "Stosuje wzory Viète’a dla równań kwadratowych.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 4, + "opis": "Rozwiązuje równania i nierówności z wartością bezwzględną.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 5, + "opis": "Analizuje równania i nierówności liniowe z parametrami oraz równania i nierówności kwadratowe z parametrami, w szczególności: wyznacza liczbę rozwiązań w zależności od parametrów, podaje warunki, przy których rozwiązania mają określone znaki bądź należą do określonego przedziału, wyznacza rozwiązania w zależności od parametrów.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 6, + "opis": "Rozwiązuje równania wielomianowe, które dają się doprowadzić do równania kwadratowego, w szczególności równania dwukwadratowe.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + }, + { + "nr": 7, + "opis": "Rozwiązuje równania wymierne postaci $\\frac{V(x)}{W(x)} = 0$, gdzie wielomiany $V(x)$ i $W(x)$ są zapisane w postaci iloczynowej.", + "atrybuty": { + "punkty": 1, + "obowiązkowe": 1 + } + } + ] + } + } + ] +} diff --git a/main.py b/main.py new file mode 100644 index 0000000..c746600 --- /dev/null +++ b/main.py @@ -0,0 +1,332 @@ +import os +import json + +from PySide6.QtWidgets import ( + QApplication, QMainWindow, QVBoxLayout, QWidget, QLabel, + QToolBar, QFileDialog, QMessageBox +) +from PySide6.QtGui import (QKeySequence, QAction, QWheelEvent) +from PySide6.QtCore import Qt +from PySide6.QtGui import QUndoStack + +# Importy lokalne +from widgets.drag_tree_widget import DragTreeWidget +from widgets.drop_tree_widget import DropTreeWidget +from widgets.common_tree_delegate import CommonTreeDelegate +from utils.colors import color_item_by_title +from utils.indentation import apply_indentation_to_all_columns + +from utils.helpers import display_number_or_dash, display_obligatory + + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("Drag & Drop - Dół -> Góra, Pastelowe kolory węzłów i dzieci") + + self.undo_stack = QUndoStack(self) + + # Górne drzewo: DROP (puste na starcie) + self.drop_tree = DropTreeWidget(self.undo_stack) + self.drop_tree.setHeaderLabels(["Punkty", "Obowiązkowe", "Nr", "Opis"]) + self.configure_header(self.drop_tree) + + # Dolne drzewo: DRAG (załadowane z pp.json) + self.drag_tree = DragTreeWidget() + self.drag_tree.setHeaderLabels(["Punkty", "Obowiązkowe", "Nr", "Opis"]) + self.configure_header(self.drag_tree) + + self.init_ui() + self.resize(1100, 700) + + # Ładujemy ./data/pp.json -> DOLNE drzewo + self.init_load_data() # <-- kluczowy fragment + + # Stosujemy wcięcia dla obu drzew + apply_indentation_to_all_columns(self.drag_tree) + apply_indentation_to_all_columns(self.drop_tree) + + def configure_header(self, tw): + """Ustawienie parametrów nagłówka w TreeWidget.""" + hd = tw.header() + # Możesz tu dostosować szerokości kolumn itd. + hd.setStretchLastSection(True) + + def init_ui(self): + """Tworzenie toolbaru i layoutu głównego.""" + tb = QToolBar("Toolbar", self) + self.addToolBar(tb) + + # Undo / Redo + undo_act = self.undo_stack.createUndoAction(self, "Undo") + undo_act.setShortcut(QKeySequence.Undo) + tb.addAction(undo_act) + + redo_act = self.undo_stack.createRedoAction(self, "Redo") + redo_act.setShortcut(QKeySequence("Ctrl+Y")) + tb.addAction(redo_act) + + tb.addSeparator() + + # Przyciski import/export dla testów (opcjonalnie) + imp_d = QAction("Import Dolne", self) + imp_d.triggered.connect(lambda: self.import_tree(self.drag_tree, True)) + + exp_d = QAction("Export Dolne", self) + exp_d.triggered.connect(lambda: self.export_tree(self.drag_tree)) + + imp_g = QAction("Import Górne", self) + imp_g.triggered.connect(lambda: self.import_tree(self.drop_tree, False)) + + exp_g = QAction("Export Górne", self) + exp_g.triggered.connect(lambda: self.export_tree(self.drop_tree)) + + tb.addAction(imp_d) + tb.addAction(exp_d) + tb.addAction(imp_g) + tb.addAction(exp_g) + + # Dodaj akcję Ctrl + T do rozwijania/zwijania + toggle_expand_action = QAction("Toggle Expand/Collapse (Ctrl+T)", self) + toggle_expand_action.setShortcut(QKeySequence("Ctrl+T")) + toggle_expand_action.triggered.connect(self.drop_tree.toggle_expand_selected_items) + tb.addAction(toggle_expand_action) + + # Dodaj akcję usuwania + delete_action = QAction("Usuń zaznaczone (Backspace)", self) + delete_action.setShortcut(QKeySequence(Qt.Key_Backspace)) + delete_action.triggered.connect(self.drop_tree.delete_selected_items) + tb.addAction(delete_action) + + # Ustaw kontekst skrótu tylko dla drzewa docelowego + delete_action.setShortcutContext(Qt.WidgetWithChildrenShortcut) + self.drop_tree.addAction(delete_action) + + # Layout główny + layout = QVBoxLayout() + layout.addWidget(QLabel("System Oceniania")) + layout.addWidget(self.drop_tree) + layout.addWidget(QLabel("Podstawa Programowa")) + layout.addWidget(self.drag_tree) + + container = QWidget() + container.setLayout(layout) + self.setCentralWidget(container) + + # ---------------------------------------------------------------- + # Wczytywanie pliku pp.json do DRAG TREE (dolnego) + # ---------------------------------------------------------------- + # main.py (fragment) + def init_load_data(self): + path = os.path.join("data", "pp.json") + if not os.path.isfile(path): + print("Brak pliku data/pp.json. Pomijam.") + return + + try: + with open(path, "r", encoding="utf-8") as f: + data = json.load(f) + + self.drag_tree.clear() + + # 1) Wymagania ogólne + it_ogolne = self.create_top_item([ + "-", # punkty + display_obligatory(-1), # '-' + "-", # nr + "Wymagania ogólne" + ]) + self.drag_tree.addTopLevelItem(it_ogolne) + + if "wymagania_ogolne" in data: + for req_data in data["wymagania_ogolne"]: + self.add_tree_item(it_ogolne, req_data, keep_removable=True, subitems_key="wymagania") + + # 2) Wymagania szczegółowe + it_szcz = self.create_top_item([ + "-", # punkty + display_obligatory(-1), # '-' + "-", # nr + "Wymagania szczegółowe" + ]) + self.drag_tree.addTopLevelItem(it_szcz) + + if "wymagania_szczegolowe" in data: + for block in data["wymagania_szczegolowe"]: + # block.get("nr", -1) => może zwrócić -1, wtedy na ekranie będzie '-' + block_atryb = block.get("atrybuty", {}) + punkty_val = block_atryb.get("punkty", -1) + obow_val = block_atryb.get("obowiązkowe", -1) + nr_val = block.get("nr", -1) + opis_val = block.get("opis", "") + + block_item = self.create_top_item([ + display_number_or_dash(punkty_val), + display_obligatory(obow_val), + display_number_or_dash(nr_val), + opis_val + ]) + + it_szcz.addChild(block_item) + + # Zakresy + for zakres_type in ["zakres_podstawowy", "zakres_rozszerzony"]: + zakres_data = block.get(zakres_type, {}) + atrybuty = zakres_data.get("atrybuty", {}) + + punkty_val = atrybuty.get("punkty", -1) + obow_val = atrybuty.get("obowiązkowe", -1) + nr_val = zakres_data.get("nr", -1) + opis_val = zakres_data.get("opis", "") + + zakres_item = self.create_top_item([ + display_number_or_dash(punkty_val), + display_obligatory(obow_val), + display_number_or_dash(nr_val), + opis_val + ]) + block_item.addChild(zakres_item) + + # Rekurencja dla "uczen" + for uczen_data in zakres_data.get("uczen", []): + self.add_tree_item(zakres_item, uczen_data, keep_removable=True) + + apply_indentation_to_all_columns(self.drag_tree) + + except Exception as e: + QMessageBox.warning(self, "Błąd", f"Błąd ładowania danych: {str(e)}") + + def create_top_item(self, texts): + """Tworzy węzeł top-level z automatycznym kolorowaniem.""" + from PySide6.QtWidgets import QTreeWidgetItem + it = QTreeWidgetItem(texts) + # Ustaw userRole (opcjonalnie - np. flaga 'removable') + it.setData(0, Qt.UserRole, True) + color_item_by_title(it) + return it + + def add_tree_item(self, parent_item, item_data, keep_removable=False, subitems_key="podpunkty"): + from PySide6.QtWidgets import QTreeWidgetItem + from utils.colors import color_item_by_title + from utils.helpers import display_number_or_dash, display_obligatory + + opis = item_data.get("opis", "") + atrybuty = item_data.get("atrybuty", {}) + + nr_val = item_data.get("nr", -1) + punkty_val = atrybuty.get("punkty", -1) + obow_val = atrybuty.get("obowiązkowe", 0) + + nr_str = display_number_or_dash(nr_val) + punkty_str = display_number_or_dash(punkty_val) + obow_str = display_obligatory(obow_val) + + it = QTreeWidgetItem([punkty_str, obow_str, nr_str, opis]) + if keep_removable: + it.setData(0, Qt.UserRole, True) + + color_item_by_title(it) + parent_item.addChild(it) + + # Obsługa podpunktów + for podpunkt in item_data.get(subitems_key, []): + self.add_tree_item(it, podpunkt, keep_removable, subitems_key) + + # ---------------------------------------------------------------- + # Funkcje import/export (dla testów) + # ---------------------------------------------------------------- + def import_tree(self, tree, keep_drag_flag=False): + fn, _ = QFileDialog.getOpenFileName(self, "Import JSON", "./data", "JSON Files (*.json)") + if not fn: + return + try: + with open(fn, "r", encoding="utf-8") as f: + data = json.load(f) + self.json_to_tree(data, tree, keep_drag_flag) + QMessageBox.information(self, "Import", f"Wczytano: {fn}") + except Exception as e: + QMessageBox.warning(self, "Błąd importu", str(e)) + + def export_tree(self, tree): + fn, _ = QFileDialog.getSaveFileName(self, "Export JSON", "./data", "JSON Files (*.json)") + if not fn: + return + data = self.tree_to_json(tree) + try: + with open(fn, "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=2) + QMessageBox.information(self, "Export", f"Zapisano: {fn}") + except Exception as e: + QMessageBox.warning(self, "Błąd zapisu", str(e)) + + def tree_to_json(self, tree): + def item_to_dict(it): + node = { + "punkty": it.text(0).strip(), + "obowiązkowe": it.text(1).strip(), + "nr": it.text(2).strip(), + "opis": it.text(3).strip(), + "children": [] + } + for i in range(it.childCount()): + node["children"].append(item_to_dict(it.child(i))) + return node + + data = {"items": []} + for i in range(tree.topLevelItemCount()): + top_item = tree.topLevelItem(i) + data["items"].append(item_to_dict(top_item)) + return data + + def json_to_tree(self, data: dict, tree, keep_drag_flag=False): + tree.clear() + + def dict_to_item(nd): + from PySide6.QtWidgets import QTreeWidgetItem + p_s = nd.get("punkty", "0").strip() + o_s = nd.get("obowiązkowe", "nie").strip() + nr_s = nd.get("nr", "").strip() + op_s = nd.get("opis", "").strip() + + it = QTreeWidgetItem([p_s, o_s, nr_s, op_s]) + if keep_drag_flag: + it.setData(0, Qt.UserRole, True) + + color_item_by_title(it) + for ch_d in nd.get("children", []): + ch_it = dict_to_item(ch_d) + it.addChild(ch_it) + return it + + for nd in data.get("items", []): + t_it = dict_to_item(nd) + tree.addTopLevelItem(t_it) + + apply_indentation_to_all_columns(tree) + + # ---------------------------------------------------------------- + # Zoom (Ctrl + scroll) + # ---------------------------------------------------------------- + def wheelEvent(self, event: QWheelEvent): + if event.modifiers() & Qt.ControlModifier: + delta = event.angleDelta().y() + step = 1 if delta > 0 else -1 + self.change_font_size(step) + event.accept() + else: + super().wheelEvent(event) + + def change_font_size(self, delta): + font = self.drop_tree.font() + sz = font.pointSize() + delta + sz = max(6, sz) + font.setPointSize(sz) + self.drop_tree.setFont(font) + self.drag_tree.setFont(font) + + +if __name__ == "__main__": + app = QApplication([]) + w = MainWindow() + w.show() + app.exec() diff --git a/out/out3.json b/out/out3.json new file mode 100644 index 0000000..90f7ad2 --- /dev/null +++ b/out/out3.json @@ -0,0 +1,1433 @@ +{ + "items": [ + { + "punkty": "-", + "obowiązkowe": "nie", + "nr": "-", + "opis": "Poziom Nauczania: 1", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Wymagania szczegółowe", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "1", + "opis": "Liczby rzeczywiste", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Zakres podstawowy", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Wykonuje działania (dodawanie, odejmowanie, mnożenie, dzielenie, potęgowanie, pierwiastkowanie, logarytmowanie) w zbiorze liczb rzeczywistych.", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Wymagania ogólne", + "children": [ + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Sprawność rachunkowa.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Wykorzystanie i tworzenie informacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Rozumowanie i argumentacja.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "children": [] + } + ] + } + ] + } + ] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Przeprowadza proste dowody dotyczące podzielności liczb całkowitych i reszt z dzielenia.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "a", + "opis": "Dowód podzielności przez 24 iloczynu czterech kolejnych liczb naturalnych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "b", + "opis": "Dowód własności: jeśli liczba przy dzieleniu przez 4 daje resztę 3, to nie jest kwadratem liczby całkowitej.", + "children": [] + }, + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Wymagania ogólne", + "children": [ + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Sprawność rachunkowa.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Wykorzystanie i tworzenie informacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Rozumowanie i argumentacja.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "children": [] + } + ] + } + ] + } + ] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Stosuje własności pierwiastków dowolnego stopnia, w tym pierwiastków stopnia nieparzystego z liczb ujemnych.", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Wymagania ogólne", + "children": [ + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Sprawność rachunkowa.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Wykorzystanie i tworzenie informacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Rozumowanie i argumentacja.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "children": [] + } + ] + } + ] + } + ] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Stosuje związek pierwiastkowania z potęgowaniem oraz prawa działań na potęgach i pierwiastkach.", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Wymagania ogólne", + "children": [ + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Sprawność rachunkowa.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Wykorzystanie i tworzenie informacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Rozumowanie i argumentacja.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "children": [] + } + ] + } + ] + } + ] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "5", + "opis": "Stosuje monotoniczność potęgowania, w szczególności własności: jeśli $x < y$ oraz $a > 1$, to $a^x < a^y$, zaś gdy $x < y$ i $0 < a < 1$, to $a^x > a^y$.", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Wymagania ogólne", + "children": [ + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Sprawność rachunkowa.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Wykorzystanie i tworzenie informacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Rozumowanie i argumentacja.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "children": [] + } + ] + } + ] + } + ] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "6", + "opis": "Posługuje się pojęciem przedziału liczbowego, zaznacza przedziały na osi liczbowej.", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Wymagania ogólne", + "children": [ + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Sprawność rachunkowa.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Wykorzystanie i tworzenie informacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Rozumowanie i argumentacja.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "children": [] + } + ] + } + ] + } + ] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "7", + "opis": "Stosuje interpretację geometryczną i algebraiczną wartości bezwzględnej, rozwiązuje równania typu: $|x + 4| = 5$.", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Wymagania ogólne", + "children": [ + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Sprawność rachunkowa.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Wykorzystanie i tworzenie informacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Rozumowanie i argumentacja.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "children": [] + } + ] + } + ] + } + ] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "8", + "opis": "Wykorzystuje własności potęgowania i pierwiastkowania w sytuacjach praktycznych, w tym do obliczania procentów składanych, zysków z lokat i kosztów kredytów.", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Wymagania ogólne", + "children": [ + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Sprawność rachunkowa.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Wykorzystanie i tworzenie informacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Rozumowanie i argumentacja.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "children": [] + } + ] + } + ] + } + ] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "9", + "opis": "Stosuje związek logarytmowania z potęgowaniem, posługuje się wzorami na logarytm iloczynu, logarytm ilorazu i logarytm potęgi.", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Wymagania ogólne", + "children": [ + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Sprawność rachunkowa.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Wykorzystanie i tworzenie informacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Rozumowanie i argumentacja.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "children": [] + } + ] + } + ] + } + ] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Zakres rozszerzony", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Spełnia wymagania określone dla zakresu podstawowego.", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Wymagania ogólne", + "children": [ + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Sprawność rachunkowa.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Wykorzystanie i tworzenie informacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Rozumowanie i argumentacja.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "children": [] + } + ] + } + ] + } + ] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Stosuje wzór na zamianę podstawy logarytmu.", + "children": [ + { + "punkty": "-", + "obowiązkowe": "-", + "nr": "-", + "opis": "Wymagania ogólne", + "children": [ + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Sprawność rachunkowa.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Wykonywanie obliczeń na liczbach rzeczywistych, także przy użyciu kalkulatora, stosowanie praw działań matematycznych przy przekształcaniu wyrażeń algebraicznych oraz wykorzystywanie tych umiejętności przy rozwiązywaniu problemów w kontekstach rzeczywistych i teoretycznych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Wykorzystanie i tworzenie informacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Interpretowanie i operowanie informacjami przedstawionymi w tekście, zarówno matematycznym, jak i popularnonaukowym, a także w formie wykresów, diagramów, tabel.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Używanie języka matematycznego do tworzenia tekstów matematycznych, w tym do opisu prowadzonych rozumowań i uzasadniania wniosków, a także do przedstawiania danych.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Wykorzystanie i interpretowanie reprezentacji.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Stosowanie obiektów matematycznych i operowanie nimi, interpretowanie pojęć matematycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dobieranie i tworzenie modeli matematycznych przy rozwiązywaniu problemów praktycznych i teoretycznych.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Tworzenie pomocniczych obiektów matematycznych na podstawie istniejących, w celu przeprowadzenia argumentacji lub rozwiązania problemu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Wskazywanie konieczności lub możliwości modyfikacji modelu matematycznego w przypadkach wymagających specjalnych zastrzeżeń, dodatkowych założeń, rozważenia szczególnych uwarunkowań.", + "children": [] + } + ] + }, + { + "punkty": "-", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Rozumowanie i argumentacja.", + "children": [ + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "1", + "opis": "Przeprowadzanie rozumowań, także kilkuetapowych, podawanie argumentów uzasadniających poprawność rozumowania, odróżnianie dowodu od przykładu.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "2", + "opis": "Dostrzeganie regularności, podobieństw oraz analogii, formułowanie wniosków na ich podstawie i uzasadnianie ich poprawności.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "3", + "opis": "Dobieranie argumentów do uzasadnienia poprawności rozwiązywania problemów, tworzenie ciągu argumentów gwarantujących poprawność rozwiązania i skuteczność w poszukiwaniu rozwiązań zagadnienia.", + "children": [] + }, + { + "punkty": "1", + "obowiązkowe": "tak", + "nr": "4", + "opis": "Stosowanie i tworzenie strategii przy rozwiązywaniu zadań, również w sytuacjach nietypowych.", + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/colors.py b/utils/colors.py new file mode 100644 index 0000000..f32f5ea --- /dev/null +++ b/utils/colors.py @@ -0,0 +1,41 @@ +from PySide6.QtGui import QColor, QBrush +from PySide6.QtWidgets import QTreeWidgetItem +from PySide6.QtCore import Qt + +############################################################################### +# Kolory pastelowe (top-level + jaśniejsze dla dzieci) # +############################################################################### +GREEN = QColor("#d2ffd2") # ogólne +RED = QColor("#ffd2d2") # szczegółowe +BLUE = QColor("#d2d2ff") # przekrojowe + +GREEN_LIGHT = QColor("#f0fff0") +RED_LIGHT = QColor("#ffecec") +BLUE_LIGHT = QColor("#f0f0ff") + +def color_top_level_item(item: QTreeWidgetItem, color: QColor): + """Ustawia tło w całym wierszu (kolumnach) na podany kolor.""" + brush = QBrush(color) + for c in range(item.columnCount()): + item.setBackground(c, brush) + +def color_item_by_title(item: QTreeWidgetItem): + """Koloruje w zależności od kategorii najwyższego rodzica (ogólne / szczegółowe / przekrojowe).""" + top_parent = item + while top_parent.parent() is not None: + top_parent = top_parent.parent() + + title_lower = top_parent.text(3).lower() + is_child = (item != top_parent) + + if "wymagania ogólne" in title_lower: + color = GREEN_LIGHT if is_child else GREEN + elif "wymagana przekrojowe" in title_lower: + color = BLUE_LIGHT if is_child else BLUE + elif "wymagania szczegółowe" in title_lower: + color = RED_LIGHT if is_child else RED + else: + return # Brak dopasowania + + color_top_level_item(item, color) + diff --git a/utils/common_tree_delegate.py b/utils/common_tree_delegate.py new file mode 100644 index 0000000..b4fbcc2 --- /dev/null +++ b/utils/common_tree_delegate.py @@ -0,0 +1,51 @@ +from PySide6.QtWidgets import QStyledItemDelegate, QSpinBox +from PySide6.QtCore import Qt, QEvent, QModelIndex +from PySide6.QtGui import QMouseEvent + +class CommonTreeDelegate(QStyledItemDelegate): + def createEditor(self, parent, option, index: QModelIndex): + col = index.column() + if col == 0: + # Kolumna 0: QSpinBox z zakresem [0..10] + editor = QSpinBox(parent) + editor.setRange(0, 10) + return editor + elif col == 1: + # Kolumna 1: toggle "tak/nie" – obsługiwane double-clickiem + return None + return super().createEditor(parent, option, index) + + def setEditorData(self, editor, index: QModelIndex): + col = index.column() + if col == 0: + val_str = index.data(Qt.EditRole) + if val_str is None: + val_str = index.data(Qt.DisplayRole) + try: + val = int(val_str) + except ValueError: + val = 0 + editor.setValue(val) + else: + super().setEditorData(editor, index) + + def setModelData(self, editor, model, index: QModelIndex): + col = index.column() + if col == 0: + val = editor.value() + model.setData(index, str(val), Qt.EditRole) + elif col == 1: + # Podwójny klik przełącza "tak"/"nie" + cur = index.data(Qt.DisplayRole) + new_val = "nie" if cur == "tak" else "tak" + model.setData(index, new_val, Qt.EditRole) + else: + super().setModelData(editor, model, index) + + def editorEvent(self, event, model, option, index): + # Obsługa double-click w kolumnie 1 (toggle "tak"/"nie") + if index.column() == 1 and event.type() == QEvent.MouseButtonDblClick: + self.commitData.emit(None) + return True + return super().editorEvent(event, model, option, index) + diff --git a/utils/helpers.py b/utils/helpers.py new file mode 100644 index 0000000..3044c93 --- /dev/null +++ b/utils/helpers.py @@ -0,0 +1,34 @@ +# utils/helpers.py + +def display_number_or_dash(value) -> str: + """ + Zwraca '-' jeśli wartość == -1, + w przeciwnym razie zwraca liczbę jako string. + """ + try: + val_int = int(value) + if val_int == -1: + return "-" + return str(val_int) + except ValueError: + # Gdyby "value" nie dało się zrzutować na int + return str(value) + +def display_obligatory(value) -> str: + """ + Dla pola 'obowiązkowe': + - jeśli wartość == -1 -> '-' + - jeśli wartość == 1 -> 'tak' + - w pozostałych przypadkach -> 'nie' + """ + try: + val_int = int(value) + except ValueError: + val_int = 0 # traktujemy jako 'nie', albo można zwrócić np. 'nie' + + if val_int == -1: + return "-" + elif val_int == 1: + return "tak" + else: + return "nie" diff --git a/utils/indentation.py b/utils/indentation.py new file mode 100644 index 0000000..fc30f61 --- /dev/null +++ b/utils/indentation.py @@ -0,0 +1,24 @@ +from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem +from PySide6.QtCore import Qt + +def apply_indentation_to_all_columns(tree: QTreeWidget): + """ + Rekurencyjnie stosuje wizualne wcięcie dla wszystkich elementów w drzewie + we wszystkich kolumnach. + """ + for i in range(tree.topLevelItemCount()): + item = tree.topLevelItem(i) + _indent_subtree(item, 0) + +def _indent_subtree(item: QTreeWidgetItem, level: int): + """Dodaje wcięcie (np. spacje) na każdy poziom hierarchii w całym wierszu.""" + indent_spaces = " " * 2 * level + for col in range(item.columnCount()): + original_text = item.text(col).lstrip() + item.setText(col, f"{indent_spaces}{original_text}") + item.setData(col, Qt.TextAlignmentRole, Qt.AlignLeft | Qt.AlignVCenter) + + # Rekurencja dla dzieci + for i in range(item.childCount()): + _indent_subtree(item.child(i), level + 1) + diff --git a/widgets/__init__.py b/widgets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/widgets/common_tree_delegate.py b/widgets/common_tree_delegate.py new file mode 100644 index 0000000..b4fbcc2 --- /dev/null +++ b/widgets/common_tree_delegate.py @@ -0,0 +1,51 @@ +from PySide6.QtWidgets import QStyledItemDelegate, QSpinBox +from PySide6.QtCore import Qt, QEvent, QModelIndex +from PySide6.QtGui import QMouseEvent + +class CommonTreeDelegate(QStyledItemDelegate): + def createEditor(self, parent, option, index: QModelIndex): + col = index.column() + if col == 0: + # Kolumna 0: QSpinBox z zakresem [0..10] + editor = QSpinBox(parent) + editor.setRange(0, 10) + return editor + elif col == 1: + # Kolumna 1: toggle "tak/nie" – obsługiwane double-clickiem + return None + return super().createEditor(parent, option, index) + + def setEditorData(self, editor, index: QModelIndex): + col = index.column() + if col == 0: + val_str = index.data(Qt.EditRole) + if val_str is None: + val_str = index.data(Qt.DisplayRole) + try: + val = int(val_str) + except ValueError: + val = 0 + editor.setValue(val) + else: + super().setEditorData(editor, index) + + def setModelData(self, editor, model, index: QModelIndex): + col = index.column() + if col == 0: + val = editor.value() + model.setData(index, str(val), Qt.EditRole) + elif col == 1: + # Podwójny klik przełącza "tak"/"nie" + cur = index.data(Qt.DisplayRole) + new_val = "nie" if cur == "tak" else "tak" + model.setData(index, new_val, Qt.EditRole) + else: + super().setModelData(editor, model, index) + + def editorEvent(self, event, model, option, index): + # Obsługa double-click w kolumnie 1 (toggle "tak"/"nie") + if index.column() == 1 and event.type() == QEvent.MouseButtonDblClick: + self.commitData.emit(None) + return True + return super().editorEvent(event, model, option, index) + diff --git a/widgets/drag_tree_widget.py b/widgets/drag_tree_widget.py new file mode 100644 index 0000000..b412851 --- /dev/null +++ b/widgets/drag_tree_widget.py @@ -0,0 +1,150 @@ +import json +from PySide6.QtWidgets import ( + QTreeWidget, QTreeWidgetItem, + QAbstractItemView, QApplication +) +from PySide6.QtCore import QPoint, Qt, QMimeData +from PySide6.QtGui import QDrag, QMouseEvent + + +class DragTreeWidget(QTreeWidget): + """ + Drzewo źródłowe (drag source). Umożliwia przeciąganie wybranych węzłów + w formacie JSON do DropTreeWidget. + """ + def __init__(self, parent=None): + super().__init__(parent) + self.setSelectionMode(QAbstractItemView.ExtendedSelection) + self.setDragEnabled(True) + # Edycja np. double-click lub kliknięcie wybranego + self.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.SelectedClicked) + + self._drag_start_pos = QPoint() + self._drag_in_progress = False # Flaga kontroli przeciągania + + def handle_item_click(self, item, column): + """ + Po kliknięciu w węzeł kopiuje do schowka JSON zawierający + ścieżkę (rodzice) od korzenia do tego węzła – ale BEZ dzieci! + """ + chain_json = self.item_to_json_parents_no_children(item) + print("[LOG] Copied item + parents (NO children) to clipboard:") + print(chain_json) + + from PySide6.QtWidgets import QApplication + QApplication.clipboard().setText(chain_json) + + def item_to_json_parents_no_children(self, item: QTreeWidgetItem) -> str: + """ + Buduje strukturę JSON (jako string) dla pojedynczej ścieżki + 'korzeń -> ... -> item', bez dzieci. Każdy węzeł ma 'children': []. + """ + import json + + # Zacznij od zaznaczonego węzła + node_dict = self.item_to_dict_no_children(item) + + # Idź w górę, owijając node_dict w kolejnych rodziców + current = item.parent() + while current is not None: + parent_dict = self.item_to_dict_no_children(current) + parent_dict["children"] = [node_dict] + node_dict = parent_dict + current = current.parent() + + # Zamień na ładny, sformatowany JSON + return json.dumps(node_dict, ensure_ascii=False, indent=2) + + def item_to_dict_no_children(self, item: QTreeWidgetItem) -> dict: + """ + Zwraca słownik z polami (punkty, obowiązkowe, nr, opis), + ale 'children' zawsze jest pustą listą. + """ + return { + "punkty": item.text(0), + "obowiązkowe": item.text(1), + "nr": item.text(2), + "opis": item.text(3), + "children": [] + } + + def mousePressEvent(self, event: QMouseEvent): + item = self.itemAt(event.position().toPoint()) + if item: + # 1) Alt + LPM: kopiowanie do schowka (łańcuch bez dzieci) + if (event.button() == Qt.LeftButton) and (event.modifiers() & Qt.ShiftModifier): + chain_json = self.item_to_json_parents_no_children(item) + from PySide6.QtWidgets import QApplication + QApplication.clipboard().setText(chain_json) + return # Nie zaznaczaj już tego węzła + + # 2) Shift + LPM: kopiowanie *tylko* opisu (kolumna 3) klikniętego węzła + if (event.button() == Qt.LeftButton) and (event.modifiers() & Qt.AltModifier): + from PySide6.QtWidgets import QApplication + opis = item.text(3).strip() # kolumna 3 + QApplication.clipboard().setText(opis) + return # Nie zaznaczaj już tego węzła + + # Poniższa logika pozostaje bez zmian (drag, normalne zaznaczenie itp.) + if event.button() == Qt.LeftButton: + self._drag_start_pos = event.position().toPoint() + self._drag_in_progress = False + + super().mousePressEvent(event) + + def mouseMoveEvent(self, event: QMouseEvent): + if event.buttons() & Qt.LeftButton: + dist = (event.position().toPoint() - self._drag_start_pos).manhattanLength() + if dist > QApplication.startDragDistance() and not self._drag_in_progress: + self._drag_in_progress = True + self.startDrag() + super().mouseMoveEvent(event) + + def startDrag(self, supportedActions=Qt.MoveAction): + if self._drag_in_progress: + selected_items = self.selectedItems() + if not selected_items: + self._drag_in_progress = False + return + + # Konwertujemy zaznaczone elementy na listę JSON (uwzględniamy hierarchię) + data_list = [self.item_to_dict_with_parents(item) for item in selected_items] + formatted_json = json.dumps(data_list, ensure_ascii=False, indent=4) + + drag = QDrag(self) + mime = QMimeData() + mime.setText(formatted_json) + drag.setMimeData(mime) + + result = drag.exec(Qt.CopyAction) + self._drag_in_progress = False + + def item_to_dict_with_parents(self, item: QTreeWidgetItem) -> dict: + """ + Buduje pełną hierarchię rodziców i dzieci od wybranego elementu aż do korzenia. + """ + current_item = item + full_hierarchy = None + + while current_item is not None: + node = self.item_to_dict_subtree(current_item) + if full_hierarchy is not None: + node["children"] = [full_hierarchy] + full_hierarchy = node + current_item = current_item.parent() + + return full_hierarchy + + def item_to_dict_subtree(self, item: QTreeWidgetItem) -> dict: + """Rekurencyjnie przekształca węzeł i jego dzieci w słownik.""" + node_data = { + "punkty": item.text(0).strip(), + "obowiązkowe": item.text(1).strip(), + "nr": item.text(2).strip(), + "opis": item.text(3).strip(), + "children": [] + } + for i in range(item.childCount()): + child = item.child(i) + node_data["children"].append(self.item_to_dict_subtree(child)) + return node_data diff --git a/widgets/drop_tree_widget.py b/widgets/drop_tree_widget.py new file mode 100644 index 0000000..635c26d --- /dev/null +++ b/widgets/drop_tree_widget.py @@ -0,0 +1,444 @@ +import json +from typing import Optional + +from PySide6.QtWidgets import ( + QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMessageBox, QMenu, + QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QCheckBox, + QPushButton, QSpinBox, QDialogButtonBox, QFormLayout, QPlainTextEdit, + QInputDialog, QStyledItemDelegate, QStyleOptionViewItem +) +from PySide6.QtCore import Qt, QPoint, QModelIndex +from PySide6.QtGui import QMouseEvent, QPainter + +# -------------------------------------------------------------------- +# DELEGAT DO WCIĘĆ (IndentationDelegate) +# -------------------------------------------------------------------- +class IndentationDelegate(QStyledItemDelegate): + """ + Delegate rysujący wcięcie w KAŻDEJ kolumnie, + zależne od poziomu zagnieżdżenia w drzewie (rodziców). + Nie modyfikuje item.text(). + """ + def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex): + level = self.get_depth(index) + indent_px = 20 * level # 20 pikseli na poziom + new_option = QStyleOptionViewItem(option) + # przesunięcie rect w prawo o indent_px + new_option.rect.translate(indent_px, 0) + # zmniejszenie szerokości o indent_px + new_option.rect.setWidth(new_option.rect.width() - indent_px) + + super().paint(painter, new_option, index) + + def get_depth(self, index: QModelIndex) -> int: + depth = 0 + model = index.model() + parent_idx = model.parent(index) + while parent_idx.isValid(): + depth += 1 + parent_idx = model.parent(parent_idx) + return depth + + +from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMessageBox, QMenu +from PySide6.QtCore import Qt, QPoint +from PySide6.QtGui import QMouseEvent + +# Import komend (Undo/Redo) +from commands import AddItemCommand, RemoveItemCommand, EditPunktyCommand + +# Narzędzia kolorowania i wcięć +from utils.colors import color_item_by_title +from utils.indentation import apply_indentation_to_all_columns + + +############################################################################### +# AddItemDialog # +############################################################################### +class AddItemDialog(QDialog): + """ + Proste okno dialogowe, pozwalające wprowadzić: + - nr (int) + - opis (string) + - punkty (int, np. -1, 0, 5) + - obowiązkowe (checkbox -> True/False) + """ + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowTitle("Dodaj nowy węzeł") + + self.nr_spin = QSpinBox() + self.nr_spin.setRange(-9999, 9999) + self.nr_spin.setValue(1) + + self.opis_edit = QLineEdit() + self.opis_edit.setPlaceholderText("np. Sprawność rachunkowa...") + + self.punkty_spin = QSpinBox() + self.punkty_spin.setRange(-9999, 9999) + self.punkty_spin.setValue(-1) + + self.obow_checkbox = QCheckBox("Obowiązkowe?") + + form_layout = QFormLayout() + form_layout.addRow("Nr:", self.nr_spin) + form_layout.addRow("Opis:", self.opis_edit) + form_layout.addRow("Punkty:", self.punkty_spin) + form_layout.addRow(self.obow_checkbox) + + self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, parent=self) + self.button_box.accepted.connect(self.accept) + self.button_box.rejected.connect(self.reject) + + main_layout = QVBoxLayout() + main_layout.addLayout(form_layout) + main_layout.addWidget(self.button_box) + self.setLayout(main_layout) + + def get_data(self) -> dict: + return { + "nr": self.nr_spin.value(), + "opis": self.opis_edit.text().strip(), + "punkty": self.punkty_spin.value(), + "obowiązkowe": self.obow_checkbox.isChecked() + } + + +############################################################################### +# DropTreeWidget # +############################################################################### +class DropTreeWidget(QTreeWidget): + """ + Drzewo docelowe (drop target). Przyjmuje JSON via drag&drop z DragTreeWidget, + umożliwia Undo/Redo, kasowanie, edycję punktów, itd. + + Dodajemy IndentationDelegate do rysowania wcięć we wszystkich kolumnach. + """ + def __init__(self, undo_stack=None, parent=None): + super().__init__(parent) + self.undo_stack = undo_stack + self.setAcceptDrops(True) + self.setDropIndicatorShown(True) + self.setDragDropMode(QAbstractItemView.DropOnly) + self.setSelectionMode(QAbstractItemView.ExtendedSelection) + self.drop_event_in_progress = False + + # ------- Ustaw delegata do wcięć w 4 kolumnach ------- + delegate = IndentationDelegate(self) + for col in range(4): + self.setItemDelegateForColumn(col, delegate) + # ----------------------------------------------------- + + # ---------------------------------------------------------------- + # KONTEKSTOWE MENU (PRAWY KLIK) + # ---------------------------------------------------------------- + def contextMenuEvent(self, event): + menu = QMenu(self) + + add_act = menu.addAction("Dodaj węzeł") + paste_act = menu.addAction("Wklej JSON Subtree") + + # Jeśli mamy 1 zaznaczony węzeł z punktem != -1, pokaż edycję + selected_items = self.selectedItems() + edit_punkty_act = None + if len(selected_items) == 1: + item = selected_items[0] + punkty_str = item.text(0).strip() + if punkty_str not in ("-1", "-", ""): + try: + val = int(punkty_str) + if val != -1: + edit_punkty_act = menu.addAction("Edytuj punktację (1..10)") + except ValueError: + pass + + chosen_action = menu.exec(self.mapToGlobal(event.pos())) + if chosen_action == add_act: + self.add_item_interactive() + elif chosen_action == paste_act: + self.paste_json_subtree() + elif chosen_action == edit_punkty_act: + self.edit_punkty_1_10(selected_items[0]) + + def add_item_interactive(self): + from PySide6.QtWidgets import QTreeWidgetItem + from commands import AddItemCommand + from utils.helpers import display_number_or_dash, display_obligatory + + dialog = AddItemDialog(self) + if dialog.exec() == QDialog.Accepted: + data = dialog.get_data() + selected_items = self.selectedItems() + if selected_items: + parent_item = selected_items[0] + index = parent_item.childCount() + else: + parent_item = None + index = self.topLevelItemCount() + + punkty_str = display_number_or_dash(data["punkty"]) + obow_val = 1 if data["obowiązkowe"] else 0 + obow_str = display_obligatory(obow_val) + nr_str = display_number_or_dash(data["nr"]) + opis_str = data["opis"] + + new_item = QTreeWidgetItem([punkty_str, obow_str, nr_str, opis_str]) + color_item_by_title(new_item) + + cmd = AddItemCommand(self, parent_item, index, new_item, "Add new item") + if self.undo_stack: + self.undo_stack.push(cmd) + else: + cmd.redo() + + # ---------------------------------------------------------------- + # EDYTOWANIE PUNKTÓW (1..10) - QInputDialog, Undo/Redo + # ---------------------------------------------------------------- + def edit_punkty_1_10(self, item: QTreeWidgetItem): + from commands import EditPunktyCommand + + old_val_str = item.text(0).strip() + try: + old_val_int = int(old_val_str) + except ValueError: + return + + new_val_int, ok = QInputDialog.getInt( + self, + "Edytuj punktację", + f"Aktualna wartość = {old_val_int}. Wybierz nową (1..10):", + old_val_int, + 1, # min + 10 # max + ) + if not ok: + return + if new_val_int == old_val_int: + return + + cmd = EditPunktyCommand(item, str(old_val_int), str(new_val_int), "Edit punkty") + if self.undo_stack: + self.undo_stack.push(cmd) + else: + cmd.redo() + + # ------------------------------------- + # Shift + = / Shift + - -> +/- 1 + # ------------------------------------- + def keyPressEvent(self, event): + if event.modifiers() & Qt.ShiftModifier: + if event.key() in (Qt.Key_Equal, Qt.Key_Plus): + self.handle_punkty_inc_dec(+1) + return + elif event.key() in (Qt.Key_Minus, Qt.Key_Underscore): + self.handle_punkty_inc_dec(-1) + return + + if event.key() == Qt.Key_Backspace: + self.delete_selected_items() + event.accept() + elif event.modifiers() & Qt.ControlModifier and event.key() == Qt.Key_T: + self.toggle_expand_selected_items() + event.accept() + else: + super().keyPressEvent(event) + + def handle_punkty_inc_dec(self, inc: int): + from commands import EditPunktyCommand + + sel = self.selectedItems() + if len(sel) != 1: + return + item = sel[0] + old_val_str = item.text(0).strip() + try: + old_val_int = int(old_val_str) + except ValueError: + return + if old_val_int == -1: + return + + new_val_int = old_val_int + inc + new_val_int = max(1, min(10, new_val_int)) + if new_val_int == old_val_int: + return + + cmd = EditPunktyCommand(item, str(old_val_int), str(new_val_int), "Inc/Dec punkty") + if self.undo_stack: + self.undo_stack.push(cmd) + else: + cmd.redo() + + # ---------------------------------------------------------------- + # USUWANIE - Undo/Redo + # ---------------------------------------------------------------- + def delete_selected_items(self): + selected = self.selectedItems() + for item in reversed(selected): + cmd = RemoveItemCommand(self, item) + if self.undo_stack: + self.undo_stack.push(cmd) + else: + cmd.redo() + + # ---------------------------------------------------------------- + # Expand toggling (np. Ctrl+T w MainWindow) + # ---------------------------------------------------------------- + def toggle_expand_selected_items(self): + selected_items = self.selectedItems() + for item in selected_items: + self.toggle_expand_item_and_children(item) + + def toggle_expand_item_and_children(self, item: QTreeWidgetItem): + if item.isExpanded(): + self.collapseItem(item) + else: + self.expandItem(item) + for i in range(item.childCount()): + self.toggle_expand_item_and_children(item.child(i)) + + # ------------------------------------- + # Obsługa myszy (zaznaczenie) + # ------------------------------------- + def mousePressEvent(self, event: QMouseEvent): + item = self.itemAt(event.position().toPoint()) + if item: + if event.modifiers() & Qt.ControlModifier: + item.setSelected(not item.isSelected()) + else: + super().mousePressEvent(event) + else: + super().mousePressEvent(event) + + # ------------------------------------- + # DRAG & DROP + # ------------------------------------- + def dragEnterEvent(self, event): + if event.mimeData().hasText(): + event.acceptProposedAction() + else: + event.ignore() + + def dragMoveEvent(self, event): + if event.mimeData().hasText(): + event.acceptProposedAction() + else: + event.ignore() + + def dropEvent(self, event): + if self.drop_event_in_progress: + event.ignore() + return + + self.drop_event_in_progress = True + try: + pos = event.position().toPoint() + target_item = self.itemAt(pos) + + try: + data_list = json.loads(event.mimeData().text()) + except json.JSONDecodeError: + event.ignore() + return + + for node_data in data_list: + self.add_subtree_recursively(target_item, node_data) + + # POZOSTAWIAMY Twoją logikę (apply_indentation_to_all_columns), + # choć delegat robi wcięcia. + apply_indentation_to_all_columns(self) + + event.acceptProposedAction() + finally: + self.drop_event_in_progress = False + + # ---------------------------------------------------------------- + # Logika dodawania / scalania węzłów + # ---------------------------------------------------------------- + def add_subtree_recursively(self, target_parent: Optional[QTreeWidgetItem], node_data: dict): + existing_parent = self.find_matching_parent(target_parent, node_data) + if existing_parent: + self.merge_all_children(existing_parent, node_data.get("children", [])) + else: + new_parent = QTreeWidgetItem([ + node_data.get("punkty", "0"), + node_data.get("obowiązkowe", "nie"), + node_data.get("nr", ""), + node_data.get("opis", "") + ]) + color_item_by_title(new_parent) + + if target_parent: + target_parent.addChild(new_parent) + else: + self.addTopLevelItem(new_parent) + + self.merge_all_children(new_parent, node_data.get("children", [])) + + def find_matching_parent(self, target_parent: Optional[QTreeWidgetItem], node_data: dict) -> Optional[QTreeWidgetItem]: + return self.find_child_by_attributes(target_parent, node_data.get("nr", ""), node_data.get("opis", "")) + + def merge_all_children(self, parent_item: QTreeWidgetItem, children_data: list): + for child_data in children_data: + existing_child = self.find_child_by_attributes( + parent_item, + child_data.get("nr", ""), + child_data.get("opis", "") + ) + if existing_child: + self.merge_all_children(existing_child, child_data.get("children", [])) + else: + new_child = QTreeWidgetItem([ + child_data.get("punkty", "0"), + child_data.get("obowiązkowe", "nie"), + child_data.get("nr", ""), + child_data.get("opis", "") + ]) + color_item_by_title(new_child) + parent_item.addChild(new_child) + self.merge_all_children(new_child, child_data.get("children", [])) + + def find_child_by_attributes(self, parent_item: Optional[QTreeWidgetItem], nr: str, opis: str) -> Optional[QTreeWidgetItem]: + if parent_item: + for i in range(parent_item.childCount()): + child = parent_item.child(i) + if child.text(2).strip() == nr.strip() and child.text(3).strip() == opis.strip(): + return child + else: + for i in range(self.topLevelItemCount()): + item = self.topLevelItem(i) + if item.text(2).strip() == nr.strip() and item.text(3).strip() == opis.strip(): + return item + return None + +# ---- [PONIŻEJ DODAJEMY DELEGAT DO WCIĘĆ] ---- +class IndentationDelegate(QStyledItemDelegate): + """ + Delegate rysujący wcięcia w każdej kolumnie w zależności od poziomu + zagnieżdżenia w drzewie. Nie modyfikuje samego tekstu w węźle. + """ + def paint(self, painter, option, index): + level = self.get_depth(index) + indent_px = 20 * level + new_option = QStyleOptionViewItem(option) + new_option.rect.translate(indent_px, 0) + new_option.rect.setWidth(new_option.rect.width() - indent_px) + super().paint(painter, new_option, index) + + def get_depth(self, index): + depth = 0 + model = index.model() + parent_index = model.parent(index) + while parent_index.isValid(): + depth += 1 + parent_index = model.parent(parent_index) + return depth + + +# (Na końcu pliku) W konstruktorze DropTreeWidget +# dopisz: +""" +delegate = IndentationDelegate(self) +for col in range(4): + self.setItemDelegateForColumn(col, delegate) +""" diff --git a/widgets/drop_tree_widget.py-k b/widgets/drop_tree_widget.py-k new file mode 100644 index 0000000..3a088b7 --- /dev/null +++ b/widgets/drop_tree_widget.py-k @@ -0,0 +1,402 @@ +import json +from typing import Optional + +from PySide6.QtWidgets import ( + QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMessageBox, QMenu +) +from PySide6.QtCore import Qt, QMimeData, QPoint +from PySide6.QtGui import QMouseEvent + +# Import komend (Undo/Redo) +from commands import AddItemCommand, RemoveItemCommand + +# Import narzędzi kolorowania i wcięć +from utils.colors import color_item_by_title +from utils.indentation import apply_indentation_to_all_columns + +# W pliku: drop_tree_widget.py +from PySide6.QtWidgets import ( + QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QCheckBox, + QPushButton, QSpinBox, QDialogButtonBox, QFormLayout +) +from PySide6.QtCore import Qt + + +class AddItemDialog(QDialog): + """ + Proste okno dialogowe, pozwalające wprowadzić: + - nr (int) + - opis (string) + - punkty (int, np. -1, 0, 5) + - obowiązkowe (checkbox -> True/False) + """ + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowTitle("Dodaj nowy węzeł") + + # Pola formularza + self.nr_spin = QSpinBox() + self.nr_spin.setRange(-9999, 9999) + self.nr_spin.setValue(1) + + self.opis_edit = QLineEdit() + self.opis_edit.setPlaceholderText("np. Sprawność rachunkowa...") + + self.punkty_spin = QSpinBox() + self.punkty_spin.setRange(-9999, 9999) + self.punkty_spin.setValue(-1) + + self.obow_checkbox = QCheckBox("Obowiązkowe?") + + # Układ typu FormLayout + form_layout = QFormLayout() + form_layout.addRow("Nr:", self.nr_spin) + form_layout.addRow("Opis:", self.opis_edit) + form_layout.addRow("Punkty:", self.punkty_spin) + form_layout.addRow(self.obow_checkbox) + + # Przyciski OK/Cancel + self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, parent=self) + self.button_box.accepted.connect(self.accept) + self.button_box.rejected.connect(self.reject) + + # Główny layout + main_layout = QVBoxLayout() + main_layout.addLayout(form_layout) + main_layout.addWidget(self.button_box) + + self.setLayout(main_layout) + + # # Podłączamy sygnał itemClicked do własnej metody + # self.itemClicked.connect(self.handle_item_click) + + # def handle_item_click(self, item, column): + # """ + # Kopiuje zawartość kolumny 3 (opis) do schowka, + # gdy użytkownik kliknie wiersz w drzewie. + # """ + # opis = item.text(3) # kolumna 3 + # QApplication.clipboard().setText(opis) + # print(f"Skopiowano do schowka opis: {opis}") + + def get_data(self) -> dict: + """ + Zwraca słownik z parametrami: + {"nr": int, "opis": str, "punkty": int, "obowiązkowe": bool} + """ + return { + "nr": self.nr_spin.value(), + "opis": self.opis_edit.text().strip(), + "punkty": self.punkty_spin.value(), + "obowiązkowe": self.obow_checkbox.isChecked() + } + + +class DropTreeWidget(QTreeWidget): + """ + Drzewo docelowe (drop target). Przyjmuje JSON via drag&drop z DragTreeWidget, + umożliwia Undo/Redo, kasowanie, itd. + """ + def __init__(self, undo_stack=None, parent=None): + super().__init__(parent) + self.undo_stack = undo_stack + self.setAcceptDrops(True) + self.setDropIndicatorShown(True) + self.setDragDropMode(QAbstractItemView.DropOnly) + self.setSelectionMode(QAbstractItemView.ExtendedSelection) + + self.drop_event_in_progress = False + + + def contextMenuEvent(self, event): + """Wywołane przy kliknięciu prawym przyciskiem myszy (menu kontekstowe).""" + menu = QMenu(self) + + add_act = menu.addAction("Dodaj węzeł") + + # Możesz dodać więcej akcji, np. "Usuń zaznaczone", ale akurat mamy to na Backspace. + # add_delete_act = menu.addAction("Usuń zaznaczone") + + chosen_action = menu.exec(self.mapToGlobal(event.pos())) + if chosen_action == add_act: + self.add_item_interactive() + # elif chosen_action == add_delete_act: + # self.delete_selected_items() + + def add_item_interactive(self): + """ + Wywoływane po wybraniu opcji "Dodaj węzeł" z menu kontekstowego. + Otwiera dialog, a następnie tworzy QTreeWidgetItem i wstawia do drzewa + z użyciem AddItemCommand (undo/redo). + """ + from PySide6.QtWidgets import QTreeWidgetItem + from commands import AddItemCommand + from utils.helpers import display_number_or_dash, display_obligatory + + dialog = AddItemDialog(self) + if dialog.exec() == QDialog.Accepted: + data = dialog.get_data() + # data ma postać: {"nr": int, "opis": str, "punkty": int, "obowiązkowe": bool} + + # Wyznaczamy, gdzie wstawić nowy węzeł: + selected_items = self.selectedItems() + if selected_items: + parent_item = selected_items[0] + index = parent_item.childCount() + else: + parent_item = None + index = self.topLevelItemCount() + + # Konwersja wartości do stringów (uwzględniając -1 => '-') + punkty_str = display_number_or_dash(data["punkty"]) + + # Dla bool w polu obowiązkowe -> 1 (tak) lub 0 (nie) + obow_val = 1 if data["obowiązkowe"] else 0 + obow_str = display_obligatory(obow_val) + + nr_str = display_number_or_dash(data["nr"]) + opis_str = data["opis"] + + new_item = QTreeWidgetItem([ + punkty_str, # kolumna 0 -> punkty + obow_str, # kolumna 1 -> obowiązkowe + nr_str, # kolumna 2 -> nr + opis_str # kolumna 3 -> opis + ]) + + # Kolorowanie, jeśli używasz color_item_by_title: + from utils.colors import color_item_by_title + color_item_by_title(new_item) + + # Undo/Redo – tworzymy komendę i wrzucamy na stos + cmd = AddItemCommand( + tree=self, + parent_item=parent_item, + index=index, + cloned_item=new_item, + description="Add new item" + ) + + if self.undo_stack: + self.undo_stack.push(cmd) + else: + cmd.redo() + + + # def add_item_interactive(self): + # """ + # Wywoływane po wybraniu opcji "Dodaj węzeł" z menu kontekstowego. + # Otwiera dialog, a następnie tworzy QTreeWidgetItem i wstawia do drzewa + # z użyciem AddItemCommand (undo/redo). + # """ + # from PySide6.QtWidgets import QTreeWidgetItem + # from commands import AddItemCommand + # from utils.helpers import display_number_or_dash, display_obligatory + + # dialog = AddItemDialog(self) + # if dialog.exec() == QDialog.Accepted: + # data = dialog.get_data() + # # data ma postać: {"nr": int, "opis": str, "punkty": int, "obowiązkowe": bool} + + # # Wyznaczamy, gdzie wstawić nowy węzeł: + # selected_items = self.selectedItems() + # if selected_items: + # parent_item = selected_items[0] + # index = parent_item.childCount() + # else: + # parent_item = None + # index = self.topLevelItemCount() + + # # Konwersja wartości do stringów (uwzględniając -1 => '-') + # punkty_str = display_number_or_dash(data["punkty"]) + + # # Dla bool w polu obowiązkowe -> 1 (tak) lub 0 (nie) + # obow_val = 1 if data["obowiązkowe"] else 0 + # obow_str = display_obligatory(obow_val) + + # nr_str = display_number_or_dash(data["nr"]) + # opis_str = data["opis"] + + # new_item = QTreeWidgetItem([ + # punkty_str, # kolumna 0 -> punkty + # obow_str, # kolumna 1 -> obowiązkowe + # nr_str, # kolumna 2 -> nr + # opis_str # kolumna 3 -> opis + # ]) + + # # Kolorowanie, jeśli używasz color_item_by_title: + # from utils.colors import color_item_by_title + # color_item_by_title(new_item) + + # # Undo/Redo – tworzymy komendę i wrzucamy na stos + # cmd = AddItemCommand( + # tree=self, + # parent_item=parent_item, + # index=index, + # cloned_item=new_item, + # description="Add new item" + # ) + + # if self.undo_stack: + # self.undo_stack.push(cmd) + # else: + # cmd.redo() + + # ------------------------------------- + # Obsługa klawiatury (Backspace, Ctrl+T) + # ------------------------------------- + def keyPressEvent(self, event): + if event.key() == Qt.Key_Backspace: + self.delete_selected_items() + event.accept() + elif event.modifiers() & Qt.ControlModifier and event.key() == Qt.Key_T: + self.toggle_expand_selected_items() + event.accept() + else: + super().keyPressEvent(event) + + def toggle_expand_selected_items(self): + """Przełącza rozwinięcie/zwinięcie dla zaznaczonych elementów i ich dzieci.""" + selected_items = self.selectedItems() + for item in selected_items: + self.toggle_expand_item_and_children(item) + + def toggle_expand_item_and_children(self, item: QTreeWidgetItem): + if item.isExpanded(): + self.collapseItem(item) + else: + self.expandItem(item) + for i in range(item.childCount()): + self.toggle_expand_item_and_children(item.child(i)) + + # ------------------------------------- + # Obsługa myszy (zaznaczenie) + # ------------------------------------- + def mousePressEvent(self, event: QMouseEvent): + item = self.itemAt(event.position().toPoint()) + if item: + if event.modifiers() & Qt.ControlModifier: # Ctrl+klik + item.setSelected(not item.isSelected()) + else: + super().mousePressEvent(event) + else: + super().mousePressEvent(event) + + def delete_selected_items(self): + """Usunięcie zaznaczonych elementów (Undo/Redo).""" + selected = self.selectedItems() + for item in reversed(selected): + cmd = RemoveItemCommand(self, item) + self.undo_stack.push(cmd) + + # ------------------------------------- + # Obsługa drag&drop + # ------------------------------------- + def dragEnterEvent(self, event): + if event.mimeData().hasText(): + event.acceptProposedAction() + else: + event.ignore() + + def dragMoveEvent(self, event): + if event.mimeData().hasText(): + event.acceptProposedAction() + else: + event.ignore() + + def dropEvent(self, event): + if self.drop_event_in_progress: + event.ignore() + return + + self.drop_event_in_progress = True + try: + pos = event.position().toPoint() + target_item = self.itemAt(pos) + + try: + data_list = json.loads(event.mimeData().text()) + except json.JSONDecodeError: + event.ignore() + return + + for node_data in data_list: + self.add_subtree_recursively(target_item, node_data) + + apply_indentation_to_all_columns(self) + + event.acceptProposedAction() + finally: + self.drop_event_in_progress = False + + # ------------------------------------- + # Logika dodawania / scalania węzłów + # ------------------------------------- + def add_subtree_recursively(self, target_parent: Optional[QTreeWidgetItem], node_data: dict): + """ + Dodaje/scala hierarchię, ignorując zduplikowanych rodziców + na podstawie (nr, opis). + """ + existing_parent = self.find_matching_parent(target_parent, node_data) + + if existing_parent: + # Jeżeli już istnieje taki węzeł, scal dzieci + self.merge_all_children(existing_parent, node_data.get("children", [])) + else: + # Tworzymy nowy element + new_parent = QTreeWidgetItem([ + node_data.get("punkty", "0"), + node_data.get("obowiązkowe", "nie"), + node_data.get("nr", ""), + node_data.get("opis", "") + ]) + color_item_by_title(new_parent) + + if target_parent: + target_parent.addChild(new_parent) + else: + self.addTopLevelItem(new_parent) + + # Dodaj dzieci rekurencyjnie + self.merge_all_children(new_parent, node_data.get("children", [])) + + def find_matching_parent(self, target_parent: Optional[QTreeWidgetItem], node_data: dict) -> Optional[QTreeWidgetItem]: + """Znajduje dziecko w target_parent (lub top-level) pasujące (nr, opis).""" + return self.find_child_by_attributes(target_parent, node_data.get("nr", ""), node_data.get("opis", "")) + + def merge_all_children(self, parent_item: QTreeWidgetItem, children_data: list): + """Scal wszystkie dzieci z danymi JSON.""" + for child_data in children_data: + existing_child = self.find_child_by_attributes(parent_item, child_data.get("nr", ""), child_data.get("opis", "")) + + if existing_child: + # Jeżeli już istnieje, rekurencyjnie scal + self.merge_all_children(existing_child, child_data.get("children", [])) + else: + new_child = QTreeWidgetItem([ + child_data.get("punkty", "0"), + child_data.get("obowiązkowe", "nie"), + child_data.get("nr", ""), + child_data.get("opis", "") + ]) + color_item_by_title(new_child) + parent_item.addChild(new_child) + self.merge_all_children(new_child, child_data.get("children", [])) + + # Ewentualnie można tu sortować po numerach + # self.sort_children_by_nr(parent_item) + + def find_child_by_attributes(self, parent_item: Optional[QTreeWidgetItem], nr: str, opis: str) -> Optional[QTreeWidgetItem]: + """Wyszukuje dziecko o danych atrybutach (nr, opis).""" + if parent_item: + for i in range(parent_item.childCount()): + child = parent_item.child(i) + if child.text(2).strip() == nr.strip() and child.text(3).strip() == opis.strip(): + return child + else: + for i in range(self.topLevelItemCount()): + item = self.topLevelItem(i) + if item.text(2).strip() == nr.strip() and item.text(3).strip() == opis.strip(): + return item + return None + diff --git a/widgets/drop_tree_widget.py-kk b/widgets/drop_tree_widget.py-kk new file mode 100644 index 0000000..e5a7163 --- /dev/null +++ b/widgets/drop_tree_widget.py-kk @@ -0,0 +1,441 @@ +import json +from typing import Optional + +from PySide6.QtWidgets import ( + QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMessageBox, QMenu, + QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QCheckBox, + QPushButton, QSpinBox, QDialogButtonBox, QFormLayout, QPlainTextEdit +) +from PySide6.QtCore import Qt, QPoint +from PySide6.QtGui import QMouseEvent + +# Import komend (Undo/Redo) +from commands import AddItemCommand, RemoveItemCommand + +# Import narzędzi kolorowania i wcięć +from utils.colors import color_item_by_title +from utils.indentation import apply_indentation_to_all_columns + + +############################################################################### +# AddItemDialog # +############################################################################### +class AddItemDialog(QDialog): + """ + Proste okno dialogowe, pozwalające wprowadzić: + - nr (int) + - opis (string) + - punkty (int, np. -1, 0, 5) + - obowiązkowe (checkbox -> True/False) + """ + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowTitle("Dodaj nowy węzeł") + + # Pola formularza + self.nr_spin = QSpinBox() + self.nr_spin.setRange(-9999, 9999) + self.nr_spin.setValue(1) + + self.opis_edit = QLineEdit() + self.opis_edit.setPlaceholderText("np. Sprawność rachunkowa...") + + self.punkty_spin = QSpinBox() + self.punkty_spin.setRange(-9999, 9999) + self.punkty_spin.setValue(-1) + + self.obow_checkbox = QCheckBox("Obowiązkowe?") + + # Układ typu FormLayout + form_layout = QFormLayout() + form_layout.addRow("Nr:", self.nr_spin) + form_layout.addRow("Opis:", self.opis_edit) + form_layout.addRow("Punkty:", self.punkty_spin) + form_layout.addRow(self.obow_checkbox) + + # Przyciski OK/Cancel + self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, parent=self) + self.button_box.accepted.connect(self.accept) + self.button_box.rejected.connect(self.reject) + + # Główny layout + main_layout = QVBoxLayout() + main_layout.addLayout(form_layout) + main_layout.addWidget(self.button_box) + + self.setLayout(main_layout) + + def get_data(self) -> dict: + """ + Zwraca słownik z parametrami: + {"nr": int, "opis": str, "punkty": int, "obowiązkowe": bool} + """ + return { + "nr": self.nr_spin.value(), + "opis": self.opis_edit.text().strip(), + "punkty": self.punkty_spin.value(), + "obowiązkowe": self.obow_checkbox.isChecked() + } + + +############################################################################### +# DropTreeWidget # +############################################################################### +class DropTreeWidget(QTreeWidget): + """ + Drzewo docelowe (drop target). Przyjmuje JSON via drag&drop z DragTreeWidget, + umożliwia Undo/Redo, kasowanie, itd. + """ + def __init__(self, undo_stack=None, parent=None): + super().__init__(parent) + self.undo_stack = undo_stack + self.setAcceptDrops(True) + self.setDropIndicatorShown(True) + self.setDragDropMode(QAbstractItemView.DropOnly) + self.setSelectionMode(QAbstractItemView.ExtendedSelection) + + self.drop_event_in_progress = False + # 1) Podłączanie sygnału itemClicked + self.itemClicked.connect(self.handle_item_click) + + def handle_item_click(self, item, column): + """ + Po kliknięciu w węzeł kopiuje do schowka JSON zawierający + ścieżkę (rodzice) od korzenia do tego węzła – ale BEZ dzieci! + """ + chain_json = self.item_to_json_parents_no_children(item) + print("[LOG] Copied item + parents (NO children) to clipboard:") + print(chain_json) + + from PySide6.QtWidgets import QApplication + QApplication.clipboard().setText(chain_json) + + def item_to_json_parents_no_children(self, item: QTreeWidgetItem) -> str: + """ + Buduje strukturę JSON (jako string) dla pojedynczej ścieżki + 'korzeń -> ... -> item', bez dzieci. Każdy węzeł ma 'children': []. + """ + import json + + # Zacznij od zaznaczonego węzła + node_dict = self.item_to_dict_no_children(item) + + # Idź w górę, owijając node_dict w kolejnych rodziców + current = item.parent() + while current is not None: + parent_dict = self.item_to_dict_no_children(current) + parent_dict["children"] = [node_dict] + node_dict = parent_dict + current = current.parent() + + # Zamień na ładny, sformatowany JSON + return json.dumps(node_dict, ensure_ascii=False, indent=2) + + def item_to_dict_no_children(self, item: QTreeWidgetItem) -> dict: + """ + Zwraca słownik z polami (punkty, obowiązkowe, nr, opis), + ale 'children' zawsze jest pustą listą. + """ + return { + "punkty": item.text(0), + "obowiązkowe": item.text(1), + "nr": item.text(2), + "opis": item.text(3), + "children": [] + } + + + # ---------------------------------------------------------------- + # KONTEKSTOWE MENU (PRAWY KLIK) + # ---------------------------------------------------------------- + def contextMenuEvent(self, event): + """Wywołane przy kliknięciu prawym przyciskiem myszy (menu kontekstowe).""" + menu = QMenu(self) + + add_act = menu.addAction("Dodaj węzeł") + paste_act = menu.addAction("Wklej JSON Subtree") + + chosen_action = menu.exec(self.mapToGlobal(event.pos())) + if chosen_action == add_act: + self.add_item_interactive() + elif chosen_action == paste_act: + self.paste_json_subtree() + + def add_item_interactive(self): + """ + Wywoływane po wybraniu opcji "Dodaj węzeł" z menu kontekstowego. + Otwiera dialog, a następnie tworzy QTreeWidgetItem i wstawia do drzewa + z użyciem AddItemCommand (undo/redo). + """ + from PySide6.QtWidgets import QTreeWidgetItem + from commands import AddItemCommand + from utils.helpers import display_number_or_dash, display_obligatory + + dialog = AddItemDialog(self) + if dialog.exec() == QDialog.Accepted: + data = dialog.get_data() + # data ma postać: {"nr": int, "opis": str, "punkty": int, "obowiązkowe": bool} + + # Wyznaczamy, gdzie wstawić nowy węzeł: + selected_items = self.selectedItems() + if selected_items: + parent_item = selected_items[0] + index = parent_item.childCount() + else: + parent_item = None + index = self.topLevelItemCount() + + # Konwersja wartości do stringów (uwzględniając -1 => '-') + punkty_str = display_number_or_dash(data["punkty"]) + + # Dla bool w polu obowiązkowe -> 1 (tak) lub 0 (nie) + obow_val = 1 if data["obowiązkowe"] else 0 + obow_str = display_obligatory(obow_val) + + nr_str = display_number_or_dash(data["nr"]) + opis_str = data["opis"] + + new_item = QTreeWidgetItem([ + punkty_str, # kolumna 0 -> punkty + obow_str, # kolumna 1 -> obowiązkowe + nr_str, # kolumna 2 -> nr + opis_str # kolumna 3 -> opis + ]) + + # Kolorowanie, jeśli używasz color_item_by_title: + from utils.colors import color_item_by_title + color_item_by_title(new_item) + + # Undo/Redo – tworzymy komendę i wrzucamy na stos + cmd = AddItemCommand( + tree=self, + parent_item=parent_item, + index=index, + cloned_item=new_item, + description="Add new item" + ) + + if self.undo_stack: + self.undo_stack.push(cmd) + else: + cmd.redo() + + # ---------------------------------------------------------------- + # NOWA OPCJA: WKLEJANIE SUBTREE Z JSON + # ---------------------------------------------------------------- + def paste_json_subtree(self): + """ + Otwiera dialog, w którym użytkownik wkleja JSON w formie listy/dict. + Następnie zawartość jest dodawana rekurencyjnie do zaznaczonego węzła + (lub top-level, jeśli brak zaznaczenia). + """ + import json + from PySide6.QtWidgets import QDialog, QVBoxLayout, QDialogButtonBox, QPlainTextEdit + + class JsonPasteDialog(QDialog): + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowTitle("Wklej JSON Subtree") + + self.edit = QPlainTextEdit(self) + self.edit.setPlaceholderText("Wklej tutaj JSON...") + + btn_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + btn_box.accepted.connect(self.accept) + btn_box.rejected.connect(self.reject) + + layout = QVBoxLayout() + layout.addWidget(self.edit) + layout.addWidget(btn_box) + self.setLayout(layout) + + dlg = JsonPasteDialog(self) + if dlg.exec() == QDialog.Accepted: + text = dlg.edit.toPlainText().strip() + if not text: + QMessageBox.information(self, "Brak danych", "Nie wklejono żadnego JSON.") + return + try: + data_list = json.loads(text) + # Jeśli ktoś wklei pojedyńczy obiekt (dict), + # to obłóżmy go w listę, żeby iterować tak samo: + if isinstance(data_list, dict): + data_list = [data_list] + except json.JSONDecodeError as e: + QMessageBox.warning(self, "Błąd JSON", f"Niepoprawny JSON:\n{e}") + return + + # Wybieramy, gdzie wklejamy subtree + selected = self.selectedItems() + if selected: + parent_item = selected[0] + else: + parent_item = None + + # Używamy istniejącej logiki add_subtree_recursively + for node_data in data_list: + self.add_subtree_recursively(parent_item, node_data) + + # Upiększamy wcięcia + apply_indentation_to_all_columns(self) + + QMessageBox.information(self, "OK", "Dodano subtree z JSON.") + + + # ------------------------------------- + # Obsługa klawiatury (Backspace, Ctrl+T) + # ------------------------------------- + def keyPressEvent(self, event): + if event.key() == Qt.Key_Backspace: + self.delete_selected_items() + event.accept() + elif event.modifiers() & Qt.ControlModifier and event.key() == Qt.Key_T: + self.toggle_expand_selected_items() + event.accept() + else: + super().keyPressEvent(event) + + def delete_selected_items(self): + """Usunięcie zaznaczonych elementów (Undo/Redo).""" + selected = self.selectedItems() + for item in reversed(selected): + cmd = RemoveItemCommand(self, item) + if self.undo_stack: + self.undo_stack.push(cmd) + else: + cmd.redo() + + def toggle_expand_selected_items(self): + """Przełącza rozwinięcie/zwinięcie dla zaznaczonych elementów i ich dzieci.""" + selected_items = self.selectedItems() + for item in selected_items: + self.toggle_expand_item_and_children(item) + + def toggle_expand_item_and_children(self, item: QTreeWidgetItem): + if item.isExpanded(): + self.collapseItem(item) + else: + self.expandItem(item) + for i in range(item.childCount()): + self.toggle_expand_item_and_children(item.child(i)) + + # ------------------------------------- + # Obsługa myszy (zaznaczenie) + # ------------------------------------- + def mousePressEvent(self, event: QMouseEvent): + item = self.itemAt(event.position().toPoint()) + if item: + if event.modifiers() & Qt.ControlModifier: # Ctrl+klik + item.setSelected(not item.isSelected()) + else: + super().mousePressEvent(event) + else: + super().mousePressEvent(event) + + # ------------------------------------- + # DRAG & DROP + # ------------------------------------- + def dragEnterEvent(self, event): + if event.mimeData().hasText(): + event.acceptProposedAction() + else: + event.ignore() + + def dragMoveEvent(self, event): + if event.mimeData().hasText(): + event.acceptProposedAction() + else: + event.ignore() + + def dropEvent(self, event): + if self.drop_event_in_progress: + event.ignore() + return + + self.drop_event_in_progress = True + try: + pos = event.position().toPoint() + target_item = self.itemAt(pos) + + try: + data_list = json.loads(event.mimeData().text()) + except json.JSONDecodeError: + event.ignore() + return + + for node_data in data_list: + self.add_subtree_recursively(target_item, node_data) + + apply_indentation_to_all_columns(self) + + event.acceptProposedAction() + finally: + self.drop_event_in_progress = False + + # ------------------------------------- + # Logika dodawania / scalania węzłów + # ------------------------------------- + def add_subtree_recursively(self, target_parent: Optional[QTreeWidgetItem], node_data: dict): + """ + Dodaje/scala hierarchię, ignorując zduplikowanych rodziców + na podstawie (nr, opis). + """ + existing_parent = self.find_matching_parent(target_parent, node_data) + + if existing_parent: + # Jeżeli już istnieje taki węzeł, scal dzieci + self.merge_all_children(existing_parent, node_data.get("children", [])) + else: + # Tworzymy nowy element + new_parent = QTreeWidgetItem([ + node_data.get("punkty", "0"), + node_data.get("obowiązkowe", "nie"), + node_data.get("nr", ""), + node_data.get("opis", "") + ]) + color_item_by_title(new_parent) + + if target_parent: + target_parent.addChild(new_parent) + else: + self.addTopLevelItem(new_parent) + + # Dodaj dzieci rekurencyjnie + self.merge_all_children(new_parent, node_data.get("children", [])) + + def find_matching_parent(self, target_parent: Optional[QTreeWidgetItem], node_data: dict) -> Optional[QTreeWidgetItem]: + """Znajduje dziecko w target_parent (lub top-level) pasujące (nr, opis).""" + return self.find_child_by_attributes(target_parent, node_data.get("nr", ""), node_data.get("opis", "")) + + def merge_all_children(self, parent_item: QTreeWidgetItem, children_data: list): + """Scal wszystkie dzieci z danymi JSON.""" + for child_data in children_data: + existing_child = self.find_child_by_attributes(parent_item, child_data.get("nr", ""), child_data.get("opis", "")) + + if existing_child: + # Jeżeli już istnieje, rekurencyjnie scal + self.merge_all_children(existing_child, child_data.get("children", [])) + else: + new_child = QTreeWidgetItem([ + child_data.get("punkty", "0"), + child_data.get("obowiązkowe", "nie"), + child_data.get("nr", ""), + child_data.get("opis", "") + ]) + color_item_by_title(new_child) + parent_item.addChild(new_child) + self.merge_all_children(new_child, child_data.get("children", [])) + + def find_child_by_attributes(self, parent_item: Optional[QTreeWidgetItem], nr: str, opis: str) -> Optional[QTreeWidgetItem]: + """Wyszukuje dziecko o danych atrybutach (nr, opis).""" + if parent_item: + for i in range(parent_item.childCount()): + child = parent_item.child(i) + if child.text(2).strip() == nr.strip() and child.text(3).strip() == opis.strip(): + return child + else: + for i in range(self.topLevelItemCount()): + item = self.topLevelItem(i) + if item.text(2).strip() == nr.strip() and item.text(3).strip() == opis.strip(): + return item + return None