diff --git a/C++/.vscode/settings.json b/C++/.vscode/settings.json deleted file mode 100644 index 318dce3..0000000 --- a/C++/.vscode/settings.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "files.associations": { - "algorithm": "cpp", - "iostream": "cpp", - "numeric": "cpp", - "array": "cpp", - "atomic": "cpp", - "bit": "cpp", - "*.tcc": "cpp", - "cctype": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "compare": "cpp", - "concepts": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "string": "cpp", - "unordered_map": "cpp", - "vector": "cpp", - "exception": "cpp", - "functional": "cpp", - "iterator": "cpp", - "memory": "cpp", - "memory_resource": "cpp", - "random": "cpp", - "string_view": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "utility": "cpp", - "initializer_list": "cpp", - "iosfwd": "cpp", - "istream": "cpp", - "limits": "cpp", - "new": "cpp", - "numbers": "cpp", - "ostream": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "typeinfo": "cpp", - "ctime": "cpp", - "cstring": "cpp" - } -} \ No newline at end of file diff --git a/C++/C++.md b/C++/C++.md index f2639a4..6b3018c 100644 --- a/C++/C++.md +++ b/C++/C++.md @@ -545,3 +545,243 @@ void boo(int* ptr) { ## Структуры > Структура - тип данных, позоляющий объединить несколько переменных в одной еденице. + +```C++ +#include + +struct Employee { + short id; + int age; + double salary; +}; + +Employee john = {5, 20, 30.0}; +std::cout << john.id << ' ' << john.age << ' ' << john.salary << '\n'; +``` + +## Перечисления + +> Перечисление - это пользовательский тип данных, который представляет собой набор именованных констант, которым присваиваются целочисленные значения. Весит как *int* + +```C++ +#include + +enum Weekday { + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + Sunday, +}; + +enum Weekday2 { + Monday = 1, + Tuesday = 2, + Wednesday = 3, + Thursday = 4, + Friday = 5, + Saturday = 6, + Sunday = 7, +}; + +Weekday today = Saturday; +``` + +Weekday полностью аналогичен Weekday2 + +## Объединение + +> Объединение - это пользовательский тип данных, позволяющий хранить различные типы данных в одной области памяти. Объединение занимает память, достаточную для хранения переменной имеющий самый большой размер. + +```C++ +union MyUnion{ + int intValue; + char charValue; +}; + +MyUnion val = 65; +int a = val.intValue; // 65 +char b = val.charValue; // 'A' +``` + +## Передача значений в функцию (Подробнее) + +* Передача по значению (создаётся копия, может быть медленно, используется для примитивов) + + ```C++ + void increment(int a) { + a++; + } + + int a = 5; + increment(&a); // a остаётся 5 + ``` + +* Передача по ссылке (копия не создаётся, используется для больших значений / структур, аналогична передаче по значению, но синтаксис проще) + + ```C++ + void increment(int& a) { + a++; + } + + int a = 5; + increment(a); // a становится 6 + ``` + +* Передача по указателю (копия не создаётся, используется в основном для массивов) + + ```C++ + void increment(int* a) { + (*a)++; + } + + int a = 5; + increment(&a); // a тоже становится 6 + ``` + +## Классы + +> Классы - шаблон или описание, определяющее структуру поведения объектов. +> Объект - экземляр класса + +```С++ +class ClassName{ +// Поля и методы класса +}; +``` + +> Поля класса - переменные, котроые представляют состояие объетогв класса +> Методы класа - функции, которые определяют поведение объектов класса + +```С++ +ClassName objectName; // создание объекта класса просто + +ClassName objectName = new ClassName(); //создание объекта класса сложно +delete objectName; +``` + +## Пример + +```С++ +#include +#include + +class Person{ +public: +std::string name; +int age; + +void displayInfo(){ + std::count << "Name: " << name << ", Age: " << age << '\n'; +} +} + +int main(){ +Person person1; +person1.name = "Alice"; +person1.age = 25; +person1.displayInfo(); + +Person* person2 = new Person(); +person2 -> name = "Bob"; +person2 -> age = 30; +person2 -> displayInfo(); + +delete person2; +return 0; +} +``` + +> Пользовательский тип данных + +### Модификаторы доступа + +* public - доступно всем (по умолчанию в struct) +* private - доступно только внутри класса (по умолчанию в class) +* protected - достпуно внутри класса и наследникам + +### Конструктор и деструктор + +> Конструктор - функция, которая вызывается при инициализации класса (может быть несколько) +> Деструктор - функция, которая вызывается при уничтожении объекта + +```C++ +#include + +class MyClass { +public: + MyClass() { + std::cout << "Constructed\n"; + } + + MyClass(int val) { + std::cout << "Constucted" << val << '\n';f + } + + ~MyClass() { + std::cout << "Destroyed\n"; + } +}; +``` + +### this + +> this - указатель на объект вызвовший метод + +```C++ +#include + +class Foo{ +public: + Person(std::string val) { + this->name = val; + } +}; +``` + +## ООП + +> Инкапсуляция - данные класса скрыты от прямого доступа и манипулиций извне класса +> Наследование - создаёт классы-потомки +> Полимарфизм - разные классы используют один и тот же интерфейс + +```C++ +#include + +class Figure { +public: + virtual void draw() { + std::cout << "Draw figure\n"; + } +}; + +class Circe: public Figure { +public: + void draw() override { + std::cout << "Draw circle\n"; + } +}; + +class Rectangle: public Figure { +public: + void draw() override { + std::cout << "Draw rectangle\n"; + } +}; + +int main() { + Figure* figure = new Figure(); + Figure* circle = new Circle(): + Figure* rectangle = new Rectangle(); + + figure->draw(); + circle->draw(); + rectangle->draw(); + + delete figure; + delete circle; + delete rectangle; +} +``` diff --git a/C++/a.out b/C++/a.out deleted file mode 100755 index 319eb96..0000000 Binary files a/C++/a.out and /dev/null differ diff --git a/C++/lesson6/task1.cpp b/C++/lesson6/task1.cpp new file mode 100644 index 0000000..34d733a --- /dev/null +++ b/C++/lesson6/task1.cpp @@ -0,0 +1,16 @@ +#include +#include + +class Person { + private: + std::string name; + + public: + Person(const std::string& name) { + this->name = name; + } + + void printName() { + std::cout << this->name << '\n'; + } +}; \ No newline at end of file diff --git a/C++/lesson6/task2.cpp b/C++/lesson6/task2.cpp new file mode 100644 index 0000000..cefbb82 --- /dev/null +++ b/C++/lesson6/task2.cpp @@ -0,0 +1,47 @@ +#include +#include + +class Student { + std::string name; + int age; + double average_grade; + + public: + Student(std::string name, int age, double average) { + this->name = name; + this->age = age; + this->average_grade = average; + } + + std::string& get_name() { + return name; + } + + int get_age() { + return age; + } + + double get_average_grade() { + return average_grade; + } + + void status() { + if (average_grade >= 4 && average_grade < 5) { + std::cout << "Student is хорошист" << '\n'; + } else if (average_grade = 5) { + std::cout << "Student is отличник" << '\n'; + } else if (average_grade < 3) { + std::cout << "Student is двоечник" << '\n'; + } + } +}; + +int main() { + Student student1("George", 19, 3.5); + Student student2("Paul", 14, 4.8); + Student student3("Vasyok", 17, 5); + + student1.status(); + student2.status(); + student3.status(); +} \ No newline at end of file diff --git a/C++/lesson6/task3.cpp b/C++/lesson6/task3.cpp new file mode 100644 index 0000000..e973e7c --- /dev/null +++ b/C++/lesson6/task3.cpp @@ -0,0 +1,40 @@ +#include + +struct Account { + int number; + int money; + int percentage; + + public: + Account(int number, int money, int percentage) { + this->number = number; + this->money = money; + this->percentage = percentage; + } + + void refill(int amount) { + money += amount; + } + + void withdraw(int amount) { + money -= amount; + } + + void PrintInformation() { + std::cout << number << ' ' << money << ' ' << percentage << '\n'; + } +}; + +int main() { + Account* account1 = new Account(21213, 500, 15); + account1->PrintInformation(); + account1->refill(200); + account1->withdraw(50); + account1->PrintInformation(); + + Account* account2 = new Account(1934, 800, 10); + account2->PrintInformation(); + account2->refill(150); + account2->withdraw(23); + account2->PrintInformation(); +} diff --git a/Python/clock/main.py b/Python/clock/main.py new file mode 100755 index 0000000..9f4cf21 --- /dev/null +++ b/Python/clock/main.py @@ -0,0 +1,37 @@ +import sys +from time import strftime + +from PyQt6.QtCore import QTimer +from PyQt6.QtWidgets import QApplication, QLCDNumber, QVBoxLayout, QWidget + + +class Window(QWidget): + def __init__(self): + super().__init__() + self.setGeometry(100, 100, 800, 400) + self.setWindowTitle("Clock") + vbox = QVBoxLayout() + + self.timer = QTimer() + self.timer.timeout.connect(self.update_clock) + self.timer.start(200) + + self.lcd = QLCDNumber() + vbox.addWidget(self.lcd) + self.setLayout(vbox) + + def update_clock(self) -> None: + time_ = strftime("%H:%M:%S") + self.lcd.setDigitCount(8) + self.lcd.display(time_) + + +def main(): + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/Python/pyqt6_4/cat1.jpg b/Python/pyqt6_4/cat1.jpg new file mode 100755 index 0000000..8868123 Binary files /dev/null and b/Python/pyqt6_4/cat1.jpg differ diff --git a/Python/pyqt6_4/cat2.jpg b/Python/pyqt6_4/cat2.jpg new file mode 100755 index 0000000..ae1d0d1 Binary files /dev/null and b/Python/pyqt6_4/cat2.jpg differ diff --git a/Python/pyqt6_4/snippets/1.py b/Python/pyqt6_4/snippets/1.py new file mode 100755 index 0000000..9e3701e --- /dev/null +++ b/Python/pyqt6_4/snippets/1.py @@ -0,0 +1,21 @@ +import sys + +from PyQt6.QtWidgets import QApplication, QWidget + + +class Window(QWidget): + def __init__(self) -> None: + super().__init__() + self.setGeometry(*(500,) * 4) + self.setWindowTitle("Окно") + + +def main() -> None: + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/Python/pyqt6_4/snippets/10.py b/Python/pyqt6_4/snippets/10.py new file mode 100755 index 0000000..093f10e --- /dev/null +++ b/Python/pyqt6_4/snippets/10.py @@ -0,0 +1,61 @@ +import sys + +from PyQt6.QtWidgets import ( + QApplication, + QWidget, + QHBoxLayout, + QSpinBox, + QLabel, + QLineEdit, +) +from PyQt6.QtGui import QFont + + +class Window(QWidget): + def __init__(self) -> None: + super().__init__() + self.setGeometry(*(500,) * 4) + self.setWindowTitle("Окно") + self.price = 300 + self.amount = 0 + + hbox = QHBoxLayout() + + self.line_edit = QLineEdit(str(self.price)) + self.line_edit.setFont(QFont("Times", 14)) + + self.spin_box = QSpinBox() + self.spin_box.valueChanged.connect(self.handle_spin_box) + self.result = QLabel(str(self.total)) + + hbox.addWidget(self.line_edit) + hbox.addWidget(self.spin_box) + hbox.addWidget(self.result) + + self.setLayout(hbox) + + def handle_spin_box(self) -> None: + try: + self.price = int(self.line_edit.text()) + except ValueError: + return + self.amount = self.spin_box.value() + self.result.setText(str(self.total)) + + @property + def total(self) -> int: + return self.amount * self.price + + def handle_line_edit(self) -> None: + pass + + +def main() -> None: + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/Python/pyqt6_4/snippets/2.py b/Python/pyqt6_4/snippets/2.py new file mode 100755 index 0000000..8b51f43 --- /dev/null +++ b/Python/pyqt6_4/snippets/2.py @@ -0,0 +1,31 @@ +import sys + +from PyQt6.QtWidgets import QApplication, QWidget, QLabel +from PyQt6.QtGui import QPixmap + + +class Window(QWidget): + def __init__(self) -> None: + super().__init__() + self.setGeometry(*(500,) * 4) + self.setWindowTitle("Окно") + + label = QLabel(self) + label.setText("Текст") + label.move(300, 300) + + pixmap = QPixmap() + lable_pic = QLabel(self) + lable_pic.setPicture(pixmap) + lable_pic.move(200, 200) + + +def main() -> None: + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/Python/pyqt6_4/snippets/3.py b/Python/pyqt6_4/snippets/3.py new file mode 100755 index 0000000..1113809 --- /dev/null +++ b/Python/pyqt6_4/snippets/3.py @@ -0,0 +1,31 @@ +import sys + +from PyQt6.QtWidgets import QApplication, QWidget, QPushButton +from PyQt6.QtCore import QSize +from PyQt6.QtGui import QFont, QIcon + + +class Window(QWidget): + def __init__(self) -> None: + super().__init__() + self.setGeometry(*(500,) * 4) + self.setWindowTitle("Окно") + self.create_button() + + def create_button(self) -> None: + btn = QPushButton("Click", self) + btn.setGeometry(*(100,) * 4) + btn.setFont(QFont("Times", 14, QFont.Weight.ExtraBold)) + btn.setIcon(QIcon("cat1.jpg")) + btn.setIconSize(QSize(50, 50)) + + +def main() -> None: + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/Python/pyqt6_4/snippets/4.py b/Python/pyqt6_4/snippets/4.py new file mode 100755 index 0000000..f0d7b68 --- /dev/null +++ b/Python/pyqt6_4/snippets/4.py @@ -0,0 +1,27 @@ +import sys + +from PyQt6.QtGui import QFont +from PyQt6.QtWidgets import QApplication, QLineEdit, QWidget + + +class Window(QWidget): + def __init__(self) -> None: + super().__init__() + self.setGeometry(*(500,) * 4) + self.setWindowTitle("Окно") + + line_edit = QLineEdit(self) + line_edit.setFont(QFont("Times", 14)) + line_edit.setPlaceholderText("Enter your password") + line_edit.setEchoMode(QLineEdit.EchoMode.Password) + + +def main() -> None: + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/Python/pyqt6_4/snippets/5.py b/Python/pyqt6_4/snippets/5.py new file mode 100755 index 0000000..54e34a8 --- /dev/null +++ b/Python/pyqt6_4/snippets/5.py @@ -0,0 +1,32 @@ +import sys + +from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout + + +class Window(QWidget): + def __init__(self) -> None: + super().__init__() + self.setGeometry(*(500,) * 4) + self.setWindowTitle("Окно") + + btn1 = QPushButton("One") + btn2 = QPushButton("Two") + btn3 = QPushButton("Three") + + layout = QHBoxLayout() + layout.addWidget(btn1) + layout.addWidget(btn2) + layout.addWidget(btn3) + + self.setLayout(layout) + + +def main() -> None: + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/Python/pyqt6_4/snippets/6.py b/Python/pyqt6_4/snippets/6.py new file mode 100755 index 0000000..2c92687 --- /dev/null +++ b/Python/pyqt6_4/snippets/6.py @@ -0,0 +1,32 @@ +import sys + +from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout + + +class Window(QWidget): + def __init__(self) -> None: + super().__init__() + self.setGeometry(*(500,) * 4) + self.setWindowTitle("Окно") + + btn1 = QPushButton("One") + btn2 = QPushButton("Two") + btn3 = QPushButton("Three") + + layout = QVBoxLayout() + layout.addWidget(btn1) + layout.addWidget(btn2) + layout.addWidget(btn3) + + self.setLayout(layout) + + +def main() -> None: + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/Python/pyqt6_4/snippets/7.py b/Python/pyqt6_4/snippets/7.py new file mode 100755 index 0000000..740088b --- /dev/null +++ b/Python/pyqt6_4/snippets/7.py @@ -0,0 +1,63 @@ +import sys + +from PyQt6.QtCore import QSize +from PyQt6.QtGui import QIcon +from PyQt6.QtWidgets import ( + QApplication, + QHBoxLayout, + QLabel, + QRadioButton, + QVBoxLayout, + QWidget, +) + + +ICON_SIZE = 100 + + +class Window(QWidget): + def __init__(self) -> None: + super().__init__() + self.setGeometry(200, 200, 400, 300) + self.setWindowTitle("Окно") + self.create_radio() + + def create_radio(self) -> None: + hbox = QHBoxLayout() + + rad1 = QRadioButton("Cat 1") + rad1.setIcon(QIcon("cat1.jpg")) + rad1.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) + rad1.toggled.connect(self.radio_selected) + + rad2 = QRadioButton("Cat 2") + rad2.setIcon(QIcon("cat2.jpg")) + rad2.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) + rad2.toggled.connect(self.radio_selected) + + hbox.addWidget(rad1) + hbox.addWidget(rad2) + + vbox = QVBoxLayout() + + self.label = QLabel("") + + vbox.addWidget(self.label) + vbox.addLayout(hbox) + self.setLayout(vbox) + + def radio_selected(self) -> None: + radio_btn = self.sender() + if radio_btn.isEnabled(): + self.label.setText(f"You have chosen {radio_btn.text().lower()}") + + +def main() -> None: + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/Python/pyqt6_4/snippets/8.py b/Python/pyqt6_4/snippets/8.py new file mode 100755 index 0000000..97d3b11 --- /dev/null +++ b/Python/pyqt6_4/snippets/8.py @@ -0,0 +1,68 @@ +import sys + +from PyQt6.QtCore import QSize +from PyQt6.QtGui import QIcon +from PyQt6.QtWidgets import ( + QApplication, + QCheckBox, + QHBoxLayout, + QLabel, + QVBoxLayout, + QWidget, +) + +ICON_SIZE = 100 + + +class Window(QWidget): + def __init__(self) -> None: + super().__init__() + self.setGeometry(200, 200, 400, 300) + self.setWindowTitle("Окно") + self.create_radio() + + def create_radio(self) -> None: + hbox = QHBoxLayout() + + self.check1 = QCheckBox("Cat 1") + self.check1.setIcon(QIcon("cat1.jpg")) + self.check1.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) + self.check1.toggled.connect(self.radio_selected) + + self.check2 = QCheckBox("Cat 2") + self.check2.setIcon(QIcon("cat2.jpg")) + self.check2.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) + self.check2.toggled.connect(self.radio_selected) + + hbox.addWidget(self.check1) + hbox.addWidget(self.check2) + + vbox = QVBoxLayout() + + self.label = QLabel("You haven't chosen anything") + + vbox.addWidget(self.label) + vbox.addLayout(hbox) + self.setLayout(vbox) + + def radio_selected(self) -> None: + text = "You have chosen " + if self.check1.isChecked(): + text += self.check1.text().lower() + ", " + if self.check2.isChecked(): + text += self.check2.text().lower() + if not any((self.check1.isChecked(), self.check2.isChecked())): + text = "You haven't chosen anything" + text = text.removesuffix(", ") + self.label.setText(text) + + +def main() -> None: + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/Python/pyqt6_4/snippets/9.py b/Python/pyqt6_4/snippets/9.py new file mode 100755 index 0000000..18b707f --- /dev/null +++ b/Python/pyqt6_4/snippets/9.py @@ -0,0 +1,70 @@ +import sys +from enum import StrEnum + +from PyQt6.QtGui import QFont +from PyQt6.QtWidgets import ( + QApplication, + QComboBox, + QHBoxLayout, + QLabel, + QVBoxLayout, + QWidget, +) + + +class CatType(StrEnum): + ORANGE = "one orange brain cell" + VOID = "void" + DUST = "dust kitty" + + +class Window(QWidget): + def __init__(self) -> None: + super().__init__() + self.setGeometry(*(500,) * 4) + self.setWindowTitle("Окно") + self.current_type = CatType.ORANGE + self.create_combo() + + def create_combo(self) -> None: + hbox = QHBoxLayout() + + label = QLabel("Select cat type:") + label.setFont(QFont("Times", 15)) + + self.combo = QComboBox(self) + for cat_type in CatType: + self.combo.addItem(cat_type.capitalize()) + self.combo.currentTextChanged.connect(self.combo_changed) + + hbox.addWidget(label) + hbox.addWidget(self.combo) + + vbox = QVBoxLayout() + self.label_result = QLabel( + f"Current cat: {self.current_type.capitalize()}", + ) + + self.label_result.setFont(QFont("Times", 15)) + + vbox = QVBoxLayout() + vbox.addWidget(self.label_result) + vbox.addLayout(hbox) + self.setLayout(vbox) + + def combo_changed(self) -> None: + self.current_type = CatType(self.combo.currentText().lower()) + self.label_result.setText( + f"Current cat: {self.current_type.capitalize()}", + ) + + +def main() -> None: + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/Python/pyqt6_4/task1.py b/Python/pyqt6_4/task1.py new file mode 100755 index 0000000..e69de29 diff --git a/Python/pyqt6_4/test.py b/Python/pyqt6_4/test.py new file mode 100755 index 0000000..e69de29 diff --git a/Python/Python-test/task1.py b/Python/pyton_test/task1.py similarity index 100% rename from Python/Python-test/task1.py rename to Python/pyton_test/task1.py diff --git a/Python/Python-test/task10.py b/Python/pyton_test/task10.py similarity index 100% rename from Python/Python-test/task10.py rename to Python/pyton_test/task10.py diff --git a/Python/Python-test/task2.py b/Python/pyton_test/task2.py similarity index 100% rename from Python/Python-test/task2.py rename to Python/pyton_test/task2.py diff --git a/Python/Python-test/task3.py b/Python/pyton_test/task3.py similarity index 100% rename from Python/Python-test/task3.py rename to Python/pyton_test/task3.py diff --git a/Python/Python-test/task4.py b/Python/pyton_test/task4.py similarity index 100% rename from Python/Python-test/task4.py rename to Python/pyton_test/task4.py diff --git a/Python/Python-test/task5.py b/Python/pyton_test/task5.py similarity index 100% rename from Python/Python-test/task5.py rename to Python/pyton_test/task5.py diff --git a/Python/Python-test/task5.txt b/Python/pyton_test/task5.txt similarity index 100% rename from Python/Python-test/task5.txt rename to Python/pyton_test/task5.txt diff --git a/Python/Python-test/task6.py b/Python/pyton_test/task6.py similarity index 100% rename from Python/Python-test/task6.py rename to Python/pyton_test/task6.py diff --git a/Python/Python-test/task7.py b/Python/pyton_test/task7.py similarity index 100% rename from Python/Python-test/task7.py rename to Python/pyton_test/task7.py diff --git a/Python/Python-test/task8.py b/Python/pyton_test/task8.py similarity index 100% rename from Python/Python-test/task8.py rename to Python/pyton_test/task8.py diff --git a/Python/Python-test/task9.py b/Python/pyton_test/task9.py similarity index 100% rename from Python/Python-test/task9.py rename to Python/pyton_test/task9.py diff --git a/Python/soper/flag.jpg b/Python/soper/flag.jpg new file mode 100755 index 0000000..9d815e6 Binary files /dev/null and b/Python/soper/flag.jpg differ diff --git a/Python/soper/soper.py b/Python/soper/soper.py new file mode 100755 index 0000000..3c0f56a --- /dev/null +++ b/Python/soper/soper.py @@ -0,0 +1,207 @@ +import sys +from dataclasses import dataclass +from enum import StrEnum, auto +from random import sample +from typing import Any, Callable, Iterator, Self + +from PyQt6 import QtGui +from PyQt6.QtCore import QSize, Qt, QTimer +from PyQt6.QtGui import QIcon +from PyQt6.QtWidgets import ( + QApplication, + QHBoxLayout, + QLabel, + QLCDNumber, + QMessageBox, + QPushButton, + QVBoxLayout, + QWidget, +) + + +@dataclass(slots=True, frozen=True) +class Cords: + x: int + y: int + + +class ButtonType(StrEnum): + EMPTY = auto() + BOMB = auto() + NUMBER = auto() + + +class Button(QPushButton): + def __init__( + self, + cords: Cords, + handler1: Callable[[Self, bool], Any], + handler2: Callable[[Self], Any], + ) -> None: + super().__init__() + self.setFixedSize(50, 50) + self.type = ButtonType.EMPTY + self.number = 0 + self.cords = cords + self.marked: bool = False + self.revealed: bool = False + self.left_handler = handler1 + self.right_handler = handler2 + + def reveal(self) -> Iterator[Cords]: + self.revealed = True + self.setStyleSheet("background-color: grey") + if self.type is ButtonType.NUMBER: + self.setText(str(self.number)) + + def mousePressEvent(self, e: QtGui.QMouseEvent) -> None: + if e.button() is Qt.MouseButton.LeftButton: + self.left_handler(self, True) + elif e.button() is Qt.MouseButton.RightButton: + self.right_handler(self) + + def get_neighbors(self) -> Iterator[Cords]: + for x in range(self.cords.x - 1, self.cords.x + 2): + for y in range(self.cords.y - 1, self.cords.y + 2): + if x < 0 or x >= 10: + continue + if y < 0 or y >= 10: + continue + if Cords(x, y) == self.cords: + continue + yield Cords(x, y) + + def update_texture(self) -> None: + if self.marked: + self.setIcon(QIcon("flag.jpg")) + self.setIconSize(QSize(50, 50)) + else: + self.setIcon(QIcon()) + # TODO: remove + if self.type is ButtonType.BOMB: + self.setStyleSheet("background-color: red") + + +class Window(QWidget): + def __init__(self) -> None: + super().__init__() + self.setGeometry(0, 0, 500, 500) + self.seconds = 0 + self.revealed_buttons: set[Cords] = set() + self.timer = QTimer() + self.timer.timeout.connect(self.show_time) + self.timer.setInterval(1000) + self.timer.start() + self.bomb_amount = 10 + self.flags_amount = 0 + self.setWindowTitle("Окно") + self.create_buttons() + self.select_bombs() + + def create_buttons(self) -> None: + vbox = QVBoxLayout() + + hbox = QHBoxLayout() + self.counter = QLabel(f"{self.flags_amount}/{self.bomb_amount}") + finish_button = QPushButton() + finish_button.setText("FINISH") + finish_button.clicked.connect(self.finish) + self.time = QLCDNumber() + self.time.setDigitCount(5) + self.time.display("00:00") + hbox.addWidget(self.counter) + hbox.addWidget(finish_button) + hbox.addWidget(self.time) + + vbox.addLayout(hbox) + + self.buttons: list[list[Button]] = [] + for x in range(10): + hbox = QHBoxLayout() + row = [] + + for y in range(10): + button = Button( + Cords(x, y), + self.button_clicked, + self.set_flag, + ) + row.append(button) + hbox.addWidget(button) + + self.buttons.append(row) + vbox.addLayout(hbox) + self.setLayout(vbox) + + def select_bombs(self) -> None: + buttons = [j for i in self.buttons for j in i] + bombs = sample(buttons, self.bomb_amount) + for bomb in bombs: + bomb.type = ButtonType.BOMB + # TODO: remove + bomb.setStyleSheet("background-color: red") + for bomb in bombs: + for cords in bomb.get_neighbors(): + button = self.buttons[cords.x][cords.y] + if button.type is ButtonType.BOMB: + continue + button.type = ButtonType.NUMBER + button.number += 1 + + def button_clicked(self, button: Button, user: bool) -> None: + if button.cords in self.revealed_buttons: + return + self.revealed_buttons.add(button.cords) + if button.type is ButtonType.BOMB: + if user: + QMessageBox.warning(self, "You lost", "You lost") + self.close() + return + button.reveal() + if button.type is ButtonType.NUMBER: + return + for cords in button.get_neighbors(): + neighbor = self.buttons[cords.x][cords.y] + self.button_clicked(neighbor, False) + + def set_flag(self, button: Button) -> None: + if button.revealed: + return + was_marked = button.marked + button.marked = not was_marked + button.update_texture() + if was_marked: + self.flags_amount -= 1 + else: + self.flags_amount += 1 + self.counter.setText(f"{self.flags_amount}/{self.bomb_amount}") + + def finish(self) -> None: + buttons = [j for i in self.buttons for j in i] + bomb = ButtonType.BOMB + if not all(button.marked for button in buttons if button.type is bomb): + QMessageBox.warning(self, "You lost", "You lost") + self.close() + return + QMessageBox.information(self, "You won", "You won") + self.close() + + def show_time(self) -> None: + self.seconds += 1 + minutes, seconds = divmod(self.seconds, 60) + self.time.display(f"{minutes:0>2}:{seconds:0>2}") + if minutes >= 5: + QMessageBox.warning(self, "You lost", "You lost") + self.close() + return + + +def main() -> None: + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/Python/sql/main.py b/Python/sql/main.py new file mode 100755 index 0000000..870d627 --- /dev/null +++ b/Python/sql/main.py @@ -0,0 +1,419 @@ +from __future__ import annotations + +import os +import sys +from typing import Optional + +import psycopg2 +from psycopg2 import extensions +from PyQt6.QtWidgets import ( + QApplication, + QHBoxLayout, + QLabel, + QLineEdit, + QMessageBox, + QPushButton, + QTableWidget, + QTableWidgetItem, + QVBoxLayout, + QWidget, +) +from cryptography.hazmat.primitives.kdf.scrypt import Scrypt +from cryptography.exceptions import InvalidKey + + +def get_kdf(salt: bytes) -> Scrypt: + return Scrypt(salt, length=128, n=2**14, r=8, p=1) + + +def encrypt(password: str) -> bytes: + salt = os.urandom(64) + return (salt, get_kdf(salt).derive(password.encode("UTF-8"))) + + +def check_password(password: str, salt: bytes, expected: bytes) -> bool: + try: + get_kdf(salt).verify(password.encode("UTF-8"), expected) + except InvalidKey: + return False + else: + return True + + +Student = tuple[int, str, str, str] + +HOST = "127.0.0.1" +USER = "tester" +PASSWORD = "example123!" +PORT = 5432 +DATABASE = "testing" + + +class Connection: + connection: Optional[extensions.connection] = None + + @classmethod + def get(cls) -> extensions.connection: + if cls.connection is not None: + return cls.connection + try: + cls.connection = psycopg2.connect( + host=HOST, user=USER, password=PASSWORD, database=DATABASE + ) + cls.connection.autocommit = True + except Exception as e: + print(type(e)) + raise + return cls.connection + + @classmethod + def close(cls) -> None: + if cls.connection is not None: + cls.connection.close() + cls.connection = None + + @classmethod + def get_all_data(cls) -> list[Student]: + with cls.get().cursor() as cursor: + cursor.execute("SELECT * FROM students ORDER BY id") + return cursor.fetchall() + + @classmethod + def get_user(cls, login: str) -> Optional[tuple[bytes, bytes]]: + with cls.get().cursor() as cursor: + cursor.execute( + "SELECT salt, password, admin FROM users WHERE login = %s", + (login,), + ) + result = cursor.fetchone() + if result is None: + return None + return (bytes(result[0]), bytes(result[1]), result[2]) + + +def prepare_database() -> None: + with Connection.get().cursor() as cursor: + cursor.execute("DROP TABLE IF EXISTS students") + cursor.execute( + "CREATE TABLE students(" + "id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY," + "surname VARCHAR(255)," + "name VARCHAR(255)," + "group_ VARCHAR(255))" + ) + cursor.execute("DROP TABLE IF EXISTS users") + cursor.execute( + "CREATE TABLE users(" + "id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY," + "login VARCHAR(255)," + "salt BYTEA," + "password BYTEA," + "admin BOOLEAN)" + ) + + +def add_test_data() -> None: + with Connection.get().cursor() as cursor: + cursor.execute( + "INSERT INTO STUDENTS(name, surname, group_) VALUES " + "('Nick', 'Stepanov', 'programmer')," + "('Maxim', 'Yes', 'programmer')" + ) + admin_creds = encrypt("admin") + user_creds = encrypt("example123!") + cursor.execute( + "INSERT INTO users(login, salt, password, admin) VALUES " + "('admin', %s, %s, true)," + "('user', %s, %s, false)", + (*admin_creds, *user_creds), + ) + + +class Window(QWidget): + def __init__(self, is_admin: bool): + super().__init__() + self.is_admin = is_admin + self.setGeometry(200, 200, 700, 400) + self.setWindowTitle("SQL Test") + self.vbox = QVBoxLayout() + self.setLayout(self.vbox) + self.need_clean = [] + self.update() + + def update(self, data: Optional[list[Student]] = None): + self.clean_widgets() + + if data is None: + data = Connection.get_all_data() + + hbox_buttons = QHBoxLayout() + but1 = QPushButton("Create") + but1.clicked.connect(self.button_create_handler) + but2 = QPushButton("Edit") + but2.clicked.connect(self.button_edit_handler) + but3 = QPushButton("Remove") + but3.clicked.connect(self.button_remove_handler) + but4 = QPushButton("Find") + but4.clicked.connect(self.button_find_handler) + hbox_buttons.addWidget(but1) + hbox_buttons.addWidget(but2) + hbox_buttons.addWidget(but3) + hbox_buttons.addWidget(but4) + self.add_widget_to_need_clean(but1, but2, but3, but4) + + hbox_id = QHBoxLayout() + label_id = QLabel("ID") + self.text_id = QLineEdit() + hbox_id.addWidget(label_id) + hbox_id.addWidget(self.text_id) + self.add_widget_to_need_clean(hbox_id, label_id, self.text_id) + + hbox_surname = QHBoxLayout() + label_surname = QLabel("Surname") + self.text_surname = QLineEdit() + hbox_surname.addWidget(label_surname) + hbox_surname.addWidget(self.text_surname) + self.add_widget_to_need_clean( + hbox_surname, + label_surname, + self.text_surname, + ) + + hbox_name = QHBoxLayout() + label_name = QLabel("Name") + self.text_name = QLineEdit() + hbox_name.addWidget(label_name) + hbox_name.addWidget(self.text_name) + self.add_widget_to_need_clean(hbox_name, label_name, self.text_name) + + hbox_group = QHBoxLayout() + label_group = QLabel("Group") + self.text_group = QLineEdit() + hbox_group.addWidget(label_group) + hbox_group.addWidget(self.text_group) + self.add_widget_to_need_clean(hbox_group, label_group, self.text_group) + + hbox_table = QHBoxLayout() + table = self.form_table(data) + hbox_table.addWidget(table) + self.add_widget_to_need_clean(table) + + self.vbox.addLayout(hbox_id) + self.vbox.addLayout(hbox_surname) + self.vbox.addLayout(hbox_name) + self.vbox.addLayout(hbox_group) + self.vbox.addLayout(hbox_buttons) + self.vbox.addLayout(hbox_table) + + def add_widget_to_need_clean(self, *args): + for el in args: + self.need_clean.append(el) + + def clean_widgets(self): + if self.need_clean: + for widget in self.need_clean: + widget.deleteLater() + self.need_clean.clear() + + @staticmethod + def form_table(data: list[Student]) -> QTableWidget: + table = QTableWidget() + table.setRowCount(10) + table.setColumnCount(4) + table.setHorizontalHeaderLabels(("ID", "Surname", "Name", "Group")) + for i, row in enumerate(data): + for j, column in enumerate(map(str, row)): + table.setItem(i, j, QTableWidgetItem(column)) + return table + + def button_edit_handler(self): + if not self.is_admin: + return QMessageBox().warning( + self, "Rights", "You have to be an admin to do that" + ) + with Connection.get().cursor() as cursor: + cursor.execute("SELECT * FROM students;") + students = cursor.fetchall() + indexes = [] + for student in students: + indexes.append(str(student[0])) + if self.text_id.text() in indexes: + commands = [] + if self.text_surname.text() != "": + commands.append( + "UPDATE students SET surname = '" + + self.text_surname.text() + + "' " + + "WHERE id = " + + self.text_id.text() + + ";" + ) + if self.text_name.text() != "": + commands.append( + "UPDATE students SET name = '" + + self.text_name.text() + + "' " + + "WHERE id = " + + self.text_id.text() + + ";" + ) + if self.text_group.text() != "": + commands.append( + "UPDATE students SET gr = '" + + self.text_group.text() + + "' " + + "WHERE id = " + + self.text_id.text() + + ";" + ) + with Connection.get().cursor() as cursor: + for command in commands: + cursor.execute(command) + QMessageBox.information(self, "Info", "Data changed") + else: + QMessageBox.warning(self, "Error", "Data not changed") + self.update() + + def button_create_handler(self): + if not self.is_admin: + return QMessageBox().warning( + self, "Rights", "You have to be an admin to do that" + ) + name = self.text_name.text() + surname = self.text_surname.text() + group = self.text_group.text() + if not all((surname, name, group)): + QMessageBox.warning(self, "Invalid id", "Invalid id") + return + with Connection.get().cursor() as cursor: + cursor.execute( + "INSERT INTO students(name, surname, group_) VALUES (%s, %s, %s)", # noqa + (name, surname, group), + ) + self.update() + + def button_remove_handler(self) -> None: + if not self.is_admin: + return QMessageBox().warning( + self, "Rights", "You have to be an admin to do that" + ) + try: + id = int(self.text_id.text()) + except ValueError: + QMessageBox.warning(self, "Invalid ID", "Invalid ID") + return + with Connection.get().cursor() as cursor: + cursor.execute("DELETE FROM students WHERE id = %s", (id,)) + self.update() + + def button_find_handler(self) -> None: + id = self.text_id.text() + name = self.text_name.text() + surname = self.text_surname.text() + group = self.text_group.text() + filters = [] + params = [] + if id: + if not id.isnumeric(): + QMessageBox.warning(self, "Invalid id", "Invalid id") + return + filters.append("id = %s") + params.append(id) + if name: + filters.append("LOWER(name) ~ %s") + params.append(name.lower()) + if surname: + filters.append("LOWER(surname) ~ %s") + params.append(surname.lower()) + if group: + filters.append("LOWER(group_) ~ %s") + params.append(group.lower()) + + with Connection.get().cursor() as cursor: + if not params: + filters = "" + else: + filters = f" WHERE {' AND '.join(filters)}" + query = f"SELECT * FROM students{filters} ORDER BY id" + cursor.execute(query, params) + data = cursor.fetchall() + self.update(data) + + +class WindowPW(QWidget): + def __init__(self) -> None: + super().__init__() + self.loged_in = False + self.is_admin = False + self.setGeometry(200, 200, 250, 150) + self.setWindowTitle("Login") + label = QLabel("Name", self) + label.move(10, 10) + self.line_login = QLineEdit(self) + self.line_login.move(100, 10) + label_pw = QLabel("password", self) + label_pw.move(10, 50) + self.line_pw = QLineEdit(self) + self.line_pw.move(100, 50) + register_but = QPushButton("Register", self) + register_but.move(25, 100) + register_but.clicked.connect(self.register_handler) + but = QPushButton("OK", self) + but.clicked.connect(self.but_handler) + but.move(100, 100) + + def but_handler(self) -> None: + login = self.line_login.text().strip() + password = self.line_pw.text().strip() + user = Connection.get_user(login) + if user is None: + self.close() + return QMessageBox.warning(self, "Bye!", "Wrong creds") + salt, expected, admin = Connection.get_user(login) + if check_password(password, salt, expected): + self.loged_in = True + self.is_admin = admin + else: + QMessageBox.warning(self, "Bye!", "Wrong creds") + self.close() + + def register_handler(self) -> None: + login = self.line_login.text().strip() + password = self.line_pw.text().strip() + if not (login and password): + return QMessageBox().warning( + self, "Wrong creds", "You must input both login and password" + ) + with Connection.get().cursor() as cursor: + cursor.execute( + "SELECT true FROM users WHERE login = %s LIMIT 1", + (login,), + ) + if cursor.fetchone() is not None: + return QMessageBox().warning( + self, "Wrong creds", "Login already exists" + ) + cursor.execute( + "INSERT INTO users(login, salt, password) VALUES (%s, %s, %s)", + (login, *encrypt(password)), + ) + self.loged_in = True + self.close() + + +def main() -> None: + prepare_database() + add_test_data() + app = QApplication(sys.argv) + window = WindowPW() + window.show() + result = app.exec() + if not window.loged_in: + sys.exit(result) + window = Window(window.is_admin) + window.show() + sys.exit(app.exec()) + + +if __name__ == "__main__": + main() diff --git a/sql/Databases.md b/SQL/Databases.md similarity index 100% rename from sql/Databases.md rename to SQL/Databases.md diff --git a/sql/compose.yaml b/SQL/compose.yaml similarity index 100% rename from sql/compose.yaml rename to SQL/compose.yaml diff --git a/sql/task1.sql b/SQL/task1.sql similarity index 100% rename from sql/task1.sql rename to SQL/task1.sql diff --git a/sql/task2.sql b/SQL/task2.sql similarity index 100% rename from sql/task2.sql rename to SQL/task2.sql diff --git a/sql/task3.sql b/SQL/task3.sql similarity index 100% rename from sql/task3.sql rename to SQL/task3.sql diff --git a/sql/values.py b/SQL/values.py similarity index 100% rename from sql/values.py rename to SQL/values.py