Patch: changed year to include 2026 #12

Merged
CodeByMrFinchum merged 1 commit from patch/year into main 2026-01-04 11:15:48 +01:00

View file

@ -1,42 +1,31 @@
import os import os
from datetime import datetime from datetime import datetime
from optima35.core import OptimaManager from optima35.core import OptimaManager
from PySide6 import QtCore, QtWidgets
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.QtCore import ( from PySide6.QtCore import (
QRunnable, QDate,
QThreadPool,
Signal,
QObject, QObject,
QRegularExpression, QRegularExpression,
QRunnable,
Qt, Qt,
QDate QThreadPool,
Signal,
) )
from PySide6.QtGui import QIcon, QRegularExpressionValidator
from PySide6.QtWidgets import QApplication, QFileDialog, QMainWindow, QMessageBox
from PySide6.QtWidgets import ( from OptimaLab35 import __version__
QMessageBox,
QApplication, from .const import APPLICATION_NAME, CONFIG_BASE_PATH
QMainWindow, from .previewWindow import PreviewWindow
QFileDialog 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): class OptimaLab35(QMainWindow, Ui_MainWindow):
def __init__(self): def __init__(self):
@ -98,6 +87,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
self.ui.lat_lineEdit.setValidator(validator) self.ui.lat_lineEdit.setValidator(validator)
self.ui.long_lineEdit.setValidator(validator) self.ui.long_lineEdit.setValidator(validator)
self.ui.dateEdit.setDate(QDate.currentDate()) self.ui.dateEdit.setDate(QDate.currentDate())
# UI related function, changing parts, open, etc. # UI related function, changing parts, open, etc.
def open_preview_window(self): def open_preview_window(self):
self.preview_window = PreviewWindow() self.preview_window = PreviewWindow()
@ -118,7 +108,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
def info_window(self): def info_window(self):
info_text = f""" info_text = f"""
<h3>{self.name} v{self.version}</h3> <h3>{self.name} v{self.version}</h3>
<p>(C) 2024-2025 Mr Finchum aka CodeByMrFinchum</p> <p>(C) 2024-2026 Mr Finchum aka CodeByMrFinchum</p>
<p>{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.</p> <p>{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.</p>
<h4>Features:</h4> <h4>Features:</h4>
@ -185,8 +175,12 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
filtered.append(stripped) filtered.append(stripped)
# ignore anything else # ignore anything else
# Sort: NA first, then valid times ascending # 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"}) return sorted(
else (1, self.parse_time(x))) 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): def sort_dict_of_lists(self, input_dict):
# Partily ChatGPT # Partily ChatGPT
@ -204,7 +198,9 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
elif all(isinstance(x, str) for x in lst): elif all(isinstance(x, str) for x in lst):
sorted_dict[key] = sorted( sorted_dict[key] = sorted(
lst, 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 return sorted_dict
@ -291,7 +287,9 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
def image_list_from_folder(self, path): def image_list_from_folder(self, path):
image_files = [ 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() image_files.sort()
return image_files return image_files
@ -307,11 +305,17 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
if process == "image": if process == "image":
if not input_folder or not output_folder: 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 return False
if not input_folder_valid or not output_folder_valid: 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 return False
if len(self.image_list_from_folder(output_folder)) != 0: if len(self.image_list_from_folder(output_folder)) != 0:
@ -326,7 +330,6 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
return False return False
elif process == "exif": elif process == "exif":
if not input_folder: if not input_folder:
QMessageBox.warning(self, "Warning", "Input not selected") QMessageBox.warning(self, "Warning", "Input not selected")
return False return False
@ -343,14 +346,18 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
return False return False
if not input_folder_valid: if not input_folder_valid:
QMessageBox.warning(self, "Warning", f"Input location {input_folder_valid}") QMessageBox.warning(
self, "Warning", f"Input location {input_folder_valid}"
)
return False return False
else: else:
print("Something went wrong") print("Something went wrong")
if len(image_list) == 0: 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 False
return True return True
@ -369,7 +376,9 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
image_list = self.image_list_from_folder(self.settings["input_folder"]) image_list = self.image_list_from_folder(self.settings["input_folder"])
# Create a worker ChatGPT # 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) worker.signals.finished.connect(self.on_processing_finished)
# Start worker in thread pool ChatGPT # Start worker in thread pool ChatGPT
self.thread_pool.start(worker) self.thread_pool.start(worker)
@ -387,13 +396,13 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
i = 1 i = 1
for image_file in image_files: for image_file in image_files:
input_path = os.path.join(input_folder, image_file) input_path = os.path.join(input_folder, image_file)
self.o.insert_exif_to_image( self.o.insert_exif_to_image(
exif_dict=self.settings["user_selected_exif"], exif_dict=self.settings["user_selected_exif"],
image_path=input_path, image_path=input_path,
gps = self.settings["gps"]) gps=self.settings["gps"],
)
self.change_statusbar(image_file, 100) self.change_statusbar(image_file, 100)
self.handle_qprogressbar(int((i / len(image_files)) * 100)) self.handle_qprogressbar(int((i / len(image_files)) * 100))
i += 1 i += 1
@ -415,7 +424,11 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
image_list = self.image_list_from_folder(self.settings["input_folder"]) image_list = self.image_list_from_folder(self.settings["input_folder"])
print(image_list) print(image_list)
if not self.control_ending(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) self.toggle_buttons(True)
return return
@ -464,12 +477,21 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
user_data["lens"] = self.ui.lens_comboBox.currentText() user_data["lens"] = self.ui.lens_comboBox.currentText()
user_data["iso"] = self.ui.iso_comboBox.currentText() user_data["iso"] = self.ui.iso_comboBox.currentText()
lab_info = self.add_laboratory_info() 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["artist"] = self.ui.artist_comboBox.currentText()
user_data["copyright_info"] = self.ui.copyright_info_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}" user_data["software"] = (
if int(self.ui.contrast_spinBox.text()) != 0 or int(self.ui.brightness_spinBox.text()) != 0: f"{self.name} {self.version} with {self.o.name} {self.o.version}"
user_data["user_comment"] = f"{self.ui.user_comment_comboBox.currentText()}, contrast: {self.ui.contrast_spinBox.text()}, brightness: {self.ui.brightness_spinBox.text()}" )
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: else:
user_data["user_comment"] = self.ui.user_comment_comboBox.currentText() user_data["user_comment"] = self.ui.user_comment_comboBox.currentText()
@ -477,13 +499,18 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
def get_selected_exif(self): def get_selected_exif(self):
"""Collect selected EXIF data and handle date and GPS if necessary.""" """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 selected_exif:
if self.ui.add_date_checkBox.isChecked(): if self.ui.add_date_checkBox.isChecked():
selected_exif["date_time_original"] = self.get_date() selected_exif["date_time_original"] = self.get_date()
if self.ui.gps_checkBox.isChecked(): if self.ui.gps_checkBox.isChecked():
try: 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: except ValueError as e:
self.settings["gps"] = "Wrong gps data" self.settings["gps"] = "Wrong gps data"
else: else:
@ -503,22 +530,47 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
self.settings["output_folder"] = self.get_text_value(self.ui.output_path) self.settings["output_folder"] = self.get_text_value(self.ui.output_path)
self.settings["file_format"] = self.ui.image_type.currentText() self.settings["file_format"] = self.ui.image_type.currentText()
# Quality # Quality
self.settings["jpg_quality"] = self.get_spinbox_value(self.ui.jpg_quality_spinBox) self.settings["jpg_quality"] = self.get_spinbox_value(
self.settings["png_compression"] = self.get_spinbox_value(self.ui.png_quality_spinBox) self.ui.jpg_quality_spinBox
self.settings["resize"] = int(self.ui.resize_spinBox.text()) if self.ui.resize_spinBox.text() != "100" else None )
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) self.settings["optimize"] = self.get_checkbox_value(self.ui.optimize_checkBox)
# Changes for image # Changes for image
self.settings["brightness"] = int(self.ui.brightness_spinBox.text()) if self.ui.brightness_spinBox.text() != "0" else None self.settings["brightness"] = (
self.settings["contrast"] = int(self.ui.contrast_spinBox.text()) if self.ui.contrast_spinBox.text() != "0" else None 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) self.settings["grayscale"] = self.get_checkbox_value(self.ui.grayscale_checkBox)
# Watermark # Watermark
self.settings["font_size"] = self.get_combobox_value(self.ui.font_size_comboBox) self.settings["font_size"] = self.get_combobox_value(self.ui.font_size_comboBox)
self.settings["watermark"] = self.get_text_value(self.ui.watermark_lineEdit) self.settings["watermark"] = self.get_text_value(self.ui.watermark_lineEdit)
# Naming # Naming
new_name = self.get_text_value(self.ui.filename, False) if self.ui.rename_checkbox.isChecked() else False new_name = (
if isinstance(new_name, str): new_name = new_name.replace(" ", "_") 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["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 # Handle EXIF data selection
self.settings["copy_exif"] = self.get_checkbox_value(self.ui.exif_copy_checkBox) 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) self.settings["own_exif"] = self.get_checkbox_value(self.ui.exif_checkbox)
@ -550,11 +602,13 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
QApplication.closeAllWindows() QApplication.closeAllWindows()
event.accept() event.accept()
class WorkerSignals(QObject): class WorkerSignals(QObject):
# ChatGPT # ChatGPT
progress = Signal(int) progress = Signal(int)
finished = Signal() finished = Signal()
class ImageProcessorRunnable(QRunnable): class ImageProcessorRunnable(QRunnable):
# ChatGPT gave rough function layout # ChatGPT gave rough function layout
def __init__(self, image_files, settings, progress_callback): def __init__(self, image_files, settings, progress_callback):
@ -573,7 +627,12 @@ class ImageProcessorRunnable(QRunnable):
for i, image_file in enumerate(self.image_files, start=1): for i, image_file in enumerate(self.image_files, start=1):
input_path = os.path.join(input_folder, image_file) input_path = os.path.join(input_folder, image_file)
if self.settings["new_file_names"] != False: 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: else:
image_name = os.path.splitext(image_file)[0] image_name = os.path.splitext(image_file)[0]
output_path = os.path.join(output_folder, image_name) output_path = os.path.join(output_folder, image_name)
@ -593,7 +652,7 @@ class ImageProcessorRunnable(QRunnable):
contrast=self.settings["contrast"], contrast=self.settings["contrast"],
dict_for_exif=self.settings["user_selected_exif"], dict_for_exif=self.settings["user_selected_exif"],
gps=self.settings["gps"], gps=self.settings["gps"],
copy_exif = self.settings["copy_exif"] copy_exif=self.settings["copy_exif"],
) )
self.signals.progress.emit(int((i / len(self.image_files)) * 100)) self.signals.progress.emit(int((i / len(self.image_files)) * 100))