from PyQt6.QtWidgets import QLabel, QVBoxLayout, QHBoxLayout, QLineEdit, QCheckBox, QSlider from PyQt6.QtGui import QIntValidator from PyQt6.QtCore import Qt import numpy as np import cv2 from .ImageParameterDialog import ImageParameterDialog from .. import ImageProcessingWorker class RotationDialog(ImageParameterDialog): def __init__(self, image): super().__init__(image, ImageProcessingWorker.ImageProcessingWorker) self.setWindowTitle("Rotation") self.layout = QVBoxLayout() self.angle_label = QLabel("Angle:") self.angle_field = QLineEdit() self.angle_field.setText("0") self.angle_field.setPlaceholderText("Enter rotation angle") self.angle_field.setValidator(QIntValidator(-360, 360)) self.angle_field.textEdited.connect(self.angle_text_changed) self.angle_slider = QSlider(Qt.Orientation.Horizontal) self.angle_slider.setRange(-360, 360) self.angle_slider.setTickInterval(90) self.angle_slider.setTickPosition(QSlider.TickPosition.TicksBelow) self.angle_slider.valueChanged.connect(self.angle_slider_changed) self.keep_size_checkbox = QCheckBox("Keep original size") self.keep_size_checkbox.setChecked(True) self.keep_size_checkbox.stateChanged.connect(self.update) input_layout = QHBoxLayout() input_layout.addWidget(self.angle_label) input_layout.addWidget(self.angle_field) self.layout.addLayout(input_layout) self.layout.addWidget(self.angle_slider) self.layout.addWidget(self.keep_size_checkbox) self.layout.addWidget(self.button_box) self.setLayout(self.layout) def angle_text_changed(self, text): if text: angle = int(text) self.angle_slider.setValue(angle) self.update() def angle_slider_changed(self, value): self.angle_field.setText(str(value)) self.update() def update(self): angle = self.angle_field.text() if not angle: self.set_accept_enable(False) return self.send_to_process({ 'angle': float(angle), 'keep_size': self.keep_size_checkbox.isChecked() }) def process_image(self, image, values): angle = values['angle'] keep_size = values['keep_size'] height, width = image.shape[:2] center = (width // 2, height // 2) matrix = cv2.getRotationMatrix2D(center, angle, 1.0) if keep_size: rotated_image = cv2.warpAffine(image, matrix, (width, height)) else: cos = np.abs(matrix[0, 0]) sin = np.abs(matrix[0, 1]) new_width = int((height * sin) + (width * cos)) new_height = int((height * cos) + (width * sin)) matrix[0, 2] += (new_width / 2) - center[0] matrix[1, 2] += (new_height / 2) - center[1] rotated_image = cv2.warpAffine(image, matrix, (new_width, new_height)) self.set_accept_enable(True) return rotated_image @classmethod def dialog_name(cls): return "Rotate"