diff --git a/src/OptimaLab35/mainWindow.py b/src/OptimaLab35/mainWindow.py
index 5f5fe58..6e715be 100644
--- a/src/OptimaLab35/mainWindow.py
+++ b/src/OptimaLab35/mainWindow.py
@@ -1,42 +1,31 @@
import os
from datetime import datetime
+
from optima35.core import OptimaManager
-
-from OptimaLab35 import __version__
-from .const import (
- APPLICATION_NAME,
- CONFIG_BASE_PATH
-)
-
-from .ui import resources_rc
-from .previewWindow import PreviewWindow
-from .settingsWindow import SettingsWindow
-
-from .utils.utility import Utilities
-from .ui.main_window import Ui_MainWindow
-from .ui.exif_handler_window import ExifEditor
-from .ui.simple_dialog import SimpleDialog # Import the SimpleDialog class
-
-from PySide6 import QtWidgets, QtCore
-
+from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import (
- QRunnable,
- QThreadPool,
- Signal,
+ QDate,
QObject,
QRegularExpression,
+ QRunnable,
Qt,
- QDate
+ QThreadPool,
+ Signal,
)
+from PySide6.QtGui import QIcon, QRegularExpressionValidator
+from PySide6.QtWidgets import QApplication, QFileDialog, QMainWindow, QMessageBox
-from PySide6.QtWidgets import (
- QMessageBox,
- QApplication,
- QMainWindow,
- QFileDialog
-)
+from OptimaLab35 import __version__
+
+from .const import APPLICATION_NAME, CONFIG_BASE_PATH
+from .previewWindow import PreviewWindow
+from .settingsWindow import SettingsWindow
+from .ui import resources_rc
+from .ui.exif_handler_window import ExifEditor
+from .ui.main_window import Ui_MainWindow
+from .ui.simple_dialog import SimpleDialog # Import the SimpleDialog class
+from .utils.utility import Utilities
-from PySide6.QtGui import QRegularExpressionValidator, QIcon
class OptimaLab35(QMainWindow, Ui_MainWindow):
def __init__(self):
@@ -46,7 +35,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
self.o = OptimaManager()
self.u = Utilities(os.path.expanduser(CONFIG_BASE_PATH))
self.app_settings = self.u.load_settings()
- self.thread_pool = QThreadPool() # multi thread ChatGPT
+ self.thread_pool = QThreadPool() # multi thread ChatGPT
# Initiate internal object
self.exif_file = os.path.expanduser(f"{CONFIG_BASE_PATH}/exif.yaml")
self.available_exif_data = None
@@ -98,6 +87,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
self.ui.lat_lineEdit.setValidator(validator)
self.ui.long_lineEdit.setValidator(validator)
self.ui.dateEdit.setDate(QDate.currentDate())
+
# UI related function, changing parts, open, etc.
def open_preview_window(self):
self.preview_window = PreviewWindow()
@@ -118,7 +108,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
def info_window(self):
info_text = f"""
{self.name} v{self.version}
- (C) 2024-2025 Mr Finchum aka CodeByMrFinchum
+ (C) 2024-2026 Mr Finchum aka CodeByMrFinchum
{self.name} is a GUI for {self.o.name} (v{self.o.version}), enhancing its functionality with a user-friendly interface for efficient image and metadata management.
Features:
@@ -185,8 +175,12 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
filtered.append(stripped)
# ignore anything else
# Sort: NA first, then valid times ascending
- return sorted(filtered, key=lambda x: (0, 0) if (x is None or str(x).strip().upper() in {"NA"})
- else (1, self.parse_time(x)))
+ return sorted(
+ filtered,
+ key=lambda x: (0, 0)
+ if (x is None or str(x).strip().upper() in {"NA"})
+ else (1, self.parse_time(x)),
+ )
def sort_dict_of_lists(self, input_dict):
# Partily ChatGPT
@@ -204,7 +198,9 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
elif all(isinstance(x, str) for x in lst):
sorted_dict[key] = sorted(
lst,
- key=lambda x: (0, x.lower()) if str(x).lower() == "na" else (1, str(x).lower())
+ key=lambda x: (0, x.lower())
+ if str(x).lower() == "na"
+ else (1, str(x).lower()),
)
return sorted_dict
@@ -246,29 +242,29 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
self.populate_comboboxes(combo_mapping)
def update_quality_options(self):
- """Update visibility of quality settings based on selected format."""
- # Partly ChatGPT
- selected_format = self.ui.image_type.currentText()
- # Hide all quality settings
- self.ui.png_quality_spinBox.setVisible(False)
- self.ui.jpg_quality_spinBox.setVisible(False)
- self.ui.jpg_quality_Slider.setVisible(False)
- self.ui.png_quality_Slider.setVisible(False)
- self.ui.quality_label_1.setVisible(False)
- self.ui.quality_label_2.setVisible(False)
- # Show relevant settings
- if selected_format == "jpg":
- self.ui.jpg_quality_spinBox.setVisible(True)
- self.ui.jpg_quality_Slider.setVisible(True)
- self.ui.quality_label_1.setVisible(True)
- elif selected_format == "webp":
- self.ui.jpg_quality_spinBox.setVisible(True)
- self.ui.jpg_quality_Slider.setVisible(True)
- self.ui.quality_label_1.setVisible(True)
- elif selected_format == "png":
- self.ui.png_quality_spinBox.setVisible(True)
- self.ui.png_quality_Slider.setVisible(True)
- self.ui.quality_label_2.setVisible(True)
+ """Update visibility of quality settings based on selected format."""
+ # Partly ChatGPT
+ selected_format = self.ui.image_type.currentText()
+ # Hide all quality settings
+ self.ui.png_quality_spinBox.setVisible(False)
+ self.ui.jpg_quality_spinBox.setVisible(False)
+ self.ui.jpg_quality_Slider.setVisible(False)
+ self.ui.png_quality_Slider.setVisible(False)
+ self.ui.quality_label_1.setVisible(False)
+ self.ui.quality_label_2.setVisible(False)
+ # Show relevant settings
+ if selected_format == "jpg":
+ self.ui.jpg_quality_spinBox.setVisible(True)
+ self.ui.jpg_quality_Slider.setVisible(True)
+ self.ui.quality_label_1.setVisible(True)
+ elif selected_format == "webp":
+ self.ui.jpg_quality_spinBox.setVisible(True)
+ self.ui.jpg_quality_Slider.setVisible(True)
+ self.ui.quality_label_1.setVisible(True)
+ elif selected_format == "png":
+ self.ui.png_quality_spinBox.setVisible(True)
+ self.ui.png_quality_Slider.setVisible(True)
+ self.ui.quality_label_2.setVisible(True)
def browse_input_folder(self):
folder = QFileDialog.getExistingDirectory(self, "Select Input Folder")
@@ -280,7 +276,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
if folder:
self.ui.output_path.setText(folder)
- def change_statusbar(self, msg, timeout = 500):
+ def change_statusbar(self, msg, timeout=500):
self.ui.statusBar.showMessage(msg, timeout)
# Core functions
@@ -291,7 +287,9 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
def image_list_from_folder(self, path):
image_files = [
- f for f in os.listdir(path) if f.lower().endswith((".png", ".jpg", ".jpeg", ".webp"))
+ f
+ for f in os.listdir(path)
+ if f.lower().endswith((".png", ".jpg", ".jpeg", ".webp"))
]
image_files.sort()
return image_files
@@ -307,11 +305,17 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
if process == "image":
if not input_folder or not output_folder:
- QMessageBox.warning(self, "Warning", "Input or output folder not selected")
+ QMessageBox.warning(
+ self, "Warning", "Input or output folder not selected"
+ )
return False
if not input_folder_valid or not output_folder_valid:
- QMessageBox.warning(self, "Warning", f"Input location {input_folder_valid}\nOutput folder {output_folder_valid}...")
+ QMessageBox.warning(
+ self,
+ "Warning",
+ f"Input location {input_folder_valid}\nOutput folder {output_folder_valid}...",
+ )
return False
if len(self.image_list_from_folder(output_folder)) != 0:
@@ -326,7 +330,6 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
return False
elif process == "exif":
-
if not input_folder:
QMessageBox.warning(self, "Warning", "Input not selected")
return False
@@ -342,15 +345,19 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
if reply == QMessageBox.No:
return False
- if not input_folder_valid :
- QMessageBox.warning(self, "Warning", f"Input location {input_folder_valid}")
+ if not input_folder_valid:
+ QMessageBox.warning(
+ self, "Warning", f"Input location {input_folder_valid}"
+ )
return False
else:
print("Something went wrong")
if len(image_list) == 0:
- QMessageBox.warning(self, "Warning", "Selected folder has no supported files.")
+ QMessageBox.warning(
+ self, "Warning", "Selected folder has no supported files."
+ )
return False
return True
@@ -369,7 +376,9 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
image_list = self.image_list_from_folder(self.settings["input_folder"])
# Create a worker ChatGPT
- worker = ImageProcessorRunnable(image_list, self.settings, self.handle_qprogressbar)
+ worker = ImageProcessorRunnable(
+ image_list, self.settings, self.handle_qprogressbar
+ )
worker.signals.finished.connect(self.on_processing_finished)
# Start worker in thread pool ChatGPT
self.thread_pool.start(worker)
@@ -387,13 +396,13 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
i = 1
for image_file in image_files:
-
input_path = os.path.join(input_folder, image_file)
self.o.insert_exif_to_image(
- exif_dict = self.settings["user_selected_exif"],
- image_path = input_path,
- gps = self.settings["gps"])
+ exif_dict=self.settings["user_selected_exif"],
+ image_path=input_path,
+ gps=self.settings["gps"],
+ )
self.change_statusbar(image_file, 100)
self.handle_qprogressbar(int((i / len(image_files)) * 100))
i += 1
@@ -415,7 +424,11 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
image_list = self.image_list_from_folder(self.settings["input_folder"])
print(image_list)
if not self.control_ending(image_list):
- QMessageBox.warning(self, "Warning", f"Error: one or more filenames do not end on a number.\nCan not adjust time")
+ QMessageBox.warning(
+ self,
+ "Warning",
+ f"Error: one or more filenames do not end on a number.\nCan not adjust time",
+ )
self.toggle_buttons(True)
return
@@ -424,19 +437,19 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
self.toggle_buttons(True)
QMessageBox.information(self, "Information", "Finished")
- def get_checkbox_value(self, checkbox, default = None):
+ def get_checkbox_value(self, checkbox, default=None):
"""Helper function to get the value of a checkbox or a default value."""
return checkbox.isChecked() if checkbox else default
- def get_spinbox_value(self, spinbox, default = None):
+ def get_spinbox_value(self, spinbox, default=None):
"""Helper function to get the value of a spinbox and handle empty input."""
return int(spinbox.text()) if spinbox.text() else default
- def get_combobox_value(self, combobox, default = None):
+ def get_combobox_value(self, combobox, default=None):
"""Helper function to get the value of a combobox."""
return combobox.currentIndex() + 1 if combobox.currentIndex() != -1 else default
- def get_text_value(self, lineedit, default = None):
+ def get_text_value(self, lineedit, default=None):
"""Helper function to get the value of a text input field."""
return lineedit.text() if lineedit.text() else default
@@ -464,12 +477,21 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
user_data["lens"] = self.ui.lens_comboBox.currentText()
user_data["iso"] = self.ui.iso_comboBox.currentText()
lab_info = self.add_laboratory_info()
- user_data["image_description"] = f"{self.ui.image_description_comboBox.currentText()} {lab_info}"
+ user_data["image_description"] = (
+ f"{self.ui.image_description_comboBox.currentText()} {lab_info}"
+ )
user_data["artist"] = self.ui.artist_comboBox.currentText()
user_data["copyright_info"] = self.ui.copyright_info_comboBox.currentText()
- user_data["software"] = f"{self.name} {self.version} with {self.o.name} {self.o.version}"
- if int(self.ui.contrast_spinBox.text()) != 0 or int(self.ui.brightness_spinBox.text()) != 0:
- user_data["user_comment"] = f"{self.ui.user_comment_comboBox.currentText()}, contrast: {self.ui.contrast_spinBox.text()}, brightness: {self.ui.brightness_spinBox.text()}"
+ user_data["software"] = (
+ f"{self.name} {self.version} with {self.o.name} {self.o.version}"
+ )
+ if (
+ int(self.ui.contrast_spinBox.text()) != 0
+ or int(self.ui.brightness_spinBox.text()) != 0
+ ):
+ user_data["user_comment"] = (
+ f"{self.ui.user_comment_comboBox.currentText()}, contrast: {self.ui.contrast_spinBox.text()}, brightness: {self.ui.brightness_spinBox.text()}"
+ )
else:
user_data["user_comment"] = self.ui.user_comment_comboBox.currentText()
@@ -477,13 +499,18 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
def get_selected_exif(self):
"""Collect selected EXIF data and handle date and GPS if necessary."""
- selected_exif = self.collect_selected_exif() if self.ui.exif_checkbox.isChecked() else None
+ selected_exif = (
+ self.collect_selected_exif() if self.ui.exif_checkbox.isChecked() else None
+ )
if selected_exif:
if self.ui.add_date_checkBox.isChecked():
selected_exif["date_time_original"] = self.get_date()
if self.ui.gps_checkBox.isChecked():
try:
- self.settings["gps"] = [float(self.ui.lat_lineEdit.text()), float(self.ui.long_lineEdit.text())]
+ self.settings["gps"] = [
+ float(self.ui.lat_lineEdit.text()),
+ float(self.ui.long_lineEdit.text()),
+ ]
except ValueError as e:
self.settings["gps"] = "Wrong gps data"
else:
@@ -503,22 +530,47 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
self.settings["output_folder"] = self.get_text_value(self.ui.output_path)
self.settings["file_format"] = self.ui.image_type.currentText()
# Quality
- self.settings["jpg_quality"] = self.get_spinbox_value(self.ui.jpg_quality_spinBox)
- self.settings["png_compression"] = self.get_spinbox_value(self.ui.png_quality_spinBox)
- self.settings["resize"] = int(self.ui.resize_spinBox.text()) if self.ui.resize_spinBox.text() != "100" else None
+ self.settings["jpg_quality"] = self.get_spinbox_value(
+ self.ui.jpg_quality_spinBox
+ )
+ self.settings["png_compression"] = self.get_spinbox_value(
+ self.ui.png_quality_spinBox
+ )
+ self.settings["resize"] = (
+ int(self.ui.resize_spinBox.text())
+ if self.ui.resize_spinBox.text() != "100"
+ else None
+ )
self.settings["optimize"] = self.get_checkbox_value(self.ui.optimize_checkBox)
# Changes for image
- self.settings["brightness"] = int(self.ui.brightness_spinBox.text()) if self.ui.brightness_spinBox.text() != "0" else None
- self.settings["contrast"] = int(self.ui.contrast_spinBox.text()) if self.ui.contrast_spinBox.text() != "0" else None
+ self.settings["brightness"] = (
+ int(self.ui.brightness_spinBox.text())
+ if self.ui.brightness_spinBox.text() != "0"
+ else None
+ )
+ self.settings["contrast"] = (
+ int(self.ui.contrast_spinBox.text())
+ if self.ui.contrast_spinBox.text() != "0"
+ else None
+ )
self.settings["grayscale"] = self.get_checkbox_value(self.ui.grayscale_checkBox)
# Watermark
self.settings["font_size"] = self.get_combobox_value(self.ui.font_size_comboBox)
self.settings["watermark"] = self.get_text_value(self.ui.watermark_lineEdit)
# Naming
- new_name = self.get_text_value(self.ui.filename, False) if self.ui.rename_checkbox.isChecked() else False
- if isinstance(new_name, str): new_name = new_name.replace(" ", "_")
+ new_name = (
+ self.get_text_value(self.ui.filename, False)
+ if self.ui.rename_checkbox.isChecked()
+ else False
+ )
+ if isinstance(new_name, str):
+ new_name = new_name.replace(" ", "_")
self.settings["new_file_names"] = new_name
- self.settings["invert_image_order"] = self.get_checkbox_value(self.ui.revert_checkbox) if new_name is not False else None
+ self.settings["invert_image_order"] = (
+ self.get_checkbox_value(self.ui.revert_checkbox)
+ if new_name is not False
+ else None
+ )
# Handle EXIF data selection
self.settings["copy_exif"] = self.get_checkbox_value(self.ui.exif_copy_checkBox)
self.settings["own_exif"] = self.get_checkbox_value(self.ui.exif_checkbox)
@@ -550,11 +602,13 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
QApplication.closeAllWindows()
event.accept()
+
class WorkerSignals(QObject):
# ChatGPT
progress = Signal(int)
finished = Signal()
+
class ImageProcessorRunnable(QRunnable):
# ChatGPT gave rough function layout
def __init__(self, image_files, settings, progress_callback):
@@ -573,27 +627,32 @@ class ImageProcessorRunnable(QRunnable):
for i, image_file in enumerate(self.image_files, start=1):
input_path = os.path.join(input_folder, image_file)
if self.settings["new_file_names"] != False:
- image_name = self.u.append_number_to_name(self.settings["new_file_names"], i, len(self.image_files), self.settings["invert_image_order"])
+ image_name = self.u.append_number_to_name(
+ self.settings["new_file_names"],
+ i,
+ len(self.image_files),
+ self.settings["invert_image_order"],
+ )
else:
image_name = os.path.splitext(image_file)[0]
output_path = os.path.join(output_folder, image_name)
self.o.process_and_save_image(
- image_input_file = input_path,
- image_output_file = output_path,
- file_type = self.settings["file_format"],
- quality = self.settings["jpg_quality"],
- compressing = self.settings["png_compression"],
- optimize = self.settings["optimize"],
- resize = self.settings["resize"],
- watermark = self.settings["watermark"],
- font_size = self.settings["font_size"],
- grayscale = self.settings["grayscale"],
- brightness = self.settings["brightness"],
- contrast = self.settings["contrast"],
- dict_for_exif = self.settings["user_selected_exif"],
- gps = self.settings["gps"],
- copy_exif = self.settings["copy_exif"]
+ image_input_file=input_path,
+ image_output_file=output_path,
+ file_type=self.settings["file_format"],
+ quality=self.settings["jpg_quality"],
+ compressing=self.settings["png_compression"],
+ optimize=self.settings["optimize"],
+ resize=self.settings["resize"],
+ watermark=self.settings["watermark"],
+ font_size=self.settings["font_size"],
+ grayscale=self.settings["grayscale"],
+ brightness=self.settings["brightness"],
+ contrast=self.settings["contrast"],
+ dict_for_exif=self.settings["user_selected_exif"],
+ gps=self.settings["gps"],
+ copy_exif=self.settings["copy_exif"],
)
self.signals.progress.emit(int((i / len(self.image_files)) * 100))