from PyQt6.QtWidgets import QLabel, QVBoxLayout, QHBoxLayout, QLineEdit, QCheckBox, QSlider from PyQt6.QtGui import QIntValidator from PyQt6.QtCore import Qt from .ImageParameterDialog import ImageParameterDialog import ImageProcessingWorker import numpy as np import cv2 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"