feat: Enhanced preview window

This commit is contained in:
Mr Finchum 2025-01-30 16:45:02 +00:00
parent 51108ef86e
commit 89c3fb3e68
7 changed files with 141 additions and 44 deletions

View file

@ -1,7 +1,14 @@
# Changelog
## 0.6.x
### 0.6.0: Initial Flatpak Support
## 0.7.x
### 0.7.0: Enhanced Preview
- Images loaded into the preview window are now scaled while maintaining aspect ratio.
- Added live updates: changes to brightness, contrast, or grayscale are applied immediately.
- ⚠ This may crush the system depending on image size and system specifications.
- Removed Settings from menuBar, and extended the about window.
---
## 0.6.0: Initial Flatpak Support
- Started Flatpak package building.
- Not added to Flathub yet, as only stable software is hosted there.
- Not fully completed, icon, name, and description are included, but the version is missing for some reason.
@ -10,14 +17,12 @@
---
## 0.5.x
### 0.5.0
## 0.5.0
- Removed all leftover of tui code that was hiding in some classes.
---
## 0.4.x
### 0.4.0
## 0.4.0
- Fixed a critical issue that prevented the program from functioning.
- Updated compatibility to align with the **upcoming** optima35 **release**.

View file

@ -10,7 +10,7 @@ from OptimaLab35.ui.exif_handler_window import ExifEditor
from OptimaLab35.ui.simple_dialog import SimpleDialog # Import the SimpleDialog class
from OptimaLab35 import __version__
from PySide6.QtCore import QRunnable, QThreadPool, Signal, QObject, QRegularExpression
from PySide6.QtCore import QRunnable, QThreadPool, Signal, QObject, QRegularExpression, Qt
from PySide6 import QtWidgets
from PySide6.QtWidgets import (
@ -75,7 +75,6 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
self.ui.edit_exif_button.clicked.connect(self.open_exif_editor)
self.ui.actionAbout.triggered.connect(self.info_window)
self.ui.actionPreview.triggered.connect(self.open_preview_window)
self.ui.preview_Button.clicked.connect(self.open_preview_window)
regex = QRegularExpression(r"^\d{1,2}\.\d{1,6}$")
@ -88,7 +87,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
# UI related function, changing parts, open, etc.
def open_preview_window(self):
self.preview_window.values_selected.connect(self.update_values)
self.preview_window.show()
self.preview_window.showMaximized()
def update_values(self, value1, value2, checkbox_state):
# Update main window's widgets with the received values
@ -98,12 +97,20 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
self.ui.grayscale_checkBox.setChecked(checkbox_state)
def info_window(self):
# ChatGPT, mainly
info_text = f"""
<h3>{self.name} v{self.version}</h3>
<p>(C) 2024-2025 Mr. Finchum aka CodeByMrFinchum</p>
<p>{self.name} is a GUI for <b>{self.o.name}</b> (v{self.o.version}).</p>
<p> Both projects are in active development, for more details, visit:</p>
<p>(C) 2024-2025 Mr Finchum aka CodeByMrFinchum</p>
<p>{self.name} is a GUI for {self.o.name} (v{self.o.version}), enhancing its functionality with a\nuser-friendly interface for efficient image and metadata management.</p>
<h4>Features:</h4>
<ul>
<li>Image processing: resize, grayscale, brightness/contrast adjustments</li>
<li>Live image preview: see changes before applying</li>
<li>EXIF management: add, copy, remove metadata, GPS support</li>
<li>Watermarking: add custom text-based watermarks</li>
</ul>
<p>For more details, visit:</p>
<ul>
<li><a href="https://gitlab.com/CodeByMrFinchum/OptimaLab35">OptimaLab35 GitLab</a></li>
<li><a href="https://gitlab.com/CodeByMrFinchum/optima35">optima35 GitLab</a></li>
@ -455,6 +462,7 @@ class PreviewWindow(QMainWindow, Ui_Preview_Window):
self.ui = Ui_Preview_Window()
self.ui.setupUi(self)
self.o = OptimaManager()
self.ui.QLabel.setAlignment(Qt.AlignCenter)
## Ui interaction
self.ui.load_Button.clicked.connect(self.browse_file)
self.ui.update_Button.clicked.connect(self.update_preview)
@ -462,7 +470,22 @@ class PreviewWindow(QMainWindow, Ui_Preview_Window):
self.ui.reset_brightness_Button.clicked.connect(lambda: self.ui.brightness_spinBox.setValue(0))
self.ui.reset_contrast_Button.clicked.connect(lambda: self.ui.contrast_spinBox.setValue(0))
self.preview_image = None
# Connect UI elements to `on_ui_change`
self.ui.brightness_spinBox.valueChanged.connect(self.on_ui_change)
self.ui.brightness_Slider.valueChanged.connect(self.on_ui_change)
#self.ui.reset_brightness_Button.clicked.connect(self.on_ui_change)
self.ui.contrast_spinBox.valueChanged.connect(self.on_ui_change)
self.ui.contrast_Slider.valueChanged.connect(self.on_ui_change)
#self.ui.reset_contrast_Button.clicked.connect(self.on_ui_change)
self.ui.grayscale_checkBox.stateChanged.connect(self.on_ui_change)
def on_ui_change(self):
"""Triggers update only if live update is enabled."""
if self.ui.live_update.isChecked():
self.update_preview()
def browse_file(self):
file = QFileDialog.getOpenFileName(self, caption = "Select File", filter = ("Images (*.png *.webp *.jpg *.jpeg)"))
@ -470,29 +493,61 @@ class PreviewWindow(QMainWindow, Ui_Preview_Window):
self.ui.image_path_lineEdit.setText(file[0])
self.update_preview()
def update_preview(self):
path = self.ui.image_path_lineEdit.text()
def process_image(self, path):
"""Loads and processes the image with modifications."""
# Refactored by ChatGPT
if not os.path.isfile(path):
return
return None
try:
img = self.o.process_image_object(
image_input_file = path,
resize = 50,
watermark = "PREVIEW",
grayscale = self.ui.grayscale_checkBox.isChecked(),
brightness = int(self.ui.brightness_spinBox.text()),
contrast = int(self.ui.contrast_spinBox.text())
image_input_file=path, # Example: resize percentage
watermark="PREVIEW",
resize = 100,
grayscale=self.ui.grayscale_checkBox.isChecked(),
brightness=int(self.ui.brightness_spinBox.text()),
contrast=int(self.ui.contrast_spinBox.text())
)
return QPixmap.fromImage(img)
except Exception as e:
QMessageBox.warning(self, "Warning", "Error loading image...")
print(f"Error loading image...\n{e}")
return None
def display_image(self, pixmap):
"""Adjusts the image to fit within the QLabel."""
# ChatGPT
if pixmap is None:
return
self.preview_image = QPixmap.fromImage(img)
self.ui.QLabel.setPixmap(self.preview_image)
# Get max available size (QLabel size)
max_size = self.ui.QLabel.size()
max_width = max_size.width()
max_height = max_size.height()
# Scale image to fit within the available space while maintaining aspect ratio
scaled_pixmap = pixmap.scaled(
max_width, max_height,
Qt.KeepAspectRatio,
Qt.SmoothTransformation
)
# Set the scaled image
self.ui.QLabel.setPixmap(scaled_pixmap)
# Adjust QLabel size to match image
self.ui.QLabel.resize(scaled_pixmap.size())
def update_preview(self):
"""Handles loading and displaying the image."""
# ChatGPT
path = self.ui.image_path_lineEdit.text()
pixmap = self.process_image(path)
self.display_image(pixmap)
def close_window(self):
# Emit the signal with the values from the spinboxes and checkbox
# chatgpt
if self.ui.checkBox.isChecked():
self.values_selected.emit(self.ui.brightness_spinBox.value(), self.ui.contrast_spinBox.value(), self.ui.grayscale_checkBox.isChecked())
self.close()

View file

@ -553,15 +553,11 @@ class Ui_MainWindow(object):
self.menuBar = QMenuBar(MainWindow)
self.menuBar.setObjectName(u"menuBar")
self.menuBar.setGeometry(QRect(0, 0, 440, 27))
self.menuSettings = QMenu(self.menuBar)
self.menuSettings.setObjectName(u"menuSettings")
self.menuHelp = QMenu(self.menuBar)
self.menuHelp.setObjectName(u"menuHelp")
MainWindow.setMenuBar(self.menuBar)
self.menuBar.addAction(self.menuSettings.menuAction())
self.menuBar.addAction(self.menuHelp.menuAction())
self.menuSettings.addAction(self.actionPreview)
self.menuHelp.addAction(self.actionAbout)
self.retranslateUi(MainWindow)
@ -661,7 +657,6 @@ class Ui_MainWindow(object):
self.date_groupBox.setTitle(QCoreApplication.translate("MainWindow", u"Optional", None))
self.add_date_checkBox.setText(QCoreApplication.translate("MainWindow", u"add date", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QCoreApplication.translate("MainWindow", u"EXIF", None))
self.menuSettings.setTitle(QCoreApplication.translate("MainWindow", u"Settings", None))
self.menuHelp.setTitle(QCoreApplication.translate("MainWindow", u"Help", None))
# retranslateUi

View file

@ -835,19 +835,12 @@
<height>27</height>
</rect>
</property>
<widget class="QMenu" name="menuSettings">
<property name="title">
<string>Settings</string>
</property>
<addaction name="actionPreview"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuSettings"/>
<addaction name="menuHelp"/>
</widget>
<action name="actionInfo">

View file

@ -24,7 +24,7 @@ class Ui_Preview_Window(object):
def setupUi(self, Preview_Window):
if not Preview_Window.objectName():
Preview_Window.setObjectName(u"Preview_Window")
Preview_Window.resize(803, 700)
Preview_Window.resize(803, 775)
Preview_Window.setMinimumSize(QSize(800, 700))
self.centralwidget = QWidget(Preview_Window)
self.centralwidget.setObjectName(u"centralwidget")
@ -34,7 +34,7 @@ class Ui_Preview_Window(object):
self.QLabel.setObjectName(u"QLabel")
self.QLabel.setMinimumSize(QSize(628, 628))
self.QLabel.setFrameShape(QFrame.Box)
self.QLabel.setScaledContents(True)
self.QLabel.setScaledContents(False)
self.horizontalLayout.addWidget(self.QLabel)
@ -130,6 +130,24 @@ class Ui_Preview_Window(object):
self.verticalLayout_3.addWidget(self.update_Button)
self.widget_5 = QWidget(self.widget)
self.widget_5.setObjectName(u"widget_5")
self.verticalLayout_5 = QVBoxLayout(self.widget_5)
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
self.label_4 = QLabel(self.widget_5)
self.label_4.setObjectName(u"label_4")
self.label_4.setWordWrap(True)
self.verticalLayout_5.addWidget(self.label_4)
self.live_update = QCheckBox(self.widget_5)
self.live_update.setObjectName(u"live_update")
self.verticalLayout_5.addWidget(self.live_update)
self.verticalLayout_3.addWidget(self.widget_5)
self.widget_4 = QWidget(self.widget)
self.widget_4.setObjectName(u"widget_4")
self.verticalLayout_4 = QVBoxLayout(self.widget_4)
@ -184,6 +202,8 @@ class Ui_Preview_Window(object):
self.reset_contrast_Button.setText(QCoreApplication.translate("Preview_Window", u"Reset", None))
self.grayscale_checkBox.setText(QCoreApplication.translate("Preview_Window", u"Grayscale", None))
self.update_Button.setText(QCoreApplication.translate("Preview_Window", u"Update preview", None))
self.label_4.setText(QCoreApplication.translate("Preview_Window", u"Update preview live might have large impact on system, especilly if image is large.", None))
self.live_update.setText(QCoreApplication.translate("Preview_Window", u"Live update", None))
self.label_3.setText(QCoreApplication.translate("Preview_Window", u"Copy values to main window when closing", None))
self.checkBox.setText(QCoreApplication.translate("Preview_Window", u"Copy Values", None))
self.close_Button.setText(QCoreApplication.translate("Preview_Window", u"Close", None))

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>803</width>
<height>700</height>
<height>775</height>
</rect>
</property>
<property name="minimumSize">
@ -36,7 +36,7 @@
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
@ -181,6 +181,29 @@
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_5" native="true">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Update preview live might have large impact on system, especilly if image is large.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="live_update">
<property name="text">
<string>Live update</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_4" native="true">
<layout class="QVBoxLayout" name="verticalLayout_4">

View file

@ -1,17 +1,22 @@
from PySide6.QtWidgets import QApplication, QDialog, QVBoxLayout, QLineEdit, QPushButton, QLabel
# ChatGPT
from PySide6.QtWidgets import QApplication, QDialog, QVBoxLayout, QLabel, QPushButton
from PySide6.QtCore import Qt
class SimpleDialog(QDialog):
def __init__(self):
super().__init__()
# Set default properties
self.setGeometry(100, 100, 300, 100)
self.setWindowTitle("Information")
self.setGeometry(100, 100, 400, 100) # Default size
# Create the layout
layout = QVBoxLayout()
# Create the label for the message
self.message_label = QLabel(self)
self.message_label.setWordWrap(True) # Enable word wrapping
self.message_label.setAlignment(Qt.AlignLeft | Qt.AlignTop) # Align text
self.message_label.setMaximumWidth(400) # Set max width so it wraps text
# Create the close button
close_button = QPushButton("Close", self)
@ -27,4 +32,5 @@ class SimpleDialog(QDialog):
def show_dialog(self, title: str, message: str):
self.setWindowTitle(title) # Set the window title
self.message_label.setText(message) # Set the message text
self.adjustSize() # Adjust window height dynamically based on text content
self.exec() # Open the dialog as a modal window