| 
							
								 | 
							
							import sys | 
						
						
						
						
							 | 
							
								 | 
							
							import time | 
						
						
						
						
							 | 
							
								 | 
							
							import os | 
						
						
						
						
							 | 
							
								 | 
							
							from PyQt6.QtWidgets import (QMainWindow, QVBoxLayout, QWidget, | 
						
						
						
						
							 | 
							
								 | 
							
							                             QSystemTrayIcon, QMenu, QApplication, QHBoxLayout, QPushButton) | 
						
						
						
						
							 | 
							
								 | 
							
							from PyQt6.QtCore import Qt, pyqtSignal, QTimer, QPropertyAnimation, QEasingCurve, QPoint | 
						
						
						
						
							 | 
							
								 | 
							
							from PyQt6.QtGui import QFont, QPainter, QIcon, QAction, QPixmap, QDragEnterEvent, QDropEvent, QMouseEvent | 
						
						
						
						
							 | 
							
								 | 
							
							import uiautomation as auto | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							from ai_worker import AIWorker | 
						
						
						
						
							 | 
							
								 | 
							
							from drag_drop_handler import DragDropHandler | 
						
						
						
						
							 | 
							
								 | 
							
							from icon_window import IconWindow | 
						
						
						
						
							 | 
							
								 | 
							
							from main_interface import MainInterface | 
						
						
						
						
							 | 
							
								 | 
							
							import qtawesome as qta | 
						
						
						
						
							 | 
							
								 | 
							
							import pyperclip | 
						
						
						
						
							 | 
							
								 | 
							
							import keyboard | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							class TransparentWindow(QMainWindow): | 
						
						
						
						
							 | 
							
								 | 
							
							    text_to_insert = pyqtSignal(str) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def __init__(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        super().__init__() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.is_visible = False  # Видимость окна (скрыто/показано) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.is_expanded = False  # Ширина окна (узкое/широкое) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.hook_manager = None | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.dragging = False | 
						
						
						
						
							 | 
							
								 | 
							
							        self.drag_offset = QPoint() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.drag_n_drop_active = False | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.setup_ui() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.setup_tray() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.setup_focus_monitor() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.hide()  # Изначально окно скрыто | 
						
						
						
						
							 | 
							
								 | 
							
							        self.last_focus_state = False | 
						
						
						
						
							 | 
							
								 | 
							
							        self.last_active_timestamp = time.time() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Инициализация AI worker | 
						
						
						
						
							 | 
							
								 | 
							
							        self.ai_worker = AIWorker() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.ai_worker.text_processed.connect(self.on_text_processed) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.ai_worker.image_processed.connect(self.on_image_processed) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.ai_worker.processing_finished.connect(self.on_processing_finished) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.ai_worker.start() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Инициализация drag-drop handler | 
						
						
						
						
							 | 
							
								 | 
							
							        self.drag_drop_handler = DragDropHandler(self) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Переменные состояния | 
						
						
						
						
							 | 
							
								 | 
							
							        self.current_complaints_text = "" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.current_history_text = "" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.current_image_path = "" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.is_processing = False | 
						
						
						
						
							 | 
							
								 | 
							
							        self.pending_text_type = None | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Настройка drag-and-drop | 
						
						
						
						
							 | 
							
								 | 
							
							        self.setAcceptDrops(True) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def setup_ui(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Настраивает основной интерфейс окна""" | 
						
						
						
						
							 | 
							
								 | 
							
							        # Устанавливаем прозрачность и убираем рамку | 
						
						
						
						
							 | 
							
								 | 
							
							        self.setWindowFlags(Qt.WindowType.FramelessWindowHint | | 
						
						
						
						
							 | 
							
								 | 
							
							                            Qt.WindowType.WindowStaysOnTopHint | | 
						
						
						
						
							 | 
							
								 | 
							
							                            Qt.WindowType.Tool) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Получаем размеры экрана | 
						
						
						
						
							 | 
							
								 | 
							
							        screen_geometry = QApplication.primaryScreen().geometry() | 
						
						
						
						
							 | 
							
								 | 
							
							        screen_width = screen_geometry.width() | 
						
						
						
						
							 | 
							
								 | 
							
							        screen_height = screen_geometry.height() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Размеры окон | 
						
						
						
						
							 | 
							
								 | 
							
							        self.collapsed_width = 70 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.expanded_width = 200 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.window_height = 300 | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Позиционируем основное окно слева по центру | 
						
						
						
						
							 | 
							
								 | 
							
							        main_x_position = 10 | 
						
						
						
						
							 | 
							
								 | 
							
							        main_y_position = (screen_height - self.window_height) // 2 | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Устанавливаем начальную позицию основного окна (свернутое состояние) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.setGeometry(main_x_position, main_y_position, self.collapsed_width, self.window_height) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Создаем центральный виджет | 
						
						
						
						
							 | 
							
								 | 
							
							        self.central_widget = QWidget() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.central_widget.setObjectName("CentralWidget") | 
						
						
						
						
							 | 
							
								 | 
							
							        self.central_widget.setAcceptDrops(True) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.setCentralWidget(self.central_widget) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.central_widget.mousePressEvent = self.mouse_press_event | 
						
						
						
						
							 | 
							
								 | 
							
							        self.central_widget.mouseMoveEvent = self.mouse_move_event | 
						
						
						
						
							 | 
							
								 | 
							
							        self.central_widget.mouseReleaseEvent = self.mouse_release_event | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Основной layout | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_layout = QVBoxLayout(self.central_widget) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_layout.setContentsMargins(0, 0, 0, 0) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_layout.setSpacing(0) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.top_layout = QHBoxLayout() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.top_layout.setContentsMargins(4, 4, 4, 0) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.top_layout.setSpacing(0) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.top_layout.addStretch() | 
						
						
						
						
							 | 
							
								 | 
							
							        icn = qta.icon("ph.gear-six-bold", color=('#FFFFFF', 255)) | 
						
						
						
						
							 | 
							
								 | 
							
							        button_1 = QPushButton(icn, "") | 
						
						
						
						
							 | 
							
								 | 
							
							        button_1.setStyleSheet(""" | 
						
						
						
						
							 | 
							
								 | 
							
							                        QPushButton { | 
						
						
						
						
							 | 
							
								 | 
							
							                            color: #FFFFFF; | 
						
						
						
						
							 | 
							
								 | 
							
							                            background-color: rgba(0, 0, 0, 0); | 
						
						
						
						
							 | 
							
								 | 
							
							                            padding: 4px 4px; | 
						
						
						
						
							 | 
							
								 | 
							
							                            border-radius: 5px; | 
						
						
						
						
							 | 
							
								 | 
							
							                            font-size: 14px; | 
						
						
						
						
							 | 
							
								 | 
							
							                            font-weight: 650; | 
						
						
						
						
							 | 
							
								 | 
							
							                            margin: 0px 3px; | 
						
						
						
						
							 | 
							
								 | 
							
							                            text-align: left; | 
						
						
						
						
							 | 
							
								 | 
							
							                            max-width: 16px; | 
						
						
						
						
							 | 
							
								 | 
							
							                        } | 
						
						
						
						
							 | 
							
								 | 
							
							                        QPushButton:hover { | 
						
						
						
						
							 | 
							
								 | 
							
							                            background-color: rgba(0, 0, 0, 100); | 
						
						
						
						
							 | 
							
								 | 
							
							                        } | 
						
						
						
						
							 | 
							
								 | 
							
							                        QPushButton:pressed { | 
						
						
						
						
							 | 
							
								 | 
							
							                            background-color: rgba(0, 0, 0, 220); | 
						
						
						
						
							 | 
							
								 | 
							
							                        } | 
						
						
						
						
							 | 
							
								 | 
							
							                        QPushButton:disabled { | 
						
						
						
						
							 | 
							
								 | 
							
							                            color: #888888; | 
						
						
						
						
							 | 
							
								 | 
							
							                        } | 
						
						
						
						
							 | 
							
								 | 
							
							                    """) | 
						
						
						
						
							 | 
							
								 | 
							
							        icn = qta.icon("ph.push-pin-bold", color=('#FFFFFF', 255)) | 
						
						
						
						
							 | 
							
								 | 
							
							        button_2 = QPushButton(icn, "") | 
						
						
						
						
							 | 
							
								 | 
							
							        button_2.setStyleSheet(""" | 
						
						
						
						
							 | 
							
								 | 
							
							                        QPushButton { | 
						
						
						
						
							 | 
							
								 | 
							
							                            color: #FFFFFF; | 
						
						
						
						
							 | 
							
								 | 
							
							                            background-color: rgba(0, 0, 0, 0); | 
						
						
						
						
							 | 
							
								 | 
							
							                            padding: 4px 4px; | 
						
						
						
						
							 | 
							
								 | 
							
							                            border-radius: 5px; | 
						
						
						
						
							 | 
							
								 | 
							
							                            font-size: 14px; | 
						
						
						
						
							 | 
							
								 | 
							
							                            font-weight: 650; | 
						
						
						
						
							 | 
							
								 | 
							
							                            margin: 0px 3px; | 
						
						
						
						
							 | 
							
								 | 
							
							                            text-align: left; | 
						
						
						
						
							 | 
							
								 | 
							
							                            max-width: 16px; | 
						
						
						
						
							 | 
							
								 | 
							
							                        } | 
						
						
						
						
							 | 
							
								 | 
							
							                        QPushButton:hover { | 
						
						
						
						
							 | 
							
								 | 
							
							                            background-color: rgba(0, 0, 0, 100); | 
						
						
						
						
							 | 
							
								 | 
							
							                        } | 
						
						
						
						
							 | 
							
								 | 
							
							                        QPushButton:pressed { | 
						
						
						
						
							 | 
							
								 | 
							
							                            background-color: rgba(0, 0, 0, 220); | 
						
						
						
						
							 | 
							
								 | 
							
							                        } | 
						
						
						
						
							 | 
							
								 | 
							
							                        QPushButton:disabled { | 
						
						
						
						
							 | 
							
								 | 
							
							                            color: #888888; | 
						
						
						
						
							 | 
							
								 | 
							
							                        } | 
						
						
						
						
							 | 
							
								 | 
							
							                    """) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.top_layout.addWidget(button_1) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.top_layout.addWidget(button_2) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_layout.addLayout(self.top_layout) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Создаем и показываем иконку | 
						
						
						
						
							 | 
							
								 | 
							
							        self.icon_window = IconWindow(self) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.icon_window.icon_clicked.connect(self.toggle_window_visibility) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.icon_window.show_icon() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # .addLayout(self.central_widget) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Создаем основной интерфейс | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface = MainInterface(self) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.button_clicked.connect(self.on_button_click) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.hide()  # Изначально скрыт | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_layout.addWidget(self.main_interface) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.central_widget.setStyleSheet(""" | 
						
						
						
						
							 | 
							
								 | 
							
							            #CentralWidget { | 
						
						
						
						
							 | 
							
								 | 
							
							                background: qlineargradient( | 
						
						
						
						
							 | 
							
								 | 
							
							                    x1:0, y1:0, x2:0, y2:1, | 
						
						
						
						
							 | 
							
								 | 
							
							                    stop:0 rgba(30, 30, 30, 220), | 
						
						
						
						
							 | 
							
								 | 
							
							                    stop:1 rgba(40, 40, 40, 240) | 
						
						
						
						
							 | 
							
								 | 
							
							                ); | 
						
						
						
						
							 | 
							
								 | 
							
							                border-radius: 15px; | 
						
						
						
						
							 | 
							
								 | 
							
							            } | 
						
						
						
						
							 | 
							
								 | 
							
							        """) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.text_to_insert.connect(self.insert_text) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def mouse_press_event(self, event: QMouseEvent): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обработчик нажатия мыши на иконку""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if event.button() == Qt.MouseButton.LeftButton: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.dragging = True | 
						
						
						
						
							 | 
							
								 | 
							
							            self.drag_offset = event.position().toPoint() | 
						
						
						
						
							 | 
							
								 | 
							
							        event.accept() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def mouse_move_event(self, event: QMouseEvent): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обработчик движения мыши для иконки""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if self.dragging and event.buttons() & Qt.MouseButton.LeftButton: | 
						
						
						
						
							 | 
							
								 | 
							
							            global_pos = self.central_widget.mapToGlobal(event.position().toPoint()) | 
						
						
						
						
							 | 
							
								 | 
							
							            new_pos = global_pos - self.drag_offset + QPoint(-10,-10) | 
						
						
						
						
							 | 
							
								 | 
							
							            self.move(new_pos) | 
						
						
						
						
							 | 
							
								 | 
							
							        event.accept() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def mouse_release_event(self, event: QMouseEvent): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обработчик отпускания мыши для иконки""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if event.button() == Qt.MouseButton.LeftButton and self.dragging: | 
						
						
						
						
							 | 
							
								 | 
							
							            # Решение конфликта событий mouse_press_event и clicked | 
						
						
						
						
							 | 
							
								 | 
							
							            self.dragging = False | 
						
						
						
						
							 | 
							
								 | 
							
							        event.accept() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def toggle_window_visibility(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Переключает видимость окна (скрыть/показать)""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if self.is_visible: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.hide_window() | 
						
						
						
						
							 | 
							
								 | 
							
							        else: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.show_window() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def show_window(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Показывает окно в узком состоянии""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if self.is_visible: | 
						
						
						
						
							 | 
							
								 | 
							
							            return | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.is_visible = True | 
						
						
						
						
							 | 
							
								 | 
							
							        self.show() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.raise_() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Показываем основной интерфейс | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.show() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Устанавливаем узкую ширину и показываем compact drop область | 
						
						
						
						
							 | 
							
								 | 
							
							        # self.resize(self.collapsed_width, self.window_height) | 
						
						
						
						
							 | 
							
								 | 
							
							        # self.main_interface.hide_buttons() | 
						
						
						
						
							 | 
							
								 | 
							
							        # self.main_interface.show_compact_drop_area() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.expand_for_text_field() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def hide_window(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Полностью скрывает окно""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if not self.is_visible: | 
						
						
						
						
							 | 
							
								 | 
							
							            return | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.is_visible = False | 
						
						
						
						
							 | 
							
								 | 
							
							        self.hide() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.hide_all_drop_areas() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def expand_for_drag_drop(self, mime_data): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Расширяет окно для drag-n-drop""" | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        if not self.is_visible: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.show_window() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        if not self.is_expanded: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.animate_window_width(self.expanded_width) | 
						
						
						
						
							 | 
							
								 | 
							
							            self.is_expanded = True | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Показываем соответствующие drop области (без кнопок) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.hide_buttons() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.hide_all_drop_areas() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        if mime_data.hasUrls(): | 
						
						
						
						
							 | 
							
								 | 
							
							            urls = mime_data.urls() | 
						
						
						
						
							 | 
							
								 | 
							
							            if urls and urls[0].isLocalFile(): | 
						
						
						
						
							 | 
							
								 | 
							
							                file_path = urls[0].toLocalFile() | 
						
						
						
						
							 | 
							
								 | 
							
							                if self.drag_drop_handler.is_image_file(file_path): | 
						
						
						
						
							 | 
							
								 | 
							
							                    self.main_interface.show_image_drop_interface() | 
						
						
						
						
							 | 
							
								 | 
							
							                else: | 
						
						
						
						
							 | 
							
								 | 
							
							                    self.main_interface.show_text_drop_interface() | 
						
						
						
						
							 | 
							
								 | 
							
							            else: | 
						
						
						
						
							 | 
							
								 | 
							
							                urls = mime_data.urls() | 
						
						
						
						
							 | 
							
								 | 
							
							                file_path = urls[0] | 
						
						
						
						
							 | 
							
								 | 
							
							                self.main_interface.show_image_drop_interface() | 
						
						
						
						
							 | 
							
								 | 
							
							        elif mime_data.hasText(): | 
						
						
						
						
							 | 
							
								 | 
							
							            self.main_interface.show_text_drop_interface() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def expand_for_text_field(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Расширяет окно для работы с текстовым полем""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if not self.is_visible: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.show_window() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        if not self.is_expanded: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.animate_window_width(self.expanded_width) | 
						
						
						
						
							 | 
							
								 | 
							
							            self.is_expanded = True | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Показываем кнопки (без drop областей) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.show_buttons() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.hide_all_drop_areas() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def collapse_to_compact(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Сворачивает окно до компактного состояния""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if not self.is_expanded: | 
						
						
						
						
							 | 
							
								 | 
							
							            return | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.animate_window_width(self.collapsed_width) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.is_expanded = False | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # Показываем compact drop область | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.hide_buttons() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.show_compact_drop_area() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def animate_window_width(self, target_width, callback=None): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Анимирует изменение ширины окна""" | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        # self.resize(target_width, self.window_height) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        if callback: | 
						
						
						
						
							 | 
							
								 | 
							
							            callback() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def show_drag_drop_interface(self, mime_data): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Показывает интерфейс для drag-n-drop""" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.expand_for_drag_drop(mime_data) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def handle_dropped_image(self, image_path): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обрабатывает перетащенное изображение""" | 
						
						
						
						
							 | 
							
								 | 
							
							        try: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.current_image_path = image_path | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            print("⚠️ NEW Image. URL:", image_path) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            # Запускаем обработку изображения | 
						
						
						
						
							 | 
							
								 | 
							
							            self.set_processing_state(True) | 
						
						
						
						
							 | 
							
								 | 
							
							            self.main_interface.buttons[2].setText(" ЛОР-Статус (обработка...)") | 
						
						
						
						
							 | 
							
								 | 
							
							            self.main_interface.buttons[3].setText(" Заключение ИИ (обработка...)") | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            self.ai_worker.process_image(image_path) | 
						
						
						
						
							 | 
							
								 | 
							
							            self.main_interface.show_compact_drop_area() | 
						
						
						
						
							 | 
							
								 | 
							
							        except Exception as e: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.show_error(f"Не удалось загрузить изображение: {str(e)}") | 
						
						
						
						
							 | 
							
								 | 
							
							            self.set_processing_state(False) | 
						
						
						
						
							 | 
							
								 | 
							
							            self.main_interface.show_compact_drop_area() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def handle_dropped_text(self, text, drop_type, y_position): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обрабатывает перетащенный текст""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if not text.strip() and drop_type == 'file_drop': | 
						
						
						
						
							 | 
							
								 | 
							
							            try: | 
						
						
						
						
							 | 
							
								 | 
							
							                text = "Текст из перетащенного файла" | 
						
						
						
						
							 | 
							
								 | 
							
							            except: | 
						
						
						
						
							 | 
							
								 | 
							
							                text = "Не удалось прочитать файл" | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        if y_position < self.window_height/2: | 
						
						
						
						
							 | 
							
								 | 
							
							            # Обрабатываем как жалобы | 
						
						
						
						
							 | 
							
								 | 
							
							            self.start_text_processing(text, 'complaints') | 
						
						
						
						
							 | 
							
								 | 
							
							        else: | 
						
						
						
						
							 | 
							
								 | 
							
							            # Обрабатываем как жалобы | 
						
						
						
						
							 | 
							
								 | 
							
							            self.start_text_processing(text, 'history') | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.show_compact_drop_area() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def start_text_processing(self, text, processing_type): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Запускает обработку текста""" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.set_processing_state(True) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        if processing_type == 'complaints': | 
						
						
						
						
							 | 
							
								 | 
							
							            self.main_interface.buttons[0].setText(" Обработка...") | 
						
						
						
						
							 | 
							
								 | 
							
							            self.pending_text_type = 'complaints' | 
						
						
						
						
							 | 
							
								 | 
							
							        else: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.main_interface.buttons[1].setText(" Обработка...") | 
						
						
						
						
							 | 
							
								 | 
							
							            self.pending_text_type = 'history' | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.ai_worker.process_text(text, processing_type) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def set_processing_state(self, processing): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Устанавливает состояние обработки""" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.is_processing = processing | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.set_buttons_enabled(not processing) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.set_processing_state(processing) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def on_text_processed(self, result, processing_type): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обработчик завершения обработки текста""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if processing_type == 'complaints': | 
						
						
						
						
							 | 
							
								 | 
							
							            self.current_complaints_text = result | 
						
						
						
						
							 | 
							
								 | 
							
							            self.main_interface.buttons[0].setText(" Вставить жалобы (ИИ)") | 
						
						
						
						
							 | 
							
								 | 
							
							        else: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.current_history_text = result | 
						
						
						
						
							 | 
							
								 | 
							
							            self.main_interface.buttons[1].setText(" Вставить анамнез (ИИ)") | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def on_image_processed(self, result): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обработчик завершения обработки изображения""" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.current_image_path = result | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.buttons[2].setText(" ЛОР-Статус (ИИ)") | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.buttons[3].setText(" Заключение ИИ (готово)") | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def on_processing_finished(self, processing_type): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обработчик завершения любой обработки""" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.set_processing_state(False) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    # Drag and Drop events | 
						
						
						
						
							 | 
							
								 | 
							
							    def dragEnterEvent(self, event: QDragEnterEvent): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обработчик события перетаскивания в окно""" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.drag_drop_handler.handle_drag_enter(event) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.drag_n_drop_active = True | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def dragLeaveEvent(self, event): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обработчик события выхода перетаскивания из окна""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if self.is_visible and not self.is_processing: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.collapse_to_compact() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.drag_n_drop_active = False | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def dropEvent(self, event: QDropEvent): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обработчик события отпускания перетаскиваемого объекта""" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.drag_drop_handler.handle_drop(event) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.drag_n_drop_active = False | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def setup_tray(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Настраиваем системный трей""" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.tray_icon = QSystemTrayIcon(self) | 
						
						
						
						
							 | 
							
								 | 
							
							        pixmap = QPixmap(16, 16) | 
						
						
						
						
							 | 
							
								 | 
							
							        pixmap.fill(Qt.GlobalColor.blue) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.tray_icon.setIcon(QIcon(pixmap)) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.tray_icon.setToolTip("Input Helper\nАвтоматическая панель для ввода текста") | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        tray_menu = QMenu() | 
						
						
						
						
							 | 
							
								 | 
							
							        show_action = QAction("Показать панель", self) | 
						
						
						
						
							 | 
							
								 | 
							
							        show_action.triggered.connect(self.show_window) | 
						
						
						
						
							 | 
							
								 | 
							
							        hide_action = QAction("Скрыть панель", self) | 
						
						
						
						
							 | 
							
								 | 
							
							        hide_action.triggered.connect(self.hide_window) | 
						
						
						
						
							 | 
							
								 | 
							
							        quit_action = QAction("Выход", self) | 
						
						
						
						
							 | 
							
								 | 
							
							        quit_action.triggered.connect(self.quit_application) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        tray_menu.addAction(show_action) | 
						
						
						
						
							 | 
							
								 | 
							
							        tray_menu.addAction(hide_action) | 
						
						
						
						
							 | 
							
								 | 
							
							        tray_menu.addSeparator() | 
						
						
						
						
							 | 
							
								 | 
							
							        tray_menu.addAction(quit_action) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.tray_icon.setContextMenu(tray_menu) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.tray_icon.activated.connect(self.tray_icon_activated) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.tray_icon.show() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def is_editable_field(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        try: | 
						
						
						
						
							 | 
							
								 | 
							
							            focused = auto.GetFocusedControl() | 
						
						
						
						
							 | 
							
								 | 
							
							            if not focused: | 
						
						
						
						
							 | 
							
								 | 
							
							                return False | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            control_type = focused.ControlType | 
						
						
						
						
							 | 
							
								 | 
							
							            if control_type in [auto.ControlType.EditControl, auto.ControlType.ComboBoxControl, | 
						
						
						
						
							 | 
							
								 | 
							
							                                auto.ControlType.TextControl]: | 
						
						
						
						
							 | 
							
								 | 
							
							                return True | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            return False | 
						
						
						
						
							 | 
							
								 | 
							
							        except Exception as e: | 
						
						
						
						
							 | 
							
								 | 
							
							            return False | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def setup_focus_monitor(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Мониторинг фокуса ввода через таймер""" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.focus_timer = QTimer() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.focus_timer.timeout.connect(self.check_focused_control) | 
						
						
						
						
							 | 
							
								 | 
							
							        self.focus_timer.start(200) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def check_focused_control(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Проверяет, есть ли фокус на редактируемом поле""" | 
						
						
						
						
							 | 
							
								 | 
							
							        try: | 
						
						
						
						
							 | 
							
								 | 
							
							            if self.drag_n_drop_active: | 
						
						
						
						
							 | 
							
								 | 
							
							                return 0 | 
						
						
						
						
							 | 
							
								 | 
							
							            current_focus_state = self.is_editable_field() | 
						
						
						
						
							 | 
							
								 | 
							
							            if current_focus_state: | 
						
						
						
						
							 | 
							
								 | 
							
							                self.last_active_timestamp = time.time() | 
						
						
						
						
							 | 
							
								 | 
							
							                if not self.last_focus_state: | 
						
						
						
						
							 | 
							
								 | 
							
							                    # При фокусе на текстовом поле - расширяем окно и показываем кнопки | 
						
						
						
						
							 | 
							
								 | 
							
							                    self.expand_for_text_field() | 
						
						
						
						
							 | 
							
								 | 
							
							                else: | 
						
						
						
						
							 | 
							
								 | 
							
							                    if self.geometry().width() < self.expanded_width: | 
						
						
						
						
							 | 
							
								 | 
							
							                        self.animate_window_width(self.expanded_width) | 
						
						
						
						
							 | 
							
								 | 
							
							                        self.is_expanded = True | 
						
						
						
						
							 | 
							
								 | 
							
							                    self.main_interface.show_buttons() | 
						
						
						
						
							 | 
							
								 | 
							
							                    self.main_interface.hide_all_drop_areas() | 
						
						
						
						
							 | 
							
								 | 
							
							            else: | 
						
						
						
						
							 | 
							
								 | 
							
							                if time.time() - self.last_active_timestamp > 5: | 
						
						
						
						
							 | 
							
								 | 
							
							                    self.last_active_timestamp = time.time() | 
						
						
						
						
							 | 
							
								 | 
							
							                    if not self.last_focus_state: | 
						
						
						
						
							 | 
							
								 | 
							
							                        # При фокусе на текстовом поле - расширяем окно и показываем кнопки | 
						
						
						
						
							 | 
							
								 | 
							
							                        self.expand_for_text_field() | 
						
						
						
						
							 | 
							
								 | 
							
							                    else: | 
						
						
						
						
							 | 
							
								 | 
							
							                        if self.geometry().width() < self.expanded_width: | 
						
						
						
						
							 | 
							
								 | 
							
							                            self.animate_window_width(self.expanded_width) | 
						
						
						
						
							 | 
							
								 | 
							
							                            self.is_expanded = True | 
						
						
						
						
							 | 
							
								 | 
							
							                        self.main_interface.show_buttons() | 
						
						
						
						
							 | 
							
								 | 
							
							                        self.main_interface.hide_all_drop_areas() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            self.last_focus_state = current_focus_state | 
						
						
						
						
							 | 
							
								 | 
							
							        except Exception: | 
						
						
						
						
							 | 
							
								 | 
							
							            pass | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def on_button_click(self, index): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обработчик клика по кнопке""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if index == len(self.main_interface.buttons) - 1:  # Последняя кнопка - скрыть | 
						
						
						
						
							 | 
							
								 | 
							
							            self.restart_form() | 
						
						
						
						
							 | 
							
								 | 
							
							        elif index == 0:  # Кнопка жалоб | 
						
						
						
						
							 | 
							
								 | 
							
							            self.handle_complaints_button() | 
						
						
						
						
							 | 
							
								 | 
							
							        elif index == 1:  # Кнопка анамнеза | 
						
						
						
						
							 | 
							
								 | 
							
							            self.handle_history_button() | 
						
						
						
						
							 | 
							
								 | 
							
							        elif index == 2:  # ЛОР-Статус | 
						
						
						
						
							 | 
							
								 | 
							
							            if "(ИИ)" in self.main_interface.buttons[2].text() and self.current_image_path: | 
						
						
						
						
							 | 
							
								 | 
							
							                self.insert_text(self.current_image_path) | 
						
						
						
						
							 | 
							
								 | 
							
							            else: | 
						
						
						
						
							 | 
							
								 | 
							
							                self.insert_text("""Отоскопия (эндоскопия уха): | 
						
						
						
						
							 | 
							
								 | 
							
							Правое ухо: Наружный слуховой проход широкий, кожа розовая, чистая. Барабанная перепонка перламутрово-серого цвета, опознавательные знаки четко дифференцируются. | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							Левое ухо: Визуализируется обтурирующая масса в костном отделе наружного слухового прохода.""") | 
						
						
						
						
							 | 
							
								 | 
							
							        elif index == 3:  # Заключение ИИ | 
						
						
						
						
							 | 
							
								 | 
							
							            if "(готово)" in self.main_interface.buttons[3].text() and self.current_image_path: | 
						
						
						
						
							 | 
							
								 | 
							
							                self.insert_text(self.current_image_path) | 
						
						
						
						
							 | 
							
								 | 
							
							            else: | 
						
						
						
						
							 | 
							
								 | 
							
							                self.insert_text("H68.0 Воспаление слуховой (евстахиевой) трубы") | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def handle_complaints_button(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обрабатывает нажатие кнопки жалоб""" | 
						
						
						
						
							 | 
							
								 | 
							
							        button = self.main_interface.buttons[0] | 
						
						
						
						
							 | 
							
								 | 
							
							        text = button.text() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        if "Копировать" in text: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.copy_current_text('complaints') | 
						
						
						
						
							 | 
							
								 | 
							
							        elif "Вставить" in text and self.current_complaints_text: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.insert_text(self.current_complaints_text) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def handle_history_button(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обрабатывает нажатие кнопки анамнеза""" | 
						
						
						
						
							 | 
							
								 | 
							
							        button = self.main_interface.buttons[1] | 
						
						
						
						
							 | 
							
								 | 
							
							        text = button.text() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        if "Копировать" in text: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.copy_current_text('history') | 
						
						
						
						
							 | 
							
								 | 
							
							        elif "Вставить" in text and self.current_history_text: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.insert_text(self.current_history_text) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def copy_current_text(self, processing_type): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Копирует текст из активного поля и запускает обработку""" | 
						
						
						
						
							 | 
							
								 | 
							
							        try: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.hide_window() | 
						
						
						
						
							 | 
							
								 | 
							
							            self.is_visible = False | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            if processing_type == 'complaints': | 
						
						
						
						
							 | 
							
								 | 
							
							                self.main_interface.buttons[0].setText(" Обработка...") | 
						
						
						
						
							 | 
							
								 | 
							
							            else: | 
						
						
						
						
							 | 
							
								 | 
							
							                self.main_interface.buttons[1].setText(" Обработка...") | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            self.set_processing_state(True) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            QTimer.singleShot(50, lambda: self._do_copy_and_process(processing_type)) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        except Exception as e: | 
						
						
						
						
							 | 
							
								 | 
							
							            print(f"Ошибка при копировании: {e}") | 
						
						
						
						
							 | 
							
								 | 
							
							            self.set_processing_state(False) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    # def _do_copy_and_process(self, processing_type): | 
						
						
						
						
							 | 
							
								 | 
							
							    #     """Копирует текст и запускает обработку""" | 
						
						
						
						
							 | 
							
								 | 
							
							    #     try: | 
						
						
						
						
							 | 
							
								 | 
							
							    # | 
						
						
						
						
							 | 
							
								 | 
							
							    #         original_text = pyperclip.paste() | 
						
						
						
						
							 | 
							
								 | 
							
							    # | 
						
						
						
						
							 | 
							
								 | 
							
							    #         keyboard.press_and_release('ctrl+a') | 
						
						
						
						
							 | 
							
								 | 
							
							    #         time.sleep(0.3) | 
						
						
						
						
							 | 
							
								 | 
							
							    #         keyboard.press_and_release('ctrl+c') | 
						
						
						
						
							 | 
							
								 | 
							
							    #         time.sleep(0.3) | 
						
						
						
						
							 | 
							
								 | 
							
							    #         keyboard.press_and_release('end') | 
						
						
						
						
							 | 
							
								 | 
							
							    #         time.sleep(0.3) | 
						
						
						
						
							 | 
							
								 | 
							
							    # | 
						
						
						
						
							 | 
							
								 | 
							
							    #         copied_text = pyperclip.paste() | 
						
						
						
						
							 | 
							
								 | 
							
							    #         # pyperclip.copy(original_text) | 
						
						
						
						
							 | 
							
								 | 
							
							    # | 
						
						
						
						
							 | 
							
								 | 
							
							    #         print(copied_text) | 
						
						
						
						
							 | 
							
								 | 
							
							    #         if copied_text: #and copied_text != original_text | 
						
						
						
						
							 | 
							
								 | 
							
							    #             self.ai_worker.process_text(copied_text, processing_type) | 
						
						
						
						
							 | 
							
								 | 
							
							    #         else: | 
						
						
						
						
							 | 
							
								 | 
							
							    #             if processing_type == 'complaints': | 
						
						
						
						
							 | 
							
								 | 
							
							    #                 self.main_interface.buttons[0].setText(" Копировать жалобы") | 
						
						
						
						
							 | 
							
								 | 
							
							    #             else: | 
						
						
						
						
							 | 
							
								 | 
							
							    #                 self.main_interface.buttons[1].setText(" Копировать анамнез") | 
						
						
						
						
							 | 
							
								 | 
							
							    #             print("Не удалось скопировать текст из активного поля") | 
						
						
						
						
							 | 
							
								 | 
							
							    #             self.show_error("Не удалось скопировать текст из активного поля") | 
						
						
						
						
							 | 
							
								 | 
							
							    #             self.set_processing_state(False) | 
						
						
						
						
							 | 
							
								 | 
							
							    # | 
						
						
						
						
							 | 
							
								 | 
							
							    #         QTimer.singleShot(100, self.show_window) | 
						
						
						
						
							 | 
							
								 | 
							
							    # | 
						
						
						
						
							 | 
							
								 | 
							
							    #     except Exception as e: | 
						
						
						
						
							 | 
							
								 | 
							
							    #         print(f"Ошибка при работе с pyperclip: {e}") | 
						
						
						
						
							 | 
							
								 | 
							
							    #         self.set_processing_state(False) | 
						
						
						
						
							 | 
							
								 | 
							
							    #         QTimer.singleShot(100, self.show_window) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def _do_copy_and_process(self, processing_type): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Копирует выделенный текст из любого окна (включая Chrome) через UI Automation""" | 
						
						
						
						
							 | 
							
								 | 
							
							        try: | 
						
						
						
						
							 | 
							
								 | 
							
							            copied_text = None | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            if auto is not None: | 
						
						
						
						
							 | 
							
								 | 
							
							                try: | 
						
						
						
						
							 | 
							
								 | 
							
							                    selected_text = self._get_selected_text_uia() | 
						
						
						
						
							 | 
							
								 | 
							
							                    if selected_text: | 
						
						
						
						
							 | 
							
								 | 
							
							                        copied_text = selected_text | 
						
						
						
						
							 | 
							
								 | 
							
							                except Exception as e: | 
						
						
						
						
							 | 
							
								 | 
							
							                    print(f"UIA сбой: {e}") | 
						
						
						
						
							 | 
							
								 | 
							
							                    copied_text = None | 
						
						
						
						
							 | 
							
								 | 
							
							            print("Auto:", copied_text) | 
						
						
						
						
							 | 
							
								 | 
							
							            # Fallback: если UIA недоступен или не сработал — используем SendInput | 
						
						
						
						
							 | 
							
								 | 
							
							            if not copied_text: | 
						
						
						
						
							 | 
							
								 | 
							
							                copied_text = self._copy_via_sendinput() | 
						
						
						
						
							 | 
							
								 | 
							
							                print("SendInput:", copied_text) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            # Обработка результата | 
						
						
						
						
							 | 
							
								 | 
							
							            if copied_text: | 
						
						
						
						
							 | 
							
								 | 
							
							                self.ai_worker.process_text(copied_text, processing_type) | 
						
						
						
						
							 | 
							
								 | 
							
							            else: | 
						
						
						
						
							 | 
							
								 | 
							
							                if processing_type == 'complaints': | 
						
						
						
						
							 | 
							
								 | 
							
							                    self.main_interface.buttons[0].setText(" Копировать жалобы") | 
						
						
						
						
							 | 
							
								 | 
							
							                else: | 
						
						
						
						
							 | 
							
								 | 
							
							                    self.main_interface.buttons[1].setText(" Копировать анамнез") | 
						
						
						
						
							 | 
							
								 | 
							
							                print("Не удалось скопировать текст из активного поля") | 
						
						
						
						
							 | 
							
								 | 
							
							                self.show_error("Не удалось скопировать текст из активного поля") | 
						
						
						
						
							 | 
							
								 | 
							
							                self.set_processing_state(False) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            QTimer.singleShot(100, self.show_window) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        except Exception as e: | 
						
						
						
						
							 | 
							
								 | 
							
							            print(f"Ошибка при работе с pyperclip: {e}") | 
						
						
						
						
							 | 
							
								 | 
							
							            self.set_processing_state(False) | 
						
						
						
						
							 | 
							
								 | 
							
							            QTimer.singleShot(100, self.show_window) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def _get_selected_text_uia(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Получает весь текст из активного редактируемого поля через UI Automation""" | 
						
						
						
						
							 | 
							
								 | 
							
							        try: | 
						
						
						
						
							 | 
							
								 | 
							
							            focused = auto.GetFocusedControl() | 
						
						
						
						
							 | 
							
								 | 
							
							            if not focused: | 
						
						
						
						
							 | 
							
								 | 
							
							                return None | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            # Игнорируем известные "пустышки" | 
						
						
						
						
							 | 
							
								 | 
							
							            if focused.ClassName == "IHWindowClass" and "Input Capture" in (focused.Name or ""): | 
						
						
						
						
							 | 
							
								 | 
							
							                # RDP: невозможно получить текст локально | 
						
						
						
						
							 | 
							
								 | 
							
							                return None | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            # Пытаемся получить TextPattern (даже у ComboBoxControl в Chrome он есть!) | 
						
						
						
						
							 | 
							
								 | 
							
							            text_pattern = focused.GetTextPattern() | 
						
						
						
						
							 | 
							
								 | 
							
							            if text_pattern is not None: | 
						
						
						
						
							 | 
							
								 | 
							
							                try: | 
						
						
						
						
							 | 
							
								 | 
							
							                    full_range = text_pattern.DocumentRange | 
						
						
						
						
							 | 
							
								 | 
							
							                    if full_range: | 
						
						
						
						
							 | 
							
								 | 
							
							                        full_text = full_range.GetText(-1) | 
						
						
						
						
							 | 
							
								 | 
							
							                        if isinstance(full_text, str): | 
						
						
						
						
							 | 
							
								 | 
							
							                            return full_text.strip() | 
						
						
						
						
							 | 
							
								 | 
							
							                except Exception as e: | 
						
						
						
						
							 | 
							
								 | 
							
							                    print(f"Ошибка при получении DocumentRange: {e}") | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            # Fallback: если TextPattern недоступен, пробуем Value (осторожно!) | 
						
						
						
						
							 | 
							
								 | 
							
							            if hasattr(focused, 'LegacyIAccessible'): | 
						
						
						
						
							 | 
							
								 | 
							
							                acc = focused.LegacyIAccessible | 
						
						
						
						
							 | 
							
								 | 
							
							                value = getattr(acc, 'Value', None) | 
						
						
						
						
							 | 
							
								 | 
							
							                if isinstance(value, str) and value.strip(): | 
						
						
						
						
							 | 
							
								 | 
							
							                    # Дополнительная проверка: если Name похож на placeholder — не доверяем Value | 
						
						
						
						
							 | 
							
								 | 
							
							                    # Но в большинстве нативных приложений Value = реальный текст | 
						
						
						
						
							 | 
							
								 | 
							
							                    return value.strip() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        except Exception as e: | 
						
						
						
						
							 | 
							
								 | 
							
							            print(f"UIA: ошибка получения текста: {e}") | 
						
						
						
						
							 | 
							
								 | 
							
							        return None | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def _copy_via_sendinput(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        try: | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            # Сохраняем оригинальный буфер | 
						
						
						
						
							 | 
							
								 | 
							
							            original_clipboard = pyperclip.paste() | 
						
						
						
						
							 | 
							
								 | 
							
							            # Очищаем буфер перед копированием (чтобы точно знать, что скопировалось) | 
						
						
						
						
							 | 
							
								 | 
							
							            pyperclip.copy("") | 
						
						
						
						
							 | 
							
								 | 
							
							            time.sleep(0.15) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            # Выделяем всё и копируем | 
						
						
						
						
							 | 
							
								 | 
							
							            keyboard.press_and_release('ctrl+a', do_press=True, do_release=False) | 
						
						
						
						
							 | 
							
								 | 
							
							            time.sleep(0.2) | 
						
						
						
						
							 | 
							
								 | 
							
							            keyboard.press_and_release('ctrl+a', do_press=False, do_release=True) | 
						
						
						
						
							 | 
							
								 | 
							
							            time.sleep(0.2) | 
						
						
						
						
							 | 
							
								 | 
							
							            keyboard.press_and_release('ctrl+c', do_press=True, do_release=False) | 
						
						
						
						
							 | 
							
								 | 
							
							            time.sleep(0.2) | 
						
						
						
						
							 | 
							
								 | 
							
							            keyboard.press_and_release('ctrl+c', do_press=False, do_release=True) | 
						
						
						
						
							 | 
							
								 | 
							
							            time.sleep(0.4)  # Достаточно для большинства случаев | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            # # Перемещаем курсор в конец (опционально) | 
						
						
						
						
							 | 
							
								 | 
							
							            # keyboard.press_and_release('end') | 
						
						
						
						
							 | 
							
								 | 
							
							            time.sleep(0.15) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            # Получаем результат | 
						
						
						
						
							 | 
							
								 | 
							
							            copied_text = pyperclip.paste().strip() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            # Восстанавливаем оригинальный буфер | 
						
						
						
						
							 | 
							
								 | 
							
							            pyperclip.copy(original_clipboard) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            if copied_text: | 
						
						
						
						
							 | 
							
								 | 
							
							                return copied_text | 
						
						
						
						
							 | 
							
								 | 
							
							            else: | 
						
						
						
						
							 | 
							
								 | 
							
							                return None | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        except Exception as e: | 
						
						
						
						
							 | 
							
								 | 
							
							            print(f"Ошибка при копировании: {e}") | 
						
						
						
						
							 | 
							
								 | 
							
							            return None | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def insert_text(self, text): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Вставляет текст в активное поле ввода""" | 
						
						
						
						
							 | 
							
								 | 
							
							        try: | 
						
						
						
						
							 | 
							
								 | 
							
							            self.hide_window() | 
						
						
						
						
							 | 
							
								 | 
							
							            self.is_visible = False | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            if text: | 
						
						
						
						
							 | 
							
								 | 
							
							                QTimer.singleShot(50, lambda: self._do_insert_pyperclip(text)) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        except Exception as e: | 
						
						
						
						
							 | 
							
								 | 
							
							            print(f"Ошибка при вставке: {e}") | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def _do_insert_pyperclip(self, text): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Вставка текста с использованием pyperclip""" | 
						
						
						
						
							 | 
							
								 | 
							
							        try: | 
						
						
						
						
							 | 
							
								 | 
							
							            import pyperclip | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            original_text = pyperclip.paste() | 
						
						
						
						
							 | 
							
								 | 
							
							            pyperclip.copy(text) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							            QTimer.singleShot(30, lambda: self._paste_and_restore(original_text)) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        except Exception as e: | 
						
						
						
						
							 | 
							
								 | 
							
							            print(f"Ошибка при работе с pyperclip: {e}") | 
						
						
						
						
							 | 
							
								 | 
							
							            QTimer.singleShot(100, self.show_window) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def _paste_and_restore(self, original_text): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Вставляет текст и восстанавливает буфер""" | 
						
						
						
						
							 | 
							
								 | 
							
							        try: | 
						
						
						
						
							 | 
							
								 | 
							
							            keyboard.press_and_release('ctrl+v') | 
						
						
						
						
							 | 
							
								 | 
							
							            QTimer.singleShot(100, lambda: pyperclip.copy(original_text)) | 
						
						
						
						
							 | 
							
								 | 
							
							            QTimer.singleShot(100, self.show_window) | 
						
						
						
						
							 | 
							
								 | 
							
							        except Exception as e: | 
						
						
						
						
							 | 
							
								 | 
							
							            print(f"Ошибка при вставке: {e}") | 
						
						
						
						
							 | 
							
								 | 
							
							            QTimer.singleShot(100, self.show_window) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def show_error(self, message): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Показывает сообщение об ошибке""" | 
						
						
						
						
							 | 
							
								 | 
							
							        print(f"Ошибка: {message}") | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def tray_icon_activated(self, reason): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обработчик активации иконки в трее""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if reason == QSystemTrayIcon.ActivationReason.DoubleClick: | 
						
						
						
						
							 | 
							
								 | 
							
							            if self.is_visible: | 
						
						
						
						
							 | 
							
								 | 
							
							                self.hide_window() | 
						
						
						
						
							 | 
							
								 | 
							
							            else: | 
						
						
						
						
							 | 
							
								 | 
							
							                self.show_window() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def quit_application(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Корректный выход из приложения""" | 
						
						
						
						
							 | 
							
								 | 
							
							        if self.hook_manager: | 
						
						
						
						
							 | 
							
								 | 
							
							            try: | 
						
						
						
						
							 | 
							
								 | 
							
							                self.hook_manager.UnhookMouse() | 
						
						
						
						
							 | 
							
								 | 
							
							            except: | 
						
						
						
						
							 | 
							
								 | 
							
							                pass | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        if self.ai_worker.isRunning(): | 
						
						
						
						
							 | 
							
								 | 
							
							            self.ai_worker.stop() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.tray_icon.hide() | 
						
						
						
						
							 | 
							
								 | 
							
							        QApplication.quit() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def paintEvent(self, event): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Отрисовывает тень вокруг окна""" | 
						
						
						
						
							 | 
							
								 | 
							
							        painter = QPainter(self) | 
						
						
						
						
							 | 
							
								 | 
							
							        painter.setRenderHint(QPainter.RenderHint.Antialiasing) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        shadow_margin = 8 | 
						
						
						
						
							 | 
							
								 | 
							
							        shadow_rect = self.rect().adjusted( | 
						
						
						
						
							 | 
							
								 | 
							
							            shadow_margin, | 
						
						
						
						
							 | 
							
								 | 
							
							            shadow_margin, | 
						
						
						
						
							 | 
							
								 | 
							
							            -shadow_margin, | 
						
						
						
						
							 | 
							
								 | 
							
							            -shadow_margin | 
						
						
						
						
							 | 
							
								 | 
							
							        ) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        painter.setPen(Qt.PenStyle.NoPen) | 
						
						
						
						
							 | 
							
								 | 
							
							        painter.setBrush(Qt.GlobalColor.black) | 
						
						
						
						
							 | 
							
								 | 
							
							        painter.setOpacity(0.2) | 
						
						
						
						
							 | 
							
								 | 
							
							        painter.drawRoundedRect(shadow_rect, 15, 15) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        painter.end() | 
						
						
						
						
							 | 
							
								 | 
							
							        super().paintEvent(event) | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def closeEvent(self, event): | 
						
						
						
						
							 | 
							
								 | 
							
							        """Обработчик закрытия окна""" | 
						
						
						
						
							 | 
							
								 | 
							
							        event.ignore() | 
						
						
						
						
							 | 
							
								 | 
							
							        self.hide_window() | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							    def restart_form(self): | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.buttons[0].setText(" Копировать жалобы") | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.buttons[1].setText(" Копировать анамнез") | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.buttons[2].setText(" ЛОР-Статус") | 
						
						
						
						
							 | 
							
								 | 
							
							        self.main_interface.buttons[3].setText(" Заключение ИИ") | 
						
						
						
						
							 | 
							
								 | 
							
							 | 
						
						
						
						
							 | 
							
								 | 
							
							        self.current_complaints_text = "" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.current_history_text = "" | 
						
						
						
						
							 | 
							
								 | 
							
							        self.current_image_path = ""
 | 
						
						
						
						
							 | 
							
								 | 
							
							
 |