From 9dc14cdfcb3c2ec73047f83f6d9d489ac95685e7 Mon Sep 17 00:00:00 2001 From: Mr Finchum Date: Wed, 13 Aug 2025 12:36:17 +0200 Subject: [PATCH] feat: added two new information that can be added to the image exif. Now information about developing of the film can be added to the exif file as well as enhancing of exif window. Co-authored-by: Mr Finchum Co-committed-by: Mr Finchum --- CHANGELOG.md | 12 ++ src/OptimaLab35/mainWindow.py | 53 ++++- src/OptimaLab35/ui/exif_handler_window.py | 1 + src/OptimaLab35/ui/main_window.py | 206 ++++++++----------- src/OptimaLab35/ui/main_window.ui | 228 ++++++++++------------ src/OptimaLab35/ui/preview_window.py | 2 +- src/OptimaLab35/ui/settings_window.py | 2 +- 7 files changed, 251 insertions(+), 253 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9b2781..c13f6e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,20 @@ # Changelog + +## 1.6.x +### 1.6.0: Feature - Add Information from Developing Process + +- Added two new combo boxes: one for **film developer** and one for **development time** (how long the film was developed). +- The film developer field accepts all characters, while the time field only accepts `NA` or `mm:ss`. +- **Improved EXIF Edit Window**: Pressing **Enter** (Return) now adds the new item to the list, just like pressing the **Add** button. + +--- + ## 1.5.x ### 1.5.0: Feature - Time of dateEdit now today - Changes that instead of the dateEdit elements being always set to a last day of 2024 it is the current day. +--- + ## 1.4.x ### 1.4.2: Fix links - Fixed that changelog was linked to GitLab, not it is to code.boxyfoxy.net diff --git a/src/OptimaLab35/mainWindow.py b/src/OptimaLab35/mainWindow.py index c299689..99d1399 100644 --- a/src/OptimaLab35/mainWindow.py +++ b/src/OptimaLab35/mainWindow.py @@ -160,6 +160,33 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): elif index == 0: # Main Tab self.handle_exif_file("write") + def parse_time(self, s): + # Modified ChatGPT code + """Return total seconds if valid mm:ss, otherwise None.""" + try: + mm, ss = s.split(":") + return int(mm) * 60 + int(ss) + except (ValueError, AttributeError): + return None + + def sort_times(self, times): + # Keep only mm:ss or NA + # Modified ChatGPT code + filtered = [] + for t in times: + if t is None: + filtered.append(None) + elif isinstance(t, str): + stripped = t.strip() + if stripped.upper() in {"NA"}: + filtered.append(stripped) + elif self.parse_time(stripped) is not None: + 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))) + def sort_dict_of_lists(self, input_dict): # Partily ChatGPT sorted_dict = {} @@ -170,9 +197,14 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): lst = sorted(lst) lst = [str(x) for x in lst] sorted_dict["iso"] = lst - + elif key == "time": + lst = self.sort_times(lst) + sorted_dict["time"] = lst elif all(isinstance(x, str) for x in lst): - sorted_dict[key] = sorted(lst, key=str.lower) # Case-insensitive sort for strings + sorted_dict[key] = sorted( + lst, + key=lambda x: (0, x.lower()) if str(x).lower() == "na" else (1, str(x).lower()) + ) return sorted_dict @@ -206,6 +238,8 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): "user_comment": self.ui.user_comment_comboBox, "artist": self.ui.artist_comboBox, "copyright_info": self.ui.copyright_info_comboBox, + "developer": self.ui.dev_comboBox, + "time": self.ui.time_comboBox, } self.populate_comboboxes(combo_mapping) @@ -410,13 +444,26 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): new_date = datetime.strptime(date_input, "%Y-%m-%d") return new_date.strftime("%Y:%m:%d 00:00:00") + def add_laboratory_info(self): + lab_info_str = "" + dev_text = self.ui.dev_comboBox.currentText() + time_text = self.ui.time_comboBox.currentText() + if dev_text != "NA": + lab_info_str += f", {dev_text}" + + if time_text != "NA": + lab_info_str += f", {time_text}" + + return lab_info_str + def collect_selected_exif(self): user_data = {} user_data["make"] = self.ui.make_comboBox.currentText() user_data["model"] = self.ui.model_comboBox.currentText() user_data["lens"] = self.ui.lens_comboBox.currentText() user_data["iso"] = self.ui.iso_comboBox.currentText() - user_data["image_description"] = self.ui.image_description_comboBox.currentText() + lab_info = self.add_laboratory_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}" diff --git a/src/OptimaLab35/ui/exif_handler_window.py b/src/OptimaLab35/ui/exif_handler_window.py index 930fb51..54e6026 100644 --- a/src/OptimaLab35/ui/exif_handler_window.py +++ b/src/OptimaLab35/ui/exif_handler_window.py @@ -34,6 +34,7 @@ class ExifEditor(QMainWindow): # Line edit for adding items self.line_edit = QLineEdit() + self.line_edit.returnPressed.connect(self.add_item) self.line_edit.setPlaceholderText("Enter new item...") main_layout.addWidget(self.line_edit) diff --git a/src/OptimaLab35/ui/main_window.py b/src/OptimaLab35/ui/main_window.py index 38191e5..f526159 100644 --- a/src/OptimaLab35/ui/main_window.py +++ b/src/OptimaLab35/ui/main_window.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'main_window.ui' ## -## Created by: Qt User Interface Compiler version 6.8.2 +## Created by: Qt User Interface Compiler version 6.9.0 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -28,7 +28,7 @@ class Ui_MainWindow(object): if not MainWindow.objectName(): MainWindow.setObjectName(u"MainWindow") MainWindow.setWindowModality(Qt.WindowModality.NonModal) - MainWindow.resize(450, 720) + MainWindow.resize(450, 696) MainWindow.setMinimumSize(QSize(350, 677)) MainWindow.setMaximumSize(QSize(450, 1000)) self.actionInfo = QAction(MainWindow) @@ -364,141 +364,105 @@ class Ui_MainWindow(object): self.exif_options_group.setEnabled(False) self.gridLayout_7 = QGridLayout(self.exif_options_group) self.gridLayout_7.setObjectName(u"gridLayout_7") - self.widget_7 = QWidget(self.exif_options_group) - self.widget_7.setObjectName(u"widget_7") - self.verticalLayout_7 = QVBoxLayout(self.widget_7) - self.verticalLayout_7.setObjectName(u"verticalLayout_7") - self.label_7 = QLabel(self.widget_7) + self.label_7 = QLabel(self.exif_options_group) self.label_7.setObjectName(u"label_7") - self.verticalLayout_7.addWidget(self.label_7) + self.gridLayout_7.addWidget(self.label_7, 6, 0, 1, 1) - self.artist_comboBox = QComboBox(self.widget_7) - self.artist_comboBox.setObjectName(u"artist_comboBox") - - self.verticalLayout_7.addWidget(self.artist_comboBox) - - - self.gridLayout_7.addWidget(self.widget_7, 3, 0, 1, 1) - - self.widget_4 = QWidget(self.exif_options_group) - self.widget_4.setObjectName(u"widget_4") - self.verticalLayout_4 = QVBoxLayout(self.widget_4) - self.verticalLayout_4.setObjectName(u"verticalLayout_4") - self.label_4 = QLabel(self.widget_4) - self.label_4.setObjectName(u"label_4") - - self.verticalLayout_4.addWidget(self.label_4) - - self.iso_comboBox = QComboBox(self.widget_4) - self.iso_comboBox.setObjectName(u"iso_comboBox") - - self.verticalLayout_4.addWidget(self.iso_comboBox) - - - self.gridLayout_7.addWidget(self.widget_4, 1, 1, 1, 1) - - self.widget_6 = QWidget(self.exif_options_group) - self.widget_6.setObjectName(u"widget_6") - self.verticalLayout_6 = QVBoxLayout(self.widget_6) - self.verticalLayout_6.setObjectName(u"verticalLayout_6") - self.label_6 = QLabel(self.widget_6) - self.label_6.setObjectName(u"label_6") - - self.verticalLayout_6.addWidget(self.label_6) - - self.user_comment_comboBox = QComboBox(self.widget_6) - self.user_comment_comboBox.setObjectName(u"user_comment_comboBox") - - self.verticalLayout_6.addWidget(self.user_comment_comboBox) - - - self.gridLayout_7.addWidget(self.widget_6, 2, 1, 1, 1) - - self.widget_2 = QWidget(self.exif_options_group) - self.widget_2.setObjectName(u"widget_2") - self.verticalLayout_2 = QVBoxLayout(self.widget_2) - self.verticalLayout_2.setObjectName(u"verticalLayout_2") - self.label_2 = QLabel(self.widget_2) - self.label_2.setObjectName(u"label_2") - - self.verticalLayout_2.addWidget(self.label_2) - - self.lens_comboBox = QComboBox(self.widget_2) - self.lens_comboBox.setObjectName(u"lens_comboBox") - - self.verticalLayout_2.addWidget(self.lens_comboBox) - - - self.gridLayout_7.addWidget(self.widget_2, 1, 0, 1, 1) - - self.widget_5 = QWidget(self.exif_options_group) - self.widget_5.setObjectName(u"widget_5") - self.verticalLayout_5 = QVBoxLayout(self.widget_5) - self.verticalLayout_5.setObjectName(u"verticalLayout_5") - self.label_5 = QLabel(self.widget_5) - self.label_5.setObjectName(u"label_5") - - self.verticalLayout_5.addWidget(self.label_5) - - self.image_description_comboBox = QComboBox(self.widget_5) - self.image_description_comboBox.setObjectName(u"image_description_comboBox") - - self.verticalLayout_5.addWidget(self.image_description_comboBox) - - - self.gridLayout_7.addWidget(self.widget_5, 2, 0, 1, 1) - - self.widget = QWidget(self.exif_options_group) - self.widget.setObjectName(u"widget") - self.verticalLayout = QVBoxLayout(self.widget) - self.verticalLayout.setObjectName(u"verticalLayout") - self.label = QLabel(self.widget) + self.label = QLabel(self.exif_options_group) self.label.setObjectName(u"label") - self.verticalLayout.addWidget(self.label) + self.gridLayout_7.addWidget(self.label, 0, 0, 1, 1) - self.make_comboBox = QComboBox(self.widget) - self.make_comboBox.setObjectName(u"make_comboBox") + self.image_description_comboBox = QComboBox(self.exif_options_group) + self.image_description_comboBox.setObjectName(u"image_description_comboBox") - self.verticalLayout.addWidget(self.make_comboBox) + self.gridLayout_7.addWidget(self.image_description_comboBox, 5, 0, 1, 1) + self.label_4 = QLabel(self.exif_options_group) + self.label_4.setObjectName(u"label_4") - self.gridLayout_7.addWidget(self.widget, 0, 0, 1, 1) + self.gridLayout_7.addWidget(self.label_4, 2, 1, 1, 1) - self.widget_3 = QWidget(self.exif_options_group) - self.widget_3.setObjectName(u"widget_3") - self.verticalLayout_3 = QVBoxLayout(self.widget_3) - self.verticalLayout_3.setObjectName(u"verticalLayout_3") - self.label_3 = QLabel(self.widget_3) - self.label_3.setObjectName(u"label_3") + self.time_comboBox = QComboBox(self.exif_options_group) + self.time_comboBox.setObjectName(u"time_comboBox") - self.verticalLayout_3.addWidget(self.label_3) + self.gridLayout_7.addWidget(self.time_comboBox, 9, 1, 1, 1) - self.model_comboBox = QComboBox(self.widget_3) + self.dev_comboBox = QComboBox(self.exif_options_group) + self.dev_comboBox.setObjectName(u"dev_comboBox") + + self.gridLayout_7.addWidget(self.dev_comboBox, 9, 0, 1, 1) + + self.label_5 = QLabel(self.exif_options_group) + self.label_5.setObjectName(u"label_5") + + self.gridLayout_7.addWidget(self.label_5, 4, 0, 1, 1) + + self.model_comboBox = QComboBox(self.exif_options_group) self.model_comboBox.setObjectName(u"model_comboBox") - self.verticalLayout_3.addWidget(self.model_comboBox) + self.gridLayout_7.addWidget(self.model_comboBox, 1, 1, 1, 1) + self.lens_comboBox = QComboBox(self.exif_options_group) + self.lens_comboBox.setObjectName(u"lens_comboBox") - self.gridLayout_7.addWidget(self.widget_3, 0, 1, 1, 1) + self.gridLayout_7.addWidget(self.lens_comboBox, 3, 0, 1, 1) - self.widget_8 = QWidget(self.exif_options_group) - self.widget_8.setObjectName(u"widget_8") - self.verticalLayout_8 = QVBoxLayout(self.widget_8) - self.verticalLayout_8.setObjectName(u"verticalLayout_8") - self.label_8 = QLabel(self.widget_8) + self.user_comment_comboBox = QComboBox(self.exif_options_group) + self.user_comment_comboBox.setObjectName(u"user_comment_comboBox") + + self.gridLayout_7.addWidget(self.user_comment_comboBox, 5, 1, 1, 1) + + self.make_comboBox = QComboBox(self.exif_options_group) + self.make_comboBox.setObjectName(u"make_comboBox") + + self.gridLayout_7.addWidget(self.make_comboBox, 1, 0, 1, 1) + + self.label_14 = QLabel(self.exif_options_group) + self.label_14.setObjectName(u"label_14") + + self.gridLayout_7.addWidget(self.label_14, 8, 0, 1, 1) + + self.label_8 = QLabel(self.exif_options_group) self.label_8.setObjectName(u"label_8") - self.verticalLayout_8.addWidget(self.label_8) + self.gridLayout_7.addWidget(self.label_8, 6, 1, 1, 1) - self.copyright_info_comboBox = QComboBox(self.widget_8) + self.label_3 = QLabel(self.exif_options_group) + self.label_3.setObjectName(u"label_3") + + self.gridLayout_7.addWidget(self.label_3, 0, 1, 1, 1) + + self.label_6 = QLabel(self.exif_options_group) + self.label_6.setObjectName(u"label_6") + + self.gridLayout_7.addWidget(self.label_6, 4, 1, 1, 1) + + self.copyright_info_comboBox = QComboBox(self.exif_options_group) self.copyright_info_comboBox.setObjectName(u"copyright_info_comboBox") - self.verticalLayout_8.addWidget(self.copyright_info_comboBox) + self.gridLayout_7.addWidget(self.copyright_info_comboBox, 7, 1, 1, 1) + self.iso_comboBox = QComboBox(self.exif_options_group) + self.iso_comboBox.setObjectName(u"iso_comboBox") - self.gridLayout_7.addWidget(self.widget_8, 3, 1, 1, 1) + self.gridLayout_7.addWidget(self.iso_comboBox, 3, 1, 1, 1) + + self.label_15 = QLabel(self.exif_options_group) + self.label_15.setObjectName(u"label_15") + + self.gridLayout_7.addWidget(self.label_15, 8, 1, 1, 1) + + self.label_2 = QLabel(self.exif_options_group) + self.label_2.setObjectName(u"label_2") + + self.gridLayout_7.addWidget(self.label_2, 2, 0, 1, 1) + + self.artist_comboBox = QComboBox(self.exif_options_group) + self.artist_comboBox.setObjectName(u"artist_comboBox") + + self.gridLayout_7.addWidget(self.artist_comboBox, 7, 0, 1, 1) self.verticalLayout_9.addWidget(self.exif_options_group) @@ -543,7 +507,7 @@ class Ui_MainWindow(object): self.dateEdit = QDateEdit(self.date_groupBox) self.dateEdit.setObjectName(u"dateEdit") self.dateEdit.setEnabled(False) - self.dateEdit.setDateTime(QDateTime(QDate(2024, 12, 31), QTime(22, 0, 0))) + self.dateEdit.setDateTime(QDateTime(QDate(2024, 12, 31), QTime(20, 0, 0))) self.dateEdit.setMaximumDate(QDate(2038, 12, 31)) self.dateEdit.setMinimumDate(QDate(1970, 1, 1)) self.dateEdit.setCalendarPopup(True) @@ -563,7 +527,7 @@ class Ui_MainWindow(object): MainWindow.setStatusBar(self.statusBar) self.menuBar = QMenuBar(MainWindow) self.menuBar.setObjectName(u"menuBar") - self.menuBar.setGeometry(QRect(0, 0, 450, 19)) + self.menuBar.setGeometry(QRect(0, 0, 450, 26)) self.menuHelp = QMenu(self.menuBar) self.menuHelp.setObjectName(u"menuHelp") self.menuSettings = QMenu(self.menuBar) @@ -710,15 +674,17 @@ class Ui_MainWindow(object): self.edit_exif_button.setText(QCoreApplication.translate("MainWindow", u"EXIF editor", None)) self.exif_options_group.setTitle(QCoreApplication.translate("MainWindow", u"Essential EXIF Info", None)) self.label_7.setText(QCoreApplication.translate("MainWindow", u"Artist", None)) - self.label_4.setText(QCoreApplication.translate("MainWindow", u"ISO", None)) - self.label_6.setText(QCoreApplication.translate("MainWindow", u"Scanner", None)) - self.label_2.setText(QCoreApplication.translate("MainWindow", u"Lens", None)) - self.label_5.setText(QCoreApplication.translate("MainWindow", u"Film", None)) self.label.setText(QCoreApplication.translate("MainWindow", u"Make", None)) + self.label_4.setText(QCoreApplication.translate("MainWindow", u"ISO", None)) + self.label_5.setText(QCoreApplication.translate("MainWindow", u"Film", None)) self.make_comboBox.setCurrentText("") self.make_comboBox.setPlaceholderText("") - self.label_3.setText(QCoreApplication.translate("MainWindow", u"Model", None)) + self.label_14.setText(QCoreApplication.translate("MainWindow", u"Developer", None)) self.label_8.setText(QCoreApplication.translate("MainWindow", u"Copyright", None)) + self.label_3.setText(QCoreApplication.translate("MainWindow", u"Model", None)) + self.label_6.setText(QCoreApplication.translate("MainWindow", u"Scanner", None)) + self.label_15.setText(QCoreApplication.translate("MainWindow", u"Time", None)) + self.label_2.setText(QCoreApplication.translate("MainWindow", u"Lens", None)) self.gps_groupBox.setTitle(QCoreApplication.translate("MainWindow", u"GPS Coordinates", None)) #if QT_CONFIG(tooltip) self.gps_checkBox.setToolTip(QCoreApplication.translate("MainWindow", u"From a Homepage like latlong.net", None)) diff --git a/src/OptimaLab35/ui/main_window.ui b/src/OptimaLab35/ui/main_window.ui index 6b87b83..8a46292 100644 --- a/src/OptimaLab35/ui/main_window.ui +++ b/src/OptimaLab35/ui/main_window.ui @@ -10,7 +10,7 @@ 0 0 450 - 720 + 696 @@ -672,141 +672,113 @@ Essential EXIF Info - - - - - - - Artist - - - - - - - - - - - - - - - - ISO - - - - - - - - - - - - - - - - Scanner - - - - - - - - - - - - - - - - Lens - - - - - - - - - - - - - - - - Film - - - - - - - + + + + Artist + - - - - - - Make - - - - - - - - - - - - - - + + + Make + + + + + + + + + + ISO + + + + + + + + + + + + + Film + + + + + + + + + + + + + + + + + + + + + + + + + + Developer + + + + + + + Copyright + - - - - - - Model - - - - - - - + + + Model + - - - - - - - Copyright - - - - - - - + + + + Scanner + + + + + + + + + + + Time + + + + + + + Lens + + + + + + @@ -896,7 +868,7 @@ - 22 + 20 0 0 2024 @@ -939,7 +911,7 @@ 0 0 450 - 19 + 26 diff --git a/src/OptimaLab35/ui/preview_window.py b/src/OptimaLab35/ui/preview_window.py index 5caf68c..a1226f1 100644 --- a/src/OptimaLab35/ui/preview_window.py +++ b/src/OptimaLab35/ui/preview_window.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'preview_window.ui' ## -## Created by: Qt User Interface Compiler version 6.8.2 +## Created by: Qt User Interface Compiler version 6.9.0 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ diff --git a/src/OptimaLab35/ui/settings_window.py b/src/OptimaLab35/ui/settings_window.py index e3d7823..4950f84 100644 --- a/src/OptimaLab35/ui/settings_window.py +++ b/src/OptimaLab35/ui/settings_window.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'settings_window.ui' ## -## Created by: Qt User Interface Compiler version 6.8.2 +## Created by: Qt User Interface Compiler version 6.9.0 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################