fuck yeah!
This commit is contained in:
parent
ccb53d9091
commit
da10f5e132
44 changed files with 3260 additions and 448 deletions
149
views/canvas_widget.py
Normal file
149
views/canvas_widget.py
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
"""
|
||||
Виджет для отображения и манипуляции солнечными изображениями
|
||||
Поддерживает: зум, панорамирование, композитинг слоев
|
||||
"""
|
||||
|
||||
from PySide6.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPixmapItem
|
||||
from PySide6.QtCore import Qt, QRectF, QPointF, Signal
|
||||
from PySide6.QtGui import QPixmap, QImage, QWheelEvent, QMouseEvent, QPainter
|
||||
import numpy as np
|
||||
|
||||
|
||||
class SolarCanvas(QGraphicsView):
|
||||
"""
|
||||
Кастомный Canvas для отображения солнечных изображений
|
||||
Поддерживает масштабирование, панорамирование и наложение слоев
|
||||
"""
|
||||
|
||||
zoom_changed = Signal(float)
|
||||
|
||||
def __init__(self, controller):
|
||||
super().__init__()
|
||||
self.controller = controller
|
||||
self.scene = QGraphicsScene(self)
|
||||
self.setScene(self.scene)
|
||||
|
||||
# Настройки отображения - ИСПРАВЛЕНО: используем QPainter
|
||||
self.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||
self.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)
|
||||
self.setDragMode(self.DragMode.ScrollHandDrag)
|
||||
self.setTransformationAnchor(self.ViewportAnchor.AnchorUnderMouse)
|
||||
self.setResizeAnchor(self.ViewportAnchor.AnchorUnderMouse)
|
||||
|
||||
# Переменные для зума
|
||||
self.current_zoom = 1.0
|
||||
self.min_zoom = 0.1
|
||||
self.max_zoom = 10.0
|
||||
self.zoom_factor = 1.1
|
||||
|
||||
# Словарь для хранения слоев (QGraphicsPixmapItem)
|
||||
self.layer_items = {}
|
||||
|
||||
# Фон для лучшего контраста (черный для астрономических изображений)
|
||||
self.setBackgroundBrush(Qt.GlobalColor.black)
|
||||
|
||||
# Дополнительные настройки для плавности
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
|
||||
def set_image(self, layer_id: int, image_data: np.ndarray):
|
||||
"""
|
||||
Устанавливает изображение для слоя
|
||||
|
||||
Args:
|
||||
layer_id: ID слоя
|
||||
image_data: numpy массив с изображением
|
||||
"""
|
||||
# Конвертируем numpy array в QImage
|
||||
height, width = image_data.shape[:2]
|
||||
|
||||
if len(image_data.shape) == 2:
|
||||
# Черно-белое изображение
|
||||
bytes_per_line = width
|
||||
qimage = QImage(image_data.data, width, height, bytes_per_line, QImage.Format.Format_Grayscale8)
|
||||
else:
|
||||
# RGB изображение
|
||||
bytes_per_line = 3 * width
|
||||
qimage = QImage(image_data.data, width, height, bytes_per_line, QImage.Format.Format_RGB888)
|
||||
|
||||
# Конвертируем в QPixmap
|
||||
pixmap = QPixmap.fromImage(qimage)
|
||||
|
||||
# Создаем или обновляем графический элемент
|
||||
if layer_id in self.layer_items:
|
||||
self.layer_items[layer_id].setPixmap(pixmap)
|
||||
else:
|
||||
item = self.scene.addPixmap(pixmap)
|
||||
self.layer_items[layer_id] = item
|
||||
|
||||
# Центрируем изображение при первом добавлении
|
||||
if len(self.layer_items) == 1:
|
||||
self.centerOn(item)
|
||||
self.fitInView(item, Qt.AspectRatioMode.KeepAspectRatio)
|
||||
|
||||
def set_layer_visibility(self, layer_id: int, visible: bool):
|
||||
"""Устанавливает видимость слоя"""
|
||||
if layer_id in self.layer_items:
|
||||
self.layer_items[layer_id].setVisible(visible)
|
||||
|
||||
def set_layer_opacity(self, layer_id: int, opacity: float):
|
||||
"""Устанавливает прозрачность слоя"""
|
||||
if layer_id in self.layer_items:
|
||||
self.layer_items[layer_id].setOpacity(opacity)
|
||||
|
||||
def remove_layer(self, layer_id: int):
|
||||
"""Удаляет слой"""
|
||||
if layer_id in self.layer_items:
|
||||
self.scene.removeItem(self.layer_items[layer_id])
|
||||
del self.layer_items[layer_id]
|
||||
|
||||
def clear_all_layers(self):
|
||||
"""Очищает все слои"""
|
||||
for item in self.layer_items.values():
|
||||
self.scene.removeItem(item)
|
||||
self.layer_items.clear()
|
||||
|
||||
def wheelEvent(self, event: QWheelEvent):
|
||||
"""Обработка колесика мыши для зума"""
|
||||
# Определяем направление зума
|
||||
zoom_in = event.angleDelta().y() > 0
|
||||
|
||||
# Вычисляем новый уровень зума
|
||||
if zoom_in:
|
||||
new_zoom = self.current_zoom * self.zoom_factor
|
||||
else:
|
||||
new_zoom = self.current_zoom / self.zoom_factor
|
||||
|
||||
# Ограничиваем зум
|
||||
if self.min_zoom <= new_zoom <= self.max_zoom:
|
||||
self.current_zoom = new_zoom
|
||||
self.scale(self.zoom_factor if zoom_in else 1/self.zoom_factor,
|
||||
self.zoom_factor if zoom_in else 1/self.zoom_factor)
|
||||
self.zoom_changed.emit(self.current_zoom)
|
||||
|
||||
def mousePressEvent(self, event: QMouseEvent):
|
||||
"""Обработка нажатия мыши для панорамирования"""
|
||||
if event.button() == Qt.MouseButton.MiddleButton:
|
||||
self.setDragMode(self.DragMode.ScrollHandDrag)
|
||||
# Создаем фейковое событие с левой кнопкой
|
||||
fake_event = QMouseEvent(
|
||||
event.type(), event.pos(), Qt.MouseButton.LeftButton,
|
||||
Qt.MouseButton.LeftButton, event.modifiers()
|
||||
)
|
||||
super().mousePressEvent(fake_event)
|
||||
else:
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event: QMouseEvent):
|
||||
"""Обработка отпускания мыши"""
|
||||
if event.button() == Qt.MouseButton.MiddleButton:
|
||||
self.setDragMode(self.DragMode.NoDrag)
|
||||
super().mouseReleaseEvent(event)
|
||||
|
||||
def reset_view(self):
|
||||
"""Сбрасывает зум и позицию"""
|
||||
if self.layer_items:
|
||||
first_item = next(iter(self.layer_items.values()))
|
||||
self.fitInView(first_item, Qt.AspectRatioMode.KeepAspectRatio)
|
||||
self.current_zoom = 1.0
|
||||
self.zoom_changed.emit(self.current_zoom)
|
||||
Loading…
Add table
Add a link
Reference in a new issue