diff --git a/.idea/misc.xml b/.idea/misc.xml
index e17b4fa..bef5a0a 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,7 +1,4 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/.idea/vision-macula.iml b/.idea/vision-macula.iml
index 4ada2a6..62b7f51 100644
--- a/.idea/vision-macula.iml
+++ b/.idea/vision-macula.iml
@@ -7,7 +7,7 @@
-
+
\ No newline at end of file
diff --git a/database.db b/database.db
new file mode 100644
index 0000000..f749bb3
Binary files /dev/null and b/database.db differ
diff --git a/database_schema.sql b/database_schema.sql
new file mode 100644
index 0000000..dbe0a0c
Binary files /dev/null and b/database_schema.sql differ
diff --git a/src/bsmu/macula/app/2.png b/src/bsmu/macula/app/2.png
new file mode 100644
index 0000000..834570c
Binary files /dev/null and b/src/bsmu/macula/app/2.png differ
diff --git a/src/bsmu/macula/app/database.db b/src/bsmu/macula/app/database.db
new file mode 100644
index 0000000..7aaefac
Binary files /dev/null and b/src/bsmu/macula/app/database.db differ
diff --git a/src/bsmu/macula/app/edit.png b/src/bsmu/macula/app/edit.png
new file mode 100644
index 0000000..6b44614
Binary files /dev/null and b/src/bsmu/macula/app/edit.png differ
diff --git a/src/bsmu/macula/app/example.db b/src/bsmu/macula/app/example.db
new file mode 100644
index 0000000..e69de29
diff --git a/src/bsmu/macula/app/images/icons/edit.png b/src/bsmu/macula/app/images/icons/edit.png
new file mode 100644
index 0000000..6b44614
Binary files /dev/null and b/src/bsmu/macula/app/images/icons/edit.png differ
diff --git a/src/bsmu/macula/configs/default/bsmu.macula/app.MaculaApp.conf.yaml b/src/bsmu/macula/configs/default/bsmu.macula/app.MaculaApp.conf.yaml
index 56a0c75..d3e9922 100644
--- a/src/bsmu/macula/configs/default/bsmu.macula/app.MaculaApp.conf.yaml
+++ b/src/bsmu/macula/configs/default/bsmu.macula/app.MaculaApp.conf.yaml
@@ -28,4 +28,6 @@ plugins:
- bsmu.vision.plugins.task_storage_view.TaskStorageViewPlugin
+ - bsmu.macula.plugins.db.BD.BD
- bsmu.macula.plugins.gui.ensemble_segmenter_gui.EnsembleSegmenterGuiPlugin
+ - bsmu.macula.plugins.db.database_manager.DatabaseManager
diff --git a/src/bsmu/macula/configs/default/bsmu.macula/plugins.BD.conf.yaml b/src/bsmu/macula/configs/default/bsmu.macula/plugins.BD.conf.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/src/bsmu/macula/configs/default/bsmu.macula/plugins.database_manager.conf.yaml b/src/bsmu/macula/configs/default/bsmu.macula/plugins.database_manager.conf.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/src/bsmu/macula/plugins/db/BD.py b/src/bsmu/macula/plugins/db/BD.py
new file mode 100644
index 0000000..cec7873
--- /dev/null
+++ b/src/bsmu/macula/plugins/db/BD.py
@@ -0,0 +1,52 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from PySide6.QtCore import Qt
+from bsmu.vision.core.plugins import Plugin
+from bsmu.vision.plugins.doc_interfaces.mdi import MdiPlugin
+from bsmu.vision.plugins.windows.main import AlgorithmsMenu, MainWindowPlugin, MainWindow
+
+from bsmu.macula.plugins.db.SQLiteTableViewer import TableWidgetExample
+from bsmu.macula.plugins.ensemble_segmenter import BinaryEnsemblePlugin
+
+if TYPE_CHECKING:
+ pass
+class BD(Plugin):
+ _DEFAULT_DEPENDENCY_PLUGIN_FULL_NAME_BY_KEY = {
+ 'main_window_plugin': 'bsmu.vision.plugins.windows.main.MainWindowPlugin',
+ 'mdi_plugin': 'bsmu.vision.plugins.doc_interfaces.mdi.MdiPlugin'
+ }
+
+ def __init__(
+ self,
+ main_window_plugin: MainWindowPlugin,
+ mdi_plugin: MdiPlugin
+ ):
+ super().__init__()
+ self._main_window_plugin = main_window_plugin
+ self._mdi_plugin = mdi_plugin
+
+ self._ensemble_segmenter_gui: BinaryEnsemblePlugin | None = None
+ self._main_window: MainWindow | None = None
+
+ @property
+ def ensemble_segmenter_gui(self) -> BinaryEnsemblePlugin | None:
+ return self._ensemble_segmenter_gui
+
+ def _enable_gui(self):
+ self._main_window = self._main_window_plugin.main_window
+
+ self._main_window.add_menu_action(
+ AlgorithmsMenu,
+ self.tr('BD'),
+ self._re
+ )
+ def _re(self):
+ self.window = TableWidgetExample()
+ self.window.setWindowModality(Qt.ApplicationModal)
+ self.window.show()
+
+ def _disable(self):
+ self._ensemble_segmenter_gui = None
+ self._main_window = None
\ No newline at end of file
diff --git a/src/bsmu/macula/plugins/db/SQLiteTableViewer.py b/src/bsmu/macula/plugins/db/SQLiteTableViewer.py
new file mode 100644
index 0000000..3eed016
--- /dev/null
+++ b/src/bsmu/macula/plugins/db/SQLiteTableViewer.py
@@ -0,0 +1,494 @@
+from functools import partial
+
+from PySide6.QtGui import QIcon, Qt, QPixmap
+from PySide6.QtSql import QSqlQueryModel, QSqlDatabase, QSqlQuery
+from PySide6.QtWidgets import (
+ QWidget, QTableWidget, QPushButton, QVBoxLayout, QHBoxLayout, QFrame, QLineEdit, QLabel, QScrollArea, QGridLayout,
+ QTableView,
+ QStyledItemDelegate, QMessageBox, QGroupBox, QFormLayout, QTabWidget, QDialog
+)
+
+from bsmu.macula.plugins.db.add_patient_dialog import AddRecordDialog
+from bsmu.macula.plugins.db.edit_pacirnt_delegate import EditPacientDelegate
+from bsmu.macula.widgets.eye_data_widget import EyeDataWidget
+from bsmu.macula.records.eye_info_data import PatientExamData
+
+
+class TableWidgetExample(QWidget):
+
+ def __init__(self):
+ super().__init__()
+ self.init_db()
+ self.createMainTables()
+ self.setWindowTitle("Пациенты")
+ self.resize(1200, 800)
+ self.appointments_table_created = False
+ self.appointment_data_table_created = False
+
+ def init_db(self):
+ db = QSqlDatabase.addDatabase("QSQLITE")
+ db.setDatabaseName("database.db")
+ if not db.open():
+ print("Ошибка подключения к базе данных")
+ return None
+ self.db = db
+
+ def patient_clicked(self, index):
+ row = index.row()
+ user_id = self.patients_model.index(row, 0).data()
+ self.load_appointments_from_db(user_id)
+
+ def apointment_clicked(self, index):
+ row = index.row()
+ apointment_id = self.appointments_model.index(row, 0).data()
+ self.load_appointment_data_from_db(apointment_id)
+
+ self.init_ui()
+ # self.main_lay.addLayout(self.init_ui())
+
+ def createMainTables(self):
+ self.setWindowTitle("Пациенты")
+ self.resize(1200, 800)
+
+ self.main_lay = QHBoxLayout(self)
+ left_layout = QVBoxLayout(self)
+
+ db = QSqlDatabase.addDatabase("QSQLITE")
+ db.setDatabaseName("database.db")
+ if not db.open():
+ print("Ошибка подключения к базе данных")
+ return None
+
+ self.patients_model = QSqlQueryModel()
+ self.patients_model.setQuery("SELECT id, name, sex, year_of_birthday FROM pacients")
+
+ dict_patient = {
+ # "id": "Ид",
+ "name": "Имя",
+ "sex": "Пол",
+ "year_of_birthday": "Год рождения",
+ "acts": "Действия"
+ }
+
+ # for i in range(0, self.patients_model.columnCount()):
+ # self.patients_model.setHeaderData(i, Qt.Orientation.Horizontal, dict_patient[self.patients_model.headerData(i, Orientation.Horizontal )])
+
+ self.patients_table = QTableView(self)
+ self.patients_table.setModel(self.patients_model)
+
+ self.patients_model.insertColumn(self.patients_model.columnCount())
+
+ self.patients_model.setHeaderData(0, Qt.Orientation.Horizontal, "Ид")
+ self.patients_model.setHeaderData(1, Qt.Orientation.Horizontal, "Имя")
+ self.patients_model.setHeaderData(2, Qt.Orientation.Horizontal, "Пол")
+ self.patients_model.setHeaderData(3, Qt.Orientation.Horizontal, "Год рождения")
+ self.patients_model.setHeaderData(4, Qt.Orientation.Horizontal, "Редактировать")
+
+ self.patients_table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
+ self.patients_table.clicked.connect(self.patient_clicked)
+
+ self.appointment_edit_delegate = EditPacientDelegate(r"edit.png", self.patients_table, self.open_dialog)
+ self.patients_table.setItemDelegateForColumn(self.patients_model.columnCount() - 1, self.appointment_edit_delegate)
+
+
+ self.patients_table.setColumnHidden(0, True)
+
+ add_patient = QPushButton("Добавить пациента")
+ add_patient.clicked.connect(partial(self.open_dialog, 0))
+ button_layout = QHBoxLayout()
+ button_layout.addWidget(add_patient)
+
+ self.appointments_table = QTableView(self)
+ add_appointment_button = QPushButton("Добавить результаты приема")
+ button_layout2 = QHBoxLayout()
+ button_layout2.addWidget(add_appointment_button)
+
+ left_layout.addWidget(self.patients_table)
+ left_layout.addLayout(button_layout)
+ left_layout.addWidget(self.appointments_table)
+ left_layout.addLayout(button_layout2)
+ self.main_lay.addLayout(left_layout)
+ layout = QVBoxLayout()
+ self.tab_widget = QTabWidget()
+ layout.addWidget(self.tab_widget)
+ self.main_lay.addLayout(layout)
+ # layout2 = QVBoxLayout()
+ #
+ # # QLabel для превью
+ # self.preview_label = QLabel("Нажмите на изображение для увеличения")
+ # pixmap = QPixmap(rf"C:\Users\Evgeniy\OneDrive\Pictures\32022.jpg") # Подставьте путь к своему изображению
+ # self.preview_label.setPixmap(pixmap.scaled(200, 150)) # Масштабируем превью
+ # layout2.addWidget(self.preview_label)
+ #
+ # # Добавляем обработчик клика
+ # self.preview_label.mousePressEvent = self.open_full_image
+ # self.main_lay.addLayout(layout2)
+ # main_lay.addLayout(self.addlay())
+
+ def open_full_image(self, event):
+ """Открывает изображение в отдельном окне."""
+ self.dialog = PreviewWindow(rf"C:\Users\Evgeniy\OneDrive\Pictures\32022.jpg") # Указываем путь к изображению
+ self.dialog.exec()
+
+ def init_ui(self):
+ if (self.tab_widget.count() > 0) :
+ self.tab_widget.removeTab(0)
+ self.tab_widget.removeTab(0)
+ self.tab_widget.addTab(self.create_scrollable_area("Правый глаз", 0), "Правый глаз")
+ self.tab_widget.addTab(self.create_scrollable_area("Левый глаз", 1), "Левый глаз")
+ eye_data = PatientExamData.from_query_model(self.appointment_data_model, 1)
+ eye_widget = EyeDataWidget(eye_data)
+ self.tab_widget.addTab(eye_widget, "Левый глаз")
+
+
+
+ def create_scrollable_area(self, layout_name, index_eye):
+ """Создаёт область прокрутки с блоками"""
+ scroll_area = QScrollArea()
+ scroll_area.setWidgetResizable(True)
+
+ # Виджет-контейнер для полей
+ scroll_content = QWidget()
+ scroll_layout = QVBoxLayout()
+
+ # Добавляем блоки в прокручиваемую область
+ scroll_layout.addWidget(self.create_block("Обследование", [
+ ("Дата посещения", self.get_app_data_in(index_eye, 3)),
+ ("Продолжительность заболевания", self.get_app_data_in(index_eye, 4)),
+ ("Тип томографа ","Топкон"),
+ ("Критерий AREDS", self.get_app_data_in(index_eye, 7)),
+ ("Рефракция", self.get_app_data_in(index_eye, 8)),
+ ("Тип неоваскуляризации", self.get_app_data_in(index_eye, 9))
+ ]))
+ scroll_layout.addWidget(self.create_block("Ретинальные показатели", [
+ ("Толщина хориоидеи в центре", self.get_app_data_in(index_eye, 10)),
+ ("Толщина сетчатки в фовеоле", self.get_app_data_in(index_eye, 11)),
+ ("Общий объем", self.get_app_data_in(index_eye, 14)),
+ ("Средний объем", self.get_app_data_in(index_eye, 15))
+ ]))
+ scroll_layout.addWidget(self.create_block("", [
+ ("Состояние РПЭ", self.get_app_data_in(index_eye, 16)),
+ ("Локализация дефектов РПЭ", self.get_app_data_in(index_eye, 17)),
+ ("Локализация кистозного макулярного отека", self.get_app_data_in(index_eye, 18))
+ ]))
+ scroll_layout.addWidget(QLabel("Отслойки РПЭ"))
+ scroll_layout.addWidget(self.create_block("Серозная ОПЭ", [
+ ("Локализация", self.get_app_data_in(index_eye, 19)),
+ ("Ширина", self.get_app_data_in(index_eye, 20)),
+ ("Высота", self.get_app_data_in(index_eye, 21)),
+ ("Площадь", self.get_app_data_in(index_eye, 22))
+ ]))
+ scroll_layout.addWidget(self.create_block("Геморрагическая ОПЭ", [
+ ("Локализация", self.get_app_data_in(index_eye, 23)),
+ ("Ширина", self.get_app_data_in(index_eye, 24)),
+ ("Высота", self.get_app_data_in(index_eye, 25)),
+ ("Площадь", self.get_app_data_in(index_eye, 26))
+ ]))
+ scroll_layout.addWidget(self.create_block("Фиброваскулярная ОПЭ", [
+ ("Локализация", self.get_app_data_in(index_eye, 27)),
+ ("Ширина", self.get_app_data_in(index_eye, 28)),
+ ("Высота", self.get_app_data_in(index_eye, 29)),
+ ("Площадь", self.get_app_data_in(index_eye, 30))
+ ]))
+ scroll_layout.addWidget(self.create_block("Друзеноидная ОПЭ", [
+ ("Локализация", self.get_app_data_in(index_eye, 31)),
+ ("Ширина", self.get_app_data_in(index_eye, 32)),
+ ("Высота", self.get_app_data_in(index_eye, 33)),
+ ("Площадь", self.get_app_data_in(index_eye, 34))
+ ]))
+ scroll_layout.addWidget(self.create_block("Друзы", [
+ ("Локализация", self.get_app_data_in(index_eye, 35)),
+ ("Ширина", self.get_app_data_in(index_eye, 36)),
+ ("Высота", self.get_app_data_in(index_eye, 37)),
+ ("Площадь", self.get_app_data_in(index_eye, 38))
+ ]))
+ scroll_layout.addWidget(self.create_block("Жидкость под РПЭ", [
+ ("Пощадь", self.get_app_data_in(index_eye, 39)),
+ ("Локализация", self.get_app_data_in(index_eye, 40))
+ ]))
+ scroll_layout.addWidget(self.create_block("Эллипсоидная зона", [
+ ("Состояние", self.get_app_data_in(index_eye, 41)),
+ ("Локализация дефектов", self.get_app_data_in(index_eye, 42))
+ ]))
+ scroll_layout.addWidget(self.create_block("Миоидная зона", [
+ ("Состояние", self.get_app_data_in(index_eye, 43)),
+ ("Локализация дефектов", self.get_app_data_in(index_eye, 44))
+ ]))
+ scroll_layout.addWidget(self.create_block("Отслойка нейросенсорной сетчатки", [
+ ("Локализация", self.get_app_data_in(index_eye, 45)),
+ ("Ширина", self.get_app_data_in(index_eye, 46)),
+ ("Высота", self.get_app_data_in(index_eye, 47)),
+ ("Площадь", self.get_app_data_in(index_eye, 48))
+ ]))
+ scroll_layout.addWidget(self.create_block("Гиперрефлективный материал", [
+ ("Локализация", self.get_app_data_in(index_eye, 49)),
+ ("Площадь", self.get_app_data_in(index_eye, 50))
+ ]))
+
+ # Устанавливаем макет для контейнера
+ scroll_content.setLayout(scroll_layout)
+ scroll_area.setWidget(scroll_content)
+
+ return scroll_area
+
+ def get_app_data_in(self, x, y):
+ return str(self.appointment_data_model.index(x, y).data())
+ def create_block(self, title, fields):
+ """Создание тематического блока с полями и значениями"""
+ group_box = QGroupBox(title)
+ form_layout = QFormLayout()
+
+ # Добавляем поля с заранее заданными значениями
+ for field, value in fields:
+ input_field = QLineEdit()
+ input_field.setText(value) # Подстановка значения
+ form_layout.addRow(QLabel(field + ":"), input_field)
+
+ group_box.setLayout(form_layout)
+ return group_box
+
+ def open_dialog(self, patient_id):
+ self.dialog = AddRecordDialog(self.db, patient_id)
+ self.dialog.exec_() # Запуск модального окна
+
+ def open_dialog_appointmernt(self, patient_id):
+ self.dialog = AddRecordDialog(self.db, patient_id)
+ self.dialog.exec_() # Запуск модального окна
+
+ def load_appointments_from_db(self, pacientId):
+ db = self.open_connection()
+ if db is None:
+ return # Завершаем, если подключение не удалось
+
+ self.appointments_model = QSqlQueryModel()
+ query = QSqlQuery()
+ query.prepare("SELECT * FROM appointments WHERE pacient_id = :pacient_id")
+ query.bindValue(":pacient_id", pacientId)
+
+ if query.exec():
+ self.appointments_model.setQuery(query)
+ else:
+ print("Ошибка выполнения запроса:", query.lastError().text())
+
+ table1Description = ["Ид", "Дата приема", "duration_of_the_disease", "Действия"]
+
+
+ # for col, field in enumerate(table1Description.keys()):
+ # self.patients_model.setHeaderData(col, Qt.Orientation.Horizontal, table1Description[field])
+
+ self.appointments_table.setModel(self.appointments_model)
+
+ self.appointments_model.insertColumn(self.appointments_model.columnCount())
+
+
+ self.appointments_model.setHeaderData(0, Qt.Orientation.Horizontal, "Ид")
+ self.appointments_model.setHeaderData(1, Qt.Orientation.Horizontal, "Дата приема")
+ self.appointments_model.setHeaderData(2, Qt.Orientation.Horizontal, "Длит. болезни")
+ # self.appointments_model.setHeaderData(3, Qt.Orientation.Horizontal, "Ид пациента")
+ self.appointments_model.setHeaderData(3, Qt.Orientation.Horizontal, "Редактировать")
+ self.appointments_model.setHeaderData(4, Qt.Orientation.Horizontal, "Показать")
+
+ self.appointments_table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
+ self.appointments_table.clicked.connect(self.apointment_clicked)
+
+ self.appointment_edit_delegate2 = EditPacientDelegate(r"edit.png", self.patients_table, self.open_dialog_appointmernt)
+ self.appointments_table.setItemDelegateForColumn(self.patients_model.columnCount() - 2, self.appointment_edit_delegate2)
+
+ self.appointment_edit_delegate3 = EditPacientDelegate(r"2.png", self.patients_table, self.open_dialog_appointmernt)
+ self.appointments_table.setItemDelegateForColumn(self.patients_model.columnCount() - 1, self.appointment_edit_delegate3)
+
+ self.appointments_table.setColumnHidden(0, True)
+
+ def load_appointment_data_from_db(self, appiontmentId):
+ self.appointment_data_model = QSqlQueryModel()
+ query = QSqlQuery()
+ query.prepare("SELECT * FROM eyes WHERE appointment_id = :appiontmentId")
+ query.bindValue(":appiontmentId", appiontmentId)
+
+ if query.exec():
+ self.appointment_data_model.setQuery(query)
+ else:
+ print("Ошибка выполнения запроса:", query.lastError().text())
+
+ print(self.appointment_data_model.index(0, 0).data())
+
+ def addlay(self):
+ fields = [
+ "id", "eye", "appointment_id", "date", "duration_of_the_disease",
+ "topkon", "optopol", "areds", "refraction", "type_of_neovascularization",
+ "choroidal_thickness_center", "cts_foveola", "cts_sup_inner_fovea",
+ "cts_sup_out_fovea", "total_volume", "average_volume", "rpe_status",
+ "rpe_localisation", "cme_localisation", "serouz_rpe_detachment_localisation",
+ "serouz_rpe_detachment_width", "serouz_rpe_detachment_height", "serouz_rpe_detachment_area",
+ "hemorrhagic_rpe_detachment_localisation", "hemorrhagic_rpe_detachment_width",
+ "hemorrhagic_rpe_detachment_heidgt", "hemorrhagic_rpe_detachment_area",
+ "fibrovascular_rpe_detachment_localisation", "fibrovascular_rpe_detachment_width",
+ "fibrovascular_rpe_detachment_heidgt", "fibrovascular_rpe_detachment_area",
+ "drusenoid_detachment_rpe_localisation", "drusenoid_detachment_rpe_width",
+ "drusenoid_detachment_rpe_height", "drusenoid_detachment_rpe_area",
+ "druses_localisation", "druses_weigt", "druses_heigt", "dzuses_area",
+ "fluid_under_rpe_area", "fluid_under_rpe_localisation", "ez_status",
+ "ez_localisation", "myoidnz_status", "myoidnz_localisation",
+ "rne_detachment_localisation", "rne_detachment_width", "rne_detachment_heigt",
+ "rne_detachment_area", "hyperreflective_material_localisation", "hyperreflective_material_area"
+ ]
+
+ layout = QVBoxLayout()
+
+ # Прокрутка для длинных форм
+ scroll_area = QScrollArea()
+ scroll_widget = QWidget()
+ grid_layout = QGridLayout()
+
+ # Динамическое добавление всех полей
+ row = 0
+ input_fields = {}
+ for field in fields:
+ # Добавляем метку для каждого поля
+ label = QLabel(field.replace('_', ' ').capitalize())
+ grid_layout.addWidget(label, row, 0) # Первый столбец
+
+ # Добавляем редактируемое поле
+ input_field = QLineEdit()
+ input_fields[field] = input_field
+ grid_layout.addWidget(input_field, row, 1) # Второй столбец
+
+ row += 1
+
+ # Добавляем разделительную линию
+ separator = QFrame()
+ separator.setFrameShape(QFrame.Shape.HLine)
+ separator.setFrameShadow(QFrame.Shadow.Sunken)
+ grid_layout.addWidget(separator, row, 0, 1, 2)
+
+ row += 1
+
+ # Кнопка сохранения
+ save_button = QPushButton("Сохранить")
+ grid_layout.addWidget(save_button, row, 0, 1, 2) # На всю ширину
+
+ scroll_widget.setLayout(grid_layout)
+ scroll_area.setWidget(scroll_widget)
+ scroll_area.setWidgetResizable(True)
+
+ layout.addWidget(scroll_area)
+ return layout
+
+
+ def open_connection(self):
+ db = QSqlDatabase.addDatabase("QSQLITE") # Указываем тип базы данных (SQLite в данном случае)
+ db.setDatabaseName("database.db") # Указываем имя или путь к базе данных
+
+ if not db.open(): # Проверяем успешность открытия
+ print("Ошибка подключения к базе данных!")
+ return None
+ else:
+ print("Соединение с базой данных установлено.")
+ return db
+
+
+class ButtonDelegate(QStyledItemDelegate):
+ def paint(self, painter, option, index):
+ """Рисуем кнопку в ячейке"""
+ icon_path = r"/bsmu/macula/app/images/icons/edit.png"
+ icon = QIcon(icon_path) # 🔹 Здесь нужна иконка (например, карандаш)
+ icon.paint(painter, option.rect)
+
+ def editorEvent(self, event, model, option, index):
+ """Обрабатываем нажатие на кнопку"""
+ if event.type() == event.Type.MouseButtonPress:
+ QMessageBox.information(option.widget, "Редактирование", f"Редактируем строку {index.row()}")
+ return True
+
+ # def init_ui(self):
+ # # Основной виджет
+ # layout = QVBoxLayout()
+ #
+ # # Добавляем блоки с прокруткой
+ # layout.addWidget(self.create_scrollable_area())
+ #
+ # # Устанавливаем главный макет
+ # return layout
+ #
+ # def create_scrollable_area(self):
+ # """Создаёт область прокрутки с блоками"""
+ # scroll_area = QScrollArea()
+ # scroll_area.setWidgetResizable(True)
+ #
+ # # Виджет-контейнер для полей
+ # scroll_content = QWidget()
+ # scroll_layout = QVBoxLayout()
+ #
+ # # Добавляем блоки в прокручиваемую область
+ # scroll_layout.addWidget(self.create_block("Ид и основное", [
+ # "Ид", "Глаз", "Ид посещения", "Дата посещения", "Продолжительность заболевания"
+ # ]))
+ # scroll_layout.addWidget(self.create_block("Обследования", [
+ # "топкон", "оптопол", "критерий AREDS", "рефракция", "тип неоваскуляризации"
+ # ]))
+ # scroll_layout.addWidget(self.create_block("Хороидальная толщина", [
+ # "толщина хориоидеи в центре", "толщина ЦТС", "толщина ЦТС верхнего внутреннего слоя ямки",
+ # "толщина ЦТС верхнего наружного слоя ямки", "общий объем", "средний объем"
+ # ]))
+ # scroll_layout.addWidget(self.create_block("Состояние ЭПС", [
+ # "состояние ЭПС", "локализация ЭПС", "локализация ЦМЭ",
+ # "локализация серозного отслоения ЭПС", "ширина серозного отслоения ЭПС",
+ # "высота серозного отслоения ЭПС", "площадь серозного отслоения ЭПС"
+ # ]))
+ # scroll_layout.addWidget(self.create_block("Геморрагическое ЭПС", [
+ # "локализация геморрагического отслоения ЭПС", "ширина геморрагического отслоения ЭПС",
+ # "высота геморрагического отслоения ЭПС", "площадь геморрагического отслоения ЭПС"
+ # ]))
+ # scroll_layout.addWidget(self.create_block("Фиброваскулярное ЭПС", [
+ # "локализация фиброваскулярного отслоения ЭПС", "ширина фиброваскулярного отслоения ЭПС",
+ # "высота фиброваскулярного отслоения ЭПС", "площадь фиброваскулярного отслоения ЭПС"
+ # ]))
+ # scroll_layout.addWidget(self.create_block("Друзы и ЭПС", [
+ # "локализация друзеноидного отслоения ЭПС", "ширина друзеноидного отслоения ЭПС",
+ # "высота друзеноидного отслоения ЭПС", "площадь друзеноидного отслоения ЭПС",
+ # "локализация друз", "вес друз", "высота друз", "площадь друз"
+ # ]))
+ # scroll_layout.addWidget(self.create_block("Жидкость", [
+ # "площадь жидкости под ЭПС", "локализация жидкости под ЭПС"
+ # ]))
+ # scroll_layout.addWidget(self.create_block("зоны Эллера и прочее", [
+ # "состояние EZ", "локализация EZ", "состояние миоидной зоны", "локализация миоидной зоны"
+ # ]))
+ # scroll_layout.addWidget(self.create_block("сетчатки и гиперрефлективный материал", [
+ # "локализация отслоения РНЭ", "ширина отслоения РНЭ", "высота отслоения РНЭ",
+ # "площадь отслоения РНЭ", "локализация гиперотражающего материала",
+ # "площадь гиперотражающего материала"
+ # ]))
+ #
+ # # Устанавливаем макет для виджета
+ # scroll_content.setLayout(scroll_layout)
+ # scroll_area.setWidget(scroll_content)
+ #
+ # return scroll_area
+ #
+ # def create_block(self, title, fields):
+ # """Создание тематического блока с полями"""
+ # group_box = QGroupBox(title)
+ # form_layout = QFormLayout()
+ #
+ # # Добавляем поля
+ # for field in fields:
+ # form_layout.addRow(QLabel(field + ":"), QLineEdit())
+ #
+ # group_box.setLayout(form_layout)
+ # return group_box
+class PreviewWindow(QDialog):
+ """Окно для отображения полного изображения."""
+ def __init__(self, image_path):
+ super().__init__()
+ self.setWindowTitle("Полное изображение")
+ self.resize(800, 600)
+
+ layout = QVBoxLayout(self)
+
+ # QLabel для изображения
+ full_image_label = QLabel(self)
+ pixmap = QPixmap(image_path)
+ full_image_label.setPixmap(pixmap)
+ full_image_label.setScaledContents(True) # Масштабируем изображение
+ layout.addWidget(full_image_label)
\ No newline at end of file
diff --git a/src/bsmu/macula/plugins/db/__init__.py b/src/bsmu/macula/plugins/db/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/bsmu/macula/plugins/db/add_appointment_dialog.py b/src/bsmu/macula/plugins/db/add_appointment_dialog.py
new file mode 100644
index 0000000..22449aa
--- /dev/null
+++ b/src/bsmu/macula/plugins/db/add_appointment_dialog.py
@@ -0,0 +1,72 @@
+from functools import partial
+
+from PySide6.QtSql import QSqlQuery
+from PySide6.QtWidgets import QDialog, QLineEdit, QPushButton, QVBoxLayout, QLabel
+
+from bsmu.macula.plugins.db.database_manager import DatabaseManager
+
+
+class AddApponitmentDialog(QDialog):
+ def __init__(self, appotintment_id):
+ super().__init__()
+ self.db_manager = DatabaseManager()
+ self.is_new_appointment = appotintment_id == 0
+ self.setWindowTitle("Добавить прием" if self.is_new_appointment else "Изменить данные приема")
+
+ self.name_input = QLineEdit()
+ self.sex_input = QLineEdit()
+ self.age_input = QLineEdit()
+ self.save_button = QPushButton("Сохранить" if self.is_new_appointment else "Редактировать")
+ if (self.is_new_appointment):
+ self.save_button.clicked.connect(self.save_data)
+ else:
+ self.save_button.clicked.connect(partial(self.edit_data, appotintment_id))
+
+ layout = QVBoxLayout()
+ layout.addWidget(QLabel("Имя:"))
+ layout.addWidget(self.name_input)
+ layout.addWidget(QLabel("Пол:"))
+ layout.addWidget(self.sex_input)
+ layout.addWidget(QLabel("Год рождения:"))
+ layout.addWidget(self.age_input)
+ layout.addWidget(self.save_button)
+
+ if (not self.is_new_appointment):
+ record = self.db_manager.fetch_record_by_id("pacients", appotintment_id)
+ # query = QSqlQuery(self.db)
+ # query.prepare("SELECT id, name, sex, year_of_birthday FROM pacients WHERE id = :pacient_id")
+ # query.bindValue(":pacient_id", patient_id)
+ # query.exec_()
+ # self.appointments_model = QSqlQueryModel()
+ # self.appointments_model.setQuery(query)
+ self.name_input.setText(record[0][1])
+ self.sex_input.setText(record[0][2])
+ self.age_input.setText(str(record[0][3]))
+ self.setLayout(layout)
+
+ def save_data(self):
+ name = self.name_input.text()
+ sex = self.sex_input.text()
+ age = self.age_input.text()
+
+ if name and age.isdigit():
+ query = QSqlQuery(self.db)
+ query.prepare("INSERT INTO pacients (name, sex, year_of_birthday) VALUES (?, ?, ?)")
+ query.addBindValue(name)
+ query.addBindValue(sex)
+ query.addBindValue(int(age))
+ query.exec_()
+ self.close()
+ else:
+ print("Ошибка: введите корректные данные")
+
+ def edit_data(self, patient_id):
+ name = self.name_input.text()
+ sex = self.sex_input.text()
+ age = self.age_input.text()
+
+ if name and sex and age.isdigit():
+ self.db_manager.update_pacient(name, sex, int(age), patient_id)
+ self.close()
+ else:
+ print("Ошибка: введите корректные данные")
\ No newline at end of file
diff --git a/src/bsmu/macula/plugins/db/add_patient_dialog.py b/src/bsmu/macula/plugins/db/add_patient_dialog.py
new file mode 100644
index 0000000..c18b29b
--- /dev/null
+++ b/src/bsmu/macula/plugins/db/add_patient_dialog.py
@@ -0,0 +1,80 @@
+from functools import partial
+
+from PySide6.QtSql import QSqlQuery
+from PySide6.QtWidgets import QDialog, QLineEdit, QPushButton, QVBoxLayout, QLabel
+
+from bsmu.macula.plugins.db.database_manager import DatabaseManager
+
+
+class AddRecordDialog(QDialog):
+ def __init__(self, db, patient_id):
+ super().__init__()
+ self.db = db
+ self.db_manager = DatabaseManager()
+ self.is_new_user = patient_id == 0
+ self.setWindowTitle("Добавить пациента" if self.is_new_user else "Изменить данные пациента")
+
+ self.name_input = QLineEdit()
+ self.sex_input = QLineEdit()
+ self.age_input = QLineEdit()
+ self.save_button = QPushButton("Сохранить" if self.is_new_user else "Редактировать")
+ if (self.is_new_user):
+ self.save_button.clicked.connect(self.save_data)
+ else:
+ self.save_button.clicked.connect(partial(self.edit_data, patient_id))
+
+ layout = QVBoxLayout()
+ layout.addWidget(QLabel("Имя:"))
+ layout.addWidget(self.name_input)
+ layout.addWidget(QLabel("Пол:"))
+ layout.addWidget(self.sex_input)
+ layout.addWidget(QLabel("Год рождения:"))
+ layout.addWidget(self.age_input)
+ layout.addWidget(self.save_button)
+
+ if (not self.is_new_user):
+ record = self.db_manager.fetch_record_by_id("pacients", patient_id)
+ # query = QSqlQuery(self.db)
+ # query.prepare("SELECT id, name, sex, year_of_birthday FROM pacients WHERE id = :pacient_id")
+ # query.bindValue(":pacient_id", patient_id)
+ # query.exec_()
+ # self.appointments_model = QSqlQueryModel()
+ # self.appointments_model.setQuery(query)
+ self.name_input.setText(record[0][1])
+ self.sex_input.setText(record[0][2])
+ self.age_input.setText(str(record[0][3]))
+ self.setLayout(layout)
+
+ def save_data(self):
+ name = self.name_input.text()
+ sex = self.sex_input.text()
+ age = self.age_input.text()
+
+ if name and age.isdigit():
+ query = QSqlQuery(self.db)
+ query.prepare("INSERT INTO pacients (name, sex, year_of_birthday) VALUES (?, ?, ?)")
+ query.addBindValue(name)
+ query.addBindValue(sex)
+ query.addBindValue(int(age))
+ query.exec_()
+ self.close()
+ else:
+ print("Ошибка: введите корректные данные")
+
+ def edit_data(self, patient_id):
+ name = self.name_input.text()
+ sex = self.sex_input.text()
+ age = self.age_input.text()
+
+ if name and sex and age.isdigit():
+ self.db_manager.update_pacient(name, sex, int(age), patient_id)
+ # query = QSqlQuery(self.db)
+ # query.prepare("UPDATE pacients SET name = ? WHERE id = ?")
+ # query.addBindValue(name)
+ # query.addBindValue(sex)
+ # query.addBindValue(int(age))
+ # query.addBindValue(patient_id)
+ # bol = query.exec_()
+ self.close()
+ else:
+ print("Ошибка: введите корректные данные")
\ No newline at end of file
diff --git a/src/bsmu/macula/plugins/db/database_manager.py b/src/bsmu/macula/plugins/db/database_manager.py
new file mode 100644
index 0000000..196b730
--- /dev/null
+++ b/src/bsmu/macula/plugins/db/database_manager.py
@@ -0,0 +1,415 @@
+import sqlite3
+
+from PySide6.QtSql import QSqlQuery, QSqlDatabase
+from bsmu.vision.core.plugins import Plugin
+
+
+class DatabaseManager(Plugin):
+ CREATE_PATIENTS = '''CREATE TABLE IF NOT EXISTS pacients (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name VARCHAR(100) NOT NULL,
+ sex VARCHAR(1) NOT NULL,
+ year_of_birthday INTEGER NOT NULL
+)'''
+
+ CREATE_APPOINTMENTS = '''CREATE TABLE IF NOT EXISTS appointments (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ date DATE NOT NULL,
+ duration_of_the_disease INTEGER NOT NULL,
+ pacient_id INTEGER NOT NULL,
+ FOREIGN KEY (pacient_id) REFERENCES pacients (id)
+)'''
+
+ CREATE_EYES = '''CREATE TABLE IF NOT EXISTS eyes (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ eye VARCHAR(2) NOT NULL,
+ appointment_id INTEGER NOT NULL,
+ date DATE NOT NULL,
+ duration_of_the_disease INTEGER NOT NULL,
+ topkon BOOLEAN NOT NULL,
+ optopol BOOLEAN NOT NULL,
+ areds VARCHAR(10) NOT NULL,
+ refraction VARCHAR(10) NOT NULL,
+ type_of_neovascularization VARCHAR(10) NOT NULL,
+ "МКОЗ" NUMERIC NOT NULL,
+ choroidal_thickness_center NUMERIC NOT NULL,
+ cts_foveola NUMERIC NOT NULL,
+ cts_sup_inner_fovea NUMERIC NOT NULL,
+ cts_sup_out_fovea NUMERIC NOT NULL,
+ total_volume NUMERIC NOT NULL,
+ average_volume NUMERIC NOT NULL,
+ rpe_status NUMERIC NOT NULL,
+ rpe_localisation NUMERIC NOT NULL,
+ cme_localisation NUMERIC NOT NULL,
+ serouz_rpe_detachment_localisation NUMERIC NOT NULL,
+ serouz_rpe_detachment_width NUMERIC NOT NULL,
+ serouz_rpe_detachment_height NUMERIC NOT NULL,
+ serouz_rpe_detachment_area NUMERIC NOT NULL,
+ hemorrhagic_rpe_detachment_localisation NUMERIC NOT NULL,
+ hemorrhagic_rpe_detachment_width NUMERIC NOT NULL,
+ hemorrhagic_rpe_detachment_heidgt NUMERIC NOT NULL,
+ hemorrhagic_rpe_detachment_area NUMERIC NOT NULL,
+ fibrovascular_rpe_detachment_localisation NUMERIC NOT NULL,
+ fibrovascular_rpe_detachment_width NUMERIC NOT NULL,
+ fibrovascular_rpe_detachment_heidgt NUMERIC NOT NULL,
+ fibrovascular_rpe_detachment_area NUMERIC NOT NULL,
+ drusenoid_detachment_rpe_localisation NUMERIC NOT NULL,
+ drusenoid_detachment_rpe_width NUMERIC NOT NULL,
+ drusenoid_detachment_rpe_height NUMERIC NOT NULL,
+ drusenoid_detachment_rpe_area NUMERIC NOT NULL,
+ druses_localisation NUMERIC NOT NULL,
+ druses_weigt NUMERIC NOT NULL,
+ druses_heigt NUMERIC NOT NULL,
+ dzuses_area NUMERIC NOT NULL,
+ fluid_under_rpe_area NUMERIC NOT NULL,
+ fluid_under_rpe_localisation NUMERIC NOT NULL,
+ ez_status NUMERIC NOT NULL,
+ ez_localisation NUMERIC NOT NULL,
+ myoidnz_status NUMERIC NOT NULL,
+ myoidnz_localisation NUMERIC NOT NULL,
+ rne_detachment_localisation NUMERIC NOT NULL,
+ rne_detachment_width NUMERIC NOT NULL,
+ rne_detachment_heigt NUMERIC NOT NULL,
+ rne_detachment_area NUMERIC NOT NULL,
+ hyperreflective_material_localisation NUMERIC NOT NULL,
+ hyperreflective_material_area NUMERIC NOT NULL,
+ FOREIGN KEY (appointment_id) REFERENCES appointments (id)
+)'''
+
+ CREATE_MEDICINES = '''CREATE TABLE IF NOT EXISTS medicines (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ type VARCHAR(5) NOT NULL,
+ amount INTEGER NOT NULL,
+ appointment_id INTEGER NOT NULL,
+ FOREIGN KEY (appointment_id) REFERENCES appointments (id)
+)'''
+
+ # Pacients = "INSERT INTO pacients (name, sex, year_of_birthday) VALUES (?, ?, ?)"
+ Pacients = """INSERT INTO pacients (name, sex, year_of_birthday) VALUES ('Иван Иванов', 'М', 1990)
+,('Мария Смирнова', 'Ж', 1995)
+,('Алексей Петров', 'М', 1988)
+,('Екатерина Фролова', 'Ж', 1992)
+,('Дмитрий Сидоров', 'М', 1985)
+,('Анна Кузнецова', 'Ж', 1997)
+,('Сергей Васильев', 'М', 1982)
+,('Ольга Попова', 'Ж', 1990)
+,('Николай Орлов', 'М', 1989)
+,('Елена Михайлова', 'Ж', 1994);"""
+ # Appointments = "INSERT INTO appointments (date, duration_of_the_disease, pacient_id) VALUES (?, ?, ?)"
+ Appointments = """INSERT INTO appointments (date, duration_of_the_disease, pacient_id) VALUES ('2025-04-01', 10, 1)
+,('2025-04-15', 5, 1)
+
+,('2025-03-20', 14, 2)
+
+,('2025-02-28', 7, 3)
+,('2025-04-10', 3, 3)
+
+,('2025-01-15', 20, 4)
+
+,('2025-03-05', 11, 5)
+,('2025-04-22', 9, 5)
+
+,('2025-02-18', 15, 6)
+
+,('2025-03-01', 8, 7)
+,('2025-03-25', 6, 7)
+
+,('2025-04-03', 10, 8)
+
+,('2025-02-14', 18, 9)
+,('2025-03-30', 12, 9)
+
+,('2025-04-12', 7, 10);
+ """
+ Appointmen_1_1 = """INSERT INTO eyes (
+ eye, appointment_id, date, duration_of_the_disease, topkon, optopol, areds,
+ refraction, type_of_neovascularization, "МКОЗ", choroidal_thickness_center,
+ cts_foveola, cts_sup_inner_fovea, cts_sup_out_fovea, total_volume, average_volume,
+ rpe_status, rpe_localisation, cme_localisation, serouz_rpe_detachment_localisation,
+ serouz_rpe_detachment_width, serouz_rpe_detachment_height, serouz_rpe_detachment_area,
+ hemorrhagic_rpe_detachment_localisation, hemorrhagic_rpe_detachment_width,
+ hemorrhagic_rpe_detachment_heidgt, hemorrhagic_rpe_detachment_area,
+ fibrovascular_rpe_detachment_localisation, fibrovascular_rpe_detachment_width,
+ fibrovascular_rpe_detachment_heidgt, fibrovascular_rpe_detachment_area,
+ drusenoid_detachment_rpe_localisation, drusenoid_detachment_rpe_width,
+ drusenoid_detachment_rpe_height, drusenoid_detachment_rpe_area,
+ druses_localisation, druses_weigt, druses_heigt, dzuses_area,
+ fluid_under_rpe_area, fluid_under_rpe_localisation, ez_status, ez_localisation,
+ myoidnz_status, myoidnz_localisation, rne_detachment_localisation,
+ rne_detachment_width, rne_detachment_heigt, rne_detachment_area,
+ hyperreflective_material_localisation, hyperreflective_material_area
+) VALUES (
+ 'L', 1, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 1, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 2, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 2, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 3, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 3, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 4, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 4, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 5, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 5, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 6, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 6, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 7, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 7, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 8, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 1, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 9, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 9, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 10, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 10, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 11, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 11, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 12, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 12, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 13, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 13, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+),(
+ 'L', 14, '2025-04-15', 10, FALSE, TRUE, 'AREDS1',
+ '-2.0', 'Occult', 300, 160, 210, 190, 200, 9.0, 3.5,
+ 0, 2, 1, 1, 3.0, 2.0, 1.0,
+ 1, 1.5, 1.0, 0.5, 2, 3.5, 3.0, 1.5,
+ 1, 0.7, 0.5, 0.3, 3, 4.0, 2.5, 1.0,
+ 0.8, 2.0, 1, 2, 1, 1, 0,
+ 3.8, 2.0, 0, 0, 0
+),(
+ 'R', 14, '2025-04-16', 5, TRUE, FALSE, 'AREDS2',
+ '-1.5', 'Classic', 250, 150, 200, 180, 190, 8.5, 3.2,
+ 1, 1, 2, 0, 2.5, 1.5, 0.5,
+ 0, 1.2, 0.8, 0.3, 1, 2.8, 2.4, 1.0,
+ 0, 0.5, 0.3, 0.2, 2, 3.1, 1.8, 0.7,
+ 0.6, 1.5, 1, 2, 1, 1, 0,
+ 3.2, 1.5, 0, 0, 0
+);"""
+
+ def __init__(self, db_name="database.db"):
+ super().__init__()
+ self.db = QSqlDatabase.addDatabase("QSQLITE")
+ self.db.setDatabaseName(db_name)
+ self.connection = sqlite3.connect(db_name)
+ self.cursor = self.connection.cursor()
+ self.execute_query(self.CREATE_PATIENTS)
+ self.execute_query(self.CREATE_APPOINTMENTS)
+ self.execute_query(self.CREATE_EYES)
+ # self.execute_query(self.Pacients, )
+ # self.execute_query(self.Appointments, )
+ # self.execute_query(self.Appointmen_1_1)
+ self.close_connection()
+
+ def start_connection(self, db_name="database.db"):
+ self.connection = sqlite3.connect(db_name)
+ self.cursor = self.connection.cursor()
+
+ def execute_query(self, query, params=()):
+ try:
+ self.cursor.execute(query, params)
+ self.connection.commit()
+ except sqlite3.Error as e:
+ print(f"Ошибка выполнения запроса: {e}")
+
+ def fetch_results(self, query, params=()):
+ try:
+ self.cursor.execute(query, params)
+ return self.cursor.fetchall()
+ except sqlite3.Error as e:
+ print(f"Ошибка получения данных: {e}")
+ return []
+
+ def fetch_record_by_id(self, table, record_id):
+ self.start_connection()
+ query = "SELECT * FROM pacients WHERE id = :record_id"
+ params = (record_id)
+ self.cursor.execute(query, {"record_id": record_id})
+ return self.cursor.fetchall()
+
+ def update_pacient(self, name, sex, year_of_birthday, id):
+ self.start_connection()
+ query = "UPDATE pacients SET name = :name, sex = :sex, year_of_birthday = :year_of_birthday WHERE id = :id"
+ self.cursor.execute(query, {"name": name, "sex": sex, "year_of_birthday": year_of_birthday, "id": id,})
+ self.connection.commit()
+ self.close_connection()
+
+ def close_connection(self):
+ self.connection.close()
diff --git a/src/bsmu/macula/plugins/db/edit_pacirnt_delegate.py b/src/bsmu/macula/plugins/db/edit_pacirnt_delegate.py
new file mode 100644
index 0000000..5309cee
--- /dev/null
+++ b/src/bsmu/macula/plugins/db/edit_pacirnt_delegate.py
@@ -0,0 +1,20 @@
+from PySide6.QtGui import QIcon
+from PySide6.QtWidgets import QStyledItemDelegate, QWidget, QHBoxLayout, QPushButton, QMessageBox
+
+
+class EditPacientDelegate(QStyledItemDelegate):
+ def __init__(self, icon_path, parent=None, callback=None):
+ super().__init__(parent)
+ self.icon_path = icon_path
+ self.callback = callback # Функция, которая вызывается при нажатии кнопки
+
+ def paint(self, painter, option, index):
+ """Рисуем кнопку в ячейке"""
+ icon = QIcon(self.icon_path) # 🔹 Здесь нужна иконка (например, карандаш)
+ icon.paint(painter, option.rect)
+
+ def editorEvent(self, event, model, option, index):
+ """Обрабатываем нажатие на кнопку"""
+ if event.type() == event.Type.MouseButtonPress:
+ self.callback(model.data(model.index(index.row(), 0)))
+ return True
\ No newline at end of file
diff --git a/src/bsmu/macula/plugins/images/edit.png b/src/bsmu/macula/plugins/images/edit.png
new file mode 100644
index 0000000..6b44614
Binary files /dev/null and b/src/bsmu/macula/plugins/images/edit.png differ
diff --git a/src/bsmu/macula/records/eye_info_data.py b/src/bsmu/macula/records/eye_info_data.py
new file mode 100644
index 0000000..c0efc41
--- /dev/null
+++ b/src/bsmu/macula/records/eye_info_data.py
@@ -0,0 +1,126 @@
+from dataclasses import dataclass, field
+from datetime import date
+from typing import Optional
+
+from PySide6.QtSql import QSqlQueryModel
+
+
+@dataclass
+class Measurement:
+ """Базовый класс для измерений с локализацией и размерами"""
+ location: Optional[str] = None
+ width: Optional[float] = None
+ height: Optional[float] = None
+ area: Optional[float] = None
+
+ @classmethod
+ def from_model(cls, model: QSqlQueryModel, row: int,
+ loc_idx: int, w_idx: int, h_idx: int, a_idx: int):
+ return cls(
+ location=model.data(model.index(row, loc_idx)),
+ width=float(model.data(model.index(row, w_idx))) if model.data(model.index(row, w_idx)) else None,
+ height=float(model.data(model.index(row, h_idx))) if model.data(model.index(row, h_idx)) else None,
+ area=float(model.data(model.index(row, a_idx))) if model.data(model.index(row, a_idx)) else None
+ )
+
+@dataclass
+class ZoneStatus:
+ """Состояние анатомических зон"""
+ condition: Optional[str] = None
+ defect_location: Optional[str] = None
+
+ @classmethod
+ def from_model(cls, model: QSqlQueryModel, row: int,
+ cond_idx: int, loc_idx: int):
+ return cls(
+ condition=model.data(model.index(row, cond_idx)),
+ defect_location=model.data(model.index(row, loc_idx))
+ )
+
+@dataclass
+class PatientExamData:
+ # Основные метаданные
+ visit_date: Optional[date] = None
+ disease_duration: Optional[str] = None
+ tomograph_type: Optional[str] = "Топкон" # Добавлено поле типа томографа
+ areds_criteria: Optional[str] = None
+ refraction: Optional[str] = None
+ neovascularization_type: Optional[str] = None
+
+ # Общие измерения сетчатки
+ choroidal_center_thickness: Optional[float] = None
+ foveal_retinal_thickness: Optional[float] = None
+ total_retinal_volume: Optional[float] = None
+ average_retinal_volume: Optional[float] = None
+
+ # Состояние РПЭ
+ rpe_status: ZoneStatus = field(default_factory=ZoneStatus)
+ cmo_location: Optional[str] = None
+
+ # Типы ОПЭ
+ serous_ped: Measurement = field(default_factory=Measurement)
+ hemorrhagic_ped: Measurement = field(default_factory=Measurement)
+ fibrovascular_ped: Measurement = field(default_factory=Measurement)
+ drusenoid_ped: Measurement = field(default_factory=Measurement)
+
+ # Друзы
+ drusen: Measurement = field(default_factory=Measurement)
+
+ # Жидкость под РПЭ
+ sub_rpe_fluid: Measurement = field(default_factory=Measurement)
+
+ # Состояние зон
+ ellipsoid_zone: ZoneStatus = field(default_factory=ZoneStatus)
+ myoid_zone: ZoneStatus = field(default_factory=ZoneStatus)
+
+ # Патологии
+ nsr_detachment: Measurement = field(default_factory=Measurement)
+ hyperreflective_material: Measurement = field(default_factory=Measurement)
+
+ @classmethod
+ def from_query_model(cls, model: QSqlQueryModel, row: int):
+ def get_date(idx):
+ val = model.data(model.index(row, idx))
+ return val.toPyDate() if hasattr(val, 'toPyDate') else val
+
+ def get_str(idx):
+ val = model.data(model.index(row, idx))
+ return str(val) if val else None
+
+ def get_float(idx):
+ val = model.data(model.index(row, idx))
+ try:
+ return float(val) if val else None
+ except (ValueError, TypeError):
+ return None
+
+ return cls(
+ visit_date=get_date(3),
+ disease_duration=get_str(4),
+ tomograph_type="Топкон", # Установлено значение по умолчанию
+ areds_criteria=get_str(7),
+ refraction=get_str(8),
+ neovascularization_type=get_str(9),
+ choroidal_center_thickness=get_float(10),
+ foveal_retinal_thickness=get_float(11),
+ total_retinal_volume=get_float(14),
+ average_retinal_volume=get_float(15),
+ rpe_status=ZoneStatus.from_model(model, row, 16, 17),
+ cmo_location=get_str(18),
+ serous_ped=Measurement.from_model(model, row, 19, 20, 21, 22),
+ hemorrhagic_ped=Measurement.from_model(model, row, 23, 24, 25, 26),
+ fibrovascular_ped=Measurement.from_model(model, row, 27, 28, 29, 30),
+ drusenoid_ped=Measurement.from_model(model, row, 31, 32, 33, 34),
+ drusen=Measurement.from_model(model, row, 35, 36, 37, 38),
+ sub_rpe_fluid=Measurement(
+ area=get_float(39),
+ location=get_str(40)
+ ),
+ ellipsoid_zone=ZoneStatus.from_model(model, row, 41, 42),
+ myoid_zone=ZoneStatus.from_model(model, row, 43, 44),
+ nsr_detachment=Measurement.from_model(model, row, 45, 46, 47, 48),
+ hyperreflective_material=Measurement(
+ location=get_str(49),
+ area=get_float(50)
+ )
+ )
\ No newline at end of file
diff --git a/src/bsmu/macula/records/patient.py b/src/bsmu/macula/records/patient.py
new file mode 100644
index 0000000..ce23e1d
--- /dev/null
+++ b/src/bsmu/macula/records/patient.py
@@ -0,0 +1,9 @@
+from dataclasses import dataclass
+
+
+@dataclass
+class Patient:
+ id: int
+ name: str
+ sex: str
+ year_of_birthday: int
diff --git a/src/bsmu/macula/version.py b/src/bsmu/macula/version.py
index 93b60a1..8411e55 100644
--- a/src/bsmu/macula/version.py
+++ b/src/bsmu/macula/version.py
@@ -1 +1 @@
-__version__ = '0.5.1'
+__version__ = '0.6.1'
diff --git a/src/bsmu/macula/widgets/eye_data_widget.py b/src/bsmu/macula/widgets/eye_data_widget.py
new file mode 100644
index 0000000..f77440b
--- /dev/null
+++ b/src/bsmu/macula/widgets/eye_data_widget.py
@@ -0,0 +1,120 @@
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QWidget, QVBoxLayout, QGroupBox, QFormLayout, QLabel, QScrollArea
+
+from bsmu.macula.records.eye_info_data import PatientExamData
+
+
+class EyeDataWidget(QWidget):
+ def __init__(self, eye_data: PatientExamData, parent=None):
+ super().__init__(parent)
+ self.eye_data = eye_data
+ self.init_ui()
+
+ def init_ui(self):
+ layout = QVBoxLayout(self)
+ scroll_area = self.create_scrollable_area()
+ layout.addWidget(scroll_area)
+ self.setLayout(layout)
+
+ def create_block(self, title, fields):
+ """Создание блока с формой для группы полей"""
+ group_box = QGroupBox(title)
+ form_layout = QFormLayout()
+
+ for label_text, field_value in fields:
+ label = QLabel(label_text)
+ value = str(field_value) if field_value is not None else ""
+ value_label = QLabel(value)
+ value_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
+ form_layout.addRow(label, value_label)
+
+ group_box.setLayout(form_layout)
+ return group_box
+
+ def create_scrollable_area(self):
+ """Создаёт область прокрутки с блоками данных"""
+ scroll_area = QScrollArea()
+ scroll_area.setWidgetResizable(True)
+
+ scroll_content = QWidget()
+ scroll_layout = QVBoxLayout()
+
+ scroll_layout.addWidget(self.create_block("Обследование", [
+ ("Дата посещения", self.eye_data.visit_date),
+ ("Продолжительность заболевания", self.eye_data.disease_duration),
+ ("Тип томографа ", self.eye_data.tomograph_type),
+ ("Критерий AREDS", self.eye_data.areds_criteria),
+ ("Рефракция", self.eye_data.refraction),
+ ("Тип неоваскуляризации", self.eye_data.neovascularization_type)
+ ]))
+ scroll_layout.addWidget(self.create_block("Ретинальные показатели", [
+ ("Толщина хориоидеи в центре", self.eye_data.choroidal_center_thickness),
+ ("Толщина сетчатки в фовеоле", self.eye_data.foveal_retinal_thickness),
+ ("Общий объем", self.eye_data.total_retinal_volume),
+ ("Средний объем", self.eye_data.average_retinal_volume)
+ ]))
+ scroll_layout.addWidget(self.create_block("", [
+ ("Состояние РПЭ", self.eye_data.rpe_status.condition),
+ ("Локализация дефектов РПЭ", self.eye_data.rpe_status.defect_location),
+ ("Локализация кистозного макулярного отека", self.eye_data.cmo_location)
+ ]))
+ scroll_layout.addWidget(QLabel("Отслойки РПЭ"))
+ scroll_layout.addWidget(self.create_block("Серозная ОПЭ", [
+ ("Локализация", self.eye_data.serous_ped.location),
+ ("Ширина", self.eye_data.serous_ped.width),
+ ("Высота", self.eye_data.serous_ped.height),
+ ("Площадь", self.eye_data.serous_ped.area)
+ ]))
+ scroll_layout.addWidget(self.create_block("Геморрагическая ОПЭ", [
+ ("Локализация", self.eye_data.hemorrhagic_ped.location),
+ ("Ширина", self.eye_data.hemorrhagic_ped.width),
+ ("Высота", self.eye_data.hemorrhagic_ped.height),
+ ("Площадь", self.eye_data.hemorrhagic_ped.area)
+ ]))
+ scroll_layout.addWidget(self.create_block("Фиброваскулярная ОПЭ", [
+ ("Локализация", self.eye_data.fibrovascular_ped.location),
+ ("Ширина", self.eye_data.fibrovascular_ped.width),
+ ("Высота", self.eye_data.fibrovascular_ped.height),
+ ("Площадь", self.eye_data.fibrovascular_ped.area)
+ ]))
+ scroll_layout.addWidget(self.create_block("Друзеноидная ОПЭ", [
+ ("Локализация", self.eye_data.drusenoid_ped.location),
+ ("Ширина", self.eye_data.drusenoid_ped.width),
+ ("Высота", self.eye_data.drusenoid_ped.height),
+ ("Площадь", self.eye_data.drusenoid_ped.area)
+ ]))
+ scroll_layout.addWidget(self.create_block("Друзы", [
+ ("Локализация", self.eye_data.drusen.location),
+ ("Ширина", self.eye_data.drusen.width),
+ ("Высота", self.eye_data.drusen.height),
+ ("Площадь", self.eye_data.drusen.area)
+ ]))
+ scroll_layout.addWidget(self.create_block("Жидкость под РПЭ", [
+ ("Пощадь", self.eye_data.sub_rpe_fluid.area),
+ ("Локализация", self.eye_data.sub_rpe_fluid.location)
+ ]))
+ scroll_layout.addWidget(self.create_block("Эллипсоидная зона", [
+ ("Состояние", self.eye_data.ellipsoid_zone.condition),
+ ("Локализация дефектов", self.eye_data.ellipsoid_zone.defect_location)
+ ]))
+ scroll_layout.addWidget(self.create_block("Миоидная зона", [
+ ("Состояние", self.eye_data.myoid_zone.condition),
+ ("Локализация дефектов", self.eye_data.myoid_zone.defect_location)
+ ]))
+ scroll_layout.addWidget(self.create_block("Отслойка нейросенсорной сетчатки", [
+ ("Локализация", self.eye_data.nsr_detachment.location),
+ ("Ширина", self.eye_data.nsr_detachment.width),
+ ("Высота", self.eye_data.nsr_detachment.height),
+ ("Площадь", self.eye_data.nsr_detachment.area)
+ ]))
+ scroll_layout.addWidget(self.create_block("Гиперрефлективный материал", [
+ ("Локализация", self.eye_data.hyperreflective_material.location),
+ ("Площадь", self.eye_data.hyperreflective_material.area)
+ ]))
+
+ # Добавьте остальные блоки по аналогии...
+
+ scroll_content.setLayout(scroll_layout)
+ scroll_area.setWidget(scroll_content)
+
+ return scroll_area
\ No newline at end of file