diff --git a/CHANGELOG.md b/CHANGELOG.md index fc1eb38..4bab113 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,27 @@ # Changelog -## 0.1.0 +## 0.2.x +### 0.2.2 +- Moved processing images into a different thread, making the UI responsiable while processing + +### 0.2.1 +- Insert exif to image file (i.e. without changing the file). + +### 0.2.0 +- Now spaces in rename string are replaces with `_`. +- version check of `optima35`, incase pip did not update it. +- Sorting entries from exif file. + +### 0.2.0-a1 +- Main UI received a facelift. +- Added a new experimental preview window to display an image and show how changing values affects it. +- Programm now warns for potential overwrite of existing files. + +## 0.1.x +### 0.1.1 +- Update metadata, preview, readme, bump in version for pip + +### 0.1.0 - Preserved the current working GUI by pinning `optima35` to a specific version for guaranteed compatibility. ## 0.0.x diff --git a/README.md b/README.md index 1a985c8..b627649 100644 --- a/README.md +++ b/README.md @@ -31,22 +31,27 @@ pip install OptimaLab35 - **TUI**: Fallback if **PySide6** is missing or can be explicitly started using the `--tui` flag. ### Preview GUI +**PREVIEW** might be out of date. **Main tab** -{width=40%} +{width=40%} + +**Preview window** + +{width=40%} **Exif tab** -{width=40%} +{width=40%} **Exif editor** -{width=40%} +{width=40%} **Info window** -{width=40%} +{width=40%} ## **Features** diff --git a/media/OptimaLab35.ico b/media/OptimaLab35.ico new file mode 100644 index 0000000..3f664a9 Binary files /dev/null and b/media/OptimaLab35.ico differ diff --git a/media/main_tab.png b/media/main_tab.png index 16a65b4..3c0ebf6 100644 Binary files a/media/main_tab.png and b/media/main_tab.png differ diff --git a/media/preview_window.png b/media/preview_window.png new file mode 100644 index 0000000..2057ce4 Binary files /dev/null and b/media/preview_window.png differ diff --git a/pip_README.md b/pip_README.md new file mode 100644 index 0000000..f36b255 --- /dev/null +++ b/pip_README.md @@ -0,0 +1,4 @@ +OptimaLab35 is in active development, and even *stable* versions may produce errors in unexpected situations. It is a GUI for optima35. +Please visit [OptimaLab35](https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/blob/main/media/exif_editor.png?ref_type=heads) gitlab page for more information. + +Uses [optima35](https://gitlab.com/CodeByMrFinchum/optima35) to modify images. diff --git a/pyproject.toml b/pyproject.toml index 5f83abc..b2796d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,10 +6,10 @@ build-backend = "hatchling.build" name = "OptimaLab35" dynamic = ["version"] authors = [{ name = "Mr. Finchum" }] -description = "User interface for OPTIMA35." -readme = "README.md" +description = "User interface for optima35." +readme = "pip_README.md" requires-python = ">=3.8" -dependencies = ["optima35==0.6.4", "pyside6", "PyYAML"] +dependencies = ["optima35>=0.6.6", "pyside6", "PyYAML", "packaging"] classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)", diff --git a/src/OptimaLab35/__init__.py b/src/OptimaLab35/__init__.py index 485f44a..b5fdc75 100644 --- a/src/OptimaLab35/__init__.py +++ b/src/OptimaLab35/__init__.py @@ -1 +1 @@ -__version__ = "0.1.1" +__version__ = "0.2.2" diff --git a/src/OptimaLab35/gui.py b/src/OptimaLab35/gui.py index 19294fd..ce99d86 100644 --- a/src/OptimaLab35/gui.py +++ b/src/OptimaLab35/gui.py @@ -2,13 +2,18 @@ import sys import os from datetime import datetime +import time + from optima35.core import OptimaManager from OptimaLab35.utils.utility import Utilities from OptimaLab35.ui.main_window import Ui_MainWindow +from OptimaLab35.ui.preview_window import Ui_Preview_Window 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 + from PySide6 import QtWidgets from PySide6.QtWidgets import ( QMessageBox, @@ -23,8 +28,110 @@ from PySide6.QtWidgets import ( QFileDialog, QHBoxLayout, QSpinBox, + QProgressBar, ) +from PySide6.QtGui import QPixmap, QIcon + +class PreviewWindow(QMainWindow, Ui_Preview_Window): + values_selected = Signal(int, int, bool) + + def __init__(self): + super(PreviewWindow, self).__init__() + self.ui = Ui_Preview_Window() + self.ui.setupUi(self) + self.o = OptimaManager() + ## Ui interaction + self.ui.load_Button.clicked.connect(self._browse_file) + self.ui.update_Button.clicked.connect(self._update_preview) + self.ui.close_Button.clicked.connect(self._close_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 + + def _browse_file(self): + file = QFileDialog.getOpenFileName(self, caption = "Select File", filter = ("Images (*.png *.webp *.jpg *.jpeg)")) + if file[0]: + self.ui.image_path_lineEdit.setText(file[0]) + self._update_preview() + + def _update_preview(self): + path = self.ui.image_path_lineEdit.text() + if not os.path.isfile(path): + return + try: + img = self.o.process_image( + save = False, + image_input_file = path, + image_output_file = "", + grayscale = self.ui.grayscale_checkBox.isChecked(), + brightness = int(self.ui.brightness_spinBox.text()), + contrast = int(self.ui.contrast_spinBox.text()) + ) + except Exception as e: + QMessageBox.warning(self, "Warning", "Error loading image...") + print(f"Error loading image...\n{e}") + return + + self.preview_image = QPixmap.fromImage(img) + self.ui.QLabel.setPixmap(self.preview_image) + + def _close_window(self): + # Emit the signal with the values from the spinboxes and checkbox + 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() + +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): + super().__init__() + self.image_files = image_files + self.settings = settings + self.signals = WorkerSignals() + self.signals.progress.connect(progress_callback) + self.o = OptimaManager() + self.u = Utilities() + + def run(self): + input_folder = self.settings["input_folder"] + output_folder = self.settings["output_folder"] + + 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"]) + else: + image_name = os.path.splitext(image_file)[0] + output_path = os.path.join(output_folder, image_name) + + self.o.process_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"] + ) + self.signals.progress.emit(int((i / len(self.image_files)) * 100)) + + self.signals.finished.emit() + class OptimaLab35(QMainWindow, Ui_MainWindow): def __init__(self): super(OptimaLab35, self).__init__() @@ -33,6 +140,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): self.ui = Ui_MainWindow() self.ui.setupUi(self) self.o = OptimaManager() + self.check_version() self.u = Utilities() self.u.program_configs() self.exif_file = os.path.expanduser("~/.config/OptimaLab35/exif.yaml") @@ -44,14 +152,31 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): self.sd = SimpleDialog() self._change_statusbar(f"Using {self.o.name} v{self.o.version}", 5000) + # Instantiate the second window + self.preview_window = PreviewWindow() + self.thread_pool = QThreadPool() # multi thread ChatGPT + + def open_preview_window(self): + self.preview_window.values_selected.connect(self.update_values) + self.preview_window.show() + + def update_values(self, value1, value2, checkbox_state): + # Update main window's widgets with the received values + # ChatGPT + self.ui.brightness_spinBox.setValue(value1) + self.ui.contrast_spinBox.setValue(value2) + self.ui.grayscale_checkBox.setChecked(checkbox_state) def _default_ui_layout(self): self.ui.png_quality_spinBox.setVisible(False) + self.ui.png_quality_Slider.setVisible(False) + self.ui.quality_label_2.setVisible(False) def _define_gui_interaction(self): self.ui.input_folder_button.clicked.connect(self._browse_input_folder) self.ui.output_folder_button.clicked.connect(self._browse_output_folder) - self.ui.start_button.clicked.connect(self._process) + self.ui.start_button.clicked.connect(self._start_process) + self.ui.insert_exif_Button.clicked.connect(self._start_insert_exif) self.ui.image_type.currentIndexChanged.connect(self._update_quality_options) self.ui.exif_checkbox.stateChanged.connect( @@ -60,68 +185,147 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): self.ui.tabWidget.currentChanged.connect(self._on_tab_changed) self.ui.edit_exif_button.clicked.connect(self._open_exif_editor) - self.ui.actionInfo.triggered.connect(self._info_window) + 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) def _info_window(self): + # ChatGPT, mainly info_text = f"""
(C) 2024-2025 Mr. Finchum aka CodeByMrFinchum
{self.name} is a GUI for {self.o.name} (v{self.o.version}).
-For more details, visit:
+Both projects are in active development, for more details, visit:
""" self.sd.show_dialog(f"{self.name} v{self.version}", info_text) - def _process(self): - self.ui.start_button.setEnabled(False) - self._update_settings() # Get all user selected data - input_folder_valid = os.path.exists(self.settings["input_folder"]) - output_folder_valid = os.path.exists(self.settings["output_folder"]) - 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}...") - return + def _prepear_image(self): + pass + def _image_list_from_folder(self, path): + image_files = [ + f for f in os.listdir(path) if f.lower().endswith((".png", ".jpg", ".jpeg", ".webp")) + ] + return image_files + + def _start_process(self): + self._toggle_buttons(False) + self._update_settings() # Get all user selected data input_folder = self.settings["input_folder"] output_folder = self.settings["output_folder"] + if not input_folder or not output_folder: + QMessageBox.warning(self, "Warning", "Input or output folder not selected") + self._toggle_buttons(True) + return + + input_folder_valid = os.path.exists(input_folder) + output_folder_valid = os.path.exists(output_folder) + 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}...") + self._toggle_buttons(True) + return + + image_list = self._image_list_from_folder(input_folder) + if len(image_list) == 0: + QMessageBox.warning(self, "Warning", "Selected folder has no supported files.") + self._toggle_buttons(True) + return + + if len(self._image_list_from_folder(output_folder)) != 0: + reply = QMessageBox.question( + self, + "Confirmation", + "Output folder containes images, which might get overritten, continue?", + QMessageBox.Yes | QMessageBox.No, + ) + + if reply == QMessageBox.No: + self._toggle_buttons(True) + return + + # Create a worker ChatGPT + 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) + + def _handle_qprogressbar(self, value): + self.ui.progressBar.setValue(value) + + def _on_processing_finished(self): + self._toggle_buttons(True) + self._handle_qprogressbar(0) + QMessageBox.information(self, "Information", "Finished!") + + def _toggle_buttons(self, state): + self.ui.start_button.setEnabled(state) + + if self.ui.exif_checkbox.isChecked(): + self.ui.insert_exif_Button.setEnabled(state) + + def _insert_exif(self, image_files): + input_folder = self.settings["input_folder"] - image_files = [ - f for f in os.listdir(input_folder) if f.lower().endswith((".png", ".jpg", ".jpeg", ".webp")) - ] i = 1 for image_file in image_files: - 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(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_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.ui.optimize_checkBox.isChecked(), - 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.user_selected_exif, - gps = self.settings["gps"], - copy_exif = self.settings["copy_exif"]) - self._handle_qprogressbar(i, len(image_files)) + input_path = os.path.join(input_folder, image_file) + + self.o.insert_dict_to_image( + 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 - QMessageBox.information(self, "Information", "Finished") - self.ui.start_button.setEnabled(True) self.ui.progressBar.setValue(0) + def _start_insert_exif(self): + self._toggle_buttons(False) + self._update_settings() # Get all user selected data + input_folder = self.settings["input_folder"] + output_folder = self.settings["output_folder"] + if not input_folder: + QMessageBox.warning(self, "Warning", "Input not selected") + self._toggle_buttons(True) + return + + if output_folder: + reply = QMessageBox.question( + self, + "Confirmation", + "Output folder selected, but insert exif is done to images in input folder, Continue?", + QMessageBox.Yes | QMessageBox.No, + ) + + if reply == QMessageBox.No: + self._toggle_buttons(True) + return + + input_folder_valid = os.path.exists(input_folder) + if not input_folder_valid : + QMessageBox.warning(self, "Warning", f"Input location {input_folder_valid}") + self._toggle_buttons(True) + return + + image_list = self._image_list_from_folder(input_folder) + if len(image_list) == 0: + QMessageBox.warning(self, "Warning", "Selected folder has no supported files.") + self._toggle_buttons(True) + return + + self._insert_exif(image_list) + + self._toggle_buttons(True) + QMessageBox.information(self, "Information", "Finished") + + def _open_exif_editor(self): """Open the EXIF Editor.""" self.exif_editor = ExifEditor(self.available_exif_data) @@ -146,9 +350,27 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): elif index == 0: # Main Tab self._handle_exif_file("write") + def _sort_dict_of_lists(self, input_dict): + # Partily ChatGPT + sorted_dict = {} + for key, lst in input_dict.items(): + # Sort alphabetically for strings, numerically for numbers + if key == "iso": + lst = [int(x) for x in lst] + lst = sorted(lst) + lst = [str(x) for x in lst] + sorted_dict["iso"] = lst + + elif all(isinstance(x, str) for x in lst): + sorted_dict[key] = sorted(lst, key=str.lower) # Case-insensitive sort for strings + + return sorted_dict + + def _handle_exif_file(self, do): if do == "read": - self.available_exif_data = self.u.read_yaml(self.exif_file) + file_dict = self.u.read_yaml(self.exif_file) + self.available_exif_data = self._sort_dict_of_lists(file_dict) elif do == "write": self.u.write_yaml(self.exif_file, self.available_exif_data) @@ -165,6 +387,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): "artist": self.ui.artist_comboBox, "copyright_info": self.ui.copyright_info_comboBox, } + self._populate_comboboxes(combo_mapping) def _populate_comboboxes(self, combo_mapping): @@ -181,13 +404,23 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): # 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") @@ -202,10 +435,6 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): def _change_statusbar(self, msg, timeout = 500): self.ui.statusBar.showMessage(msg, timeout) - def _handle_qprogressbar(self, current, total): - progress = int((100 / total) * current) - self.ui.progressBar.setValue(progress) - 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 @@ -251,18 +480,20 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): self.settings["own_date"] = self._get_checkbox_value(self.ui.add_date_checkBox) # Conditional settings with logic - self.settings["resize"] = self._get_spinbox_value(self.ui.resize_spinBox) if self.ui.resize_checkbox.isChecked() else None - self.settings["brightness"] = self._get_spinbox_value(self.ui.brightness_spinBox) if self.ui.brightness_checkbox.isChecked() else None - self.settings["contrast"] = self._get_spinbox_value(self.ui.contrast_spinBox) if self.ui.contrast_checkbox.isChecked() else None + self.settings["resize"] = int(self.ui.resize_spinBox.text()) if self.ui.resize_spinBox.text() != "100" 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["new_file_names"] = self._get_text_value(self.ui.filename, False) if self.ui.rename_checkbox.isChecked() else False - self.settings["watermark"] = self._get_text_value(self.ui.watermark_lineEdit) if self.ui.watermark_checkbox.isChecked() else None + 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["watermark"] = self.ui.watermark_lineEdit.text() if len(self.ui.watermark_lineEdit.text()) != 0 else None # Handle EXIF data selection if self.settings["own_exif"]: - self.user_selected_exif = self._get_selected_exif() + self.settings["user_selected_exif"] = self._get_selected_exif() else: - self.user_selected_exif = None + self.settings["user_selected_exif"] = None self.settings["gps"] = None def _get_date(self): @@ -280,9 +511,27 @@ class OptimaLab35(QMainWindow, Ui_MainWindow): user_data["user_comment"] = self.ui.user_comment_comboBox.currentText() user_data["artist"] = self.ui.artist_comboBox.currentText() user_data["copyright_info"] = self.ui.copyright_info_comboBox.currentText() - user_data["software"] = f"{self.o.name} {self.o.version}" + user_data["software"] = f"{self.name} {self.version} with {self.o.name} {self.o.version}" return user_data + def closeEvent(self, event): + self.preview_window.close() + + def check_version(self, min_version="0.6.6"): + # Mainly ChatGPT + from packaging import version # Use `packaging` for robust version comparison + + current_version = self.o.version + if version.parse(current_version) < version.parse(min_version): + msg = ( + f"optima35 version {current_version} detected.\n" + f"Minimum required version is {min_version}.\n" + "Please update the core package to continue.\n" + "https://pypi.org/project/optima35/" + ) + QMessageBox.critical(None, "Version Error", msg) + sys.exit(1) + def main(): app = QtWidgets.QApplication(sys.argv) window = OptimaLab35() diff --git a/src/OptimaLab35/ui/exif_handler_window.py b/src/OptimaLab35/ui/exif_handler_window.py index 8564cba..930fb51 100644 --- a/src/OptimaLab35/ui/exif_handler_window.py +++ b/src/OptimaLab35/ui/exif_handler_window.py @@ -69,7 +69,7 @@ class ExifEditor(QMainWindow): self.list_widget.addItem(new_item) self.line_edit.clear() else: - QMessageBox.warning(self, "Warning", "Cannot add an empty item.") + QMessageBox.warning(self, "Warning", f"Cannot add an empty item.\nDelete {self.exif_file}...") def delete_item(self): """Delete the selected item from the list.""" diff --git a/src/OptimaLab35/ui/main_window.py b/src/OptimaLab35/ui/main_window.py index 890eb7f..9738dc7 100644 --- a/src/OptimaLab35/ui/main_window.py +++ b/src/OptimaLab35/ui/main_window.py @@ -20,18 +20,23 @@ from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QDateEdit, QFrame, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit, QMainWindow, QMenu, QMenuBar, QProgressBar, QPushButton, QSizePolicy, - QSpinBox, QStatusBar, QTabWidget, QVBoxLayout, - QWidget) + QSlider, QSpinBox, QStatusBar, QTabWidget, + QVBoxLayout, QWidget) class Ui_MainWindow(object): def setupUi(self, MainWindow): if not MainWindow.objectName(): MainWindow.setObjectName(u"MainWindow") - MainWindow.resize(450, 708) + MainWindow.setWindowModality(Qt.NonModal) + MainWindow.resize(440, 756) MainWindow.setMinimumSize(QSize(350, 677)) - MainWindow.setMaximumSize(QSize(500, 1000)) + MainWindow.setMaximumSize(QSize(1000, 1000)) self.actionInfo = QAction(MainWindow) self.actionInfo.setObjectName(u"actionInfo") + self.actionPreview = QAction(MainWindow) + self.actionPreview.setObjectName(u"actionPreview") + self.actionAbout = QAction(MainWindow) + self.actionAbout.setObjectName(u"actionAbout") self.centralwidget = QWidget(MainWindow) self.centralwidget.setObjectName(u"centralwidget") self.gridLayout = QGridLayout(self.centralwidget) @@ -48,25 +53,28 @@ class Ui_MainWindow(object): self.folder_group.setMaximumSize(QSize(400, 16777215)) self.gridLayout_5 = QGridLayout(self.folder_group) self.gridLayout_5.setObjectName(u"gridLayout_5") + self.input_folder_button = QPushButton(self.folder_group) + self.input_folder_button.setObjectName(u"input_folder_button") + self.input_folder_button.setMinimumSize(QSize(70, 0)) + self.input_folder_button.setFlat(False) + + self.gridLayout_5.addWidget(self.input_folder_button, 0, 1, 1, 1) + + self.output_path = QLineEdit(self.folder_group) + self.output_path.setObjectName(u"output_path") + + self.gridLayout_5.addWidget(self.output_path, 0, 2, 1, 1) + self.input_path = QLineEdit(self.folder_group) self.input_path.setObjectName(u"input_path") self.gridLayout_5.addWidget(self.input_path, 0, 0, 1, 1) - self.output_path = QLineEdit(self.folder_group) - self.output_path.setObjectName(u"output_path") - - self.gridLayout_5.addWidget(self.output_path, 0, 1, 1, 1) - - self.input_folder_button = QPushButton(self.folder_group) - self.input_folder_button.setObjectName(u"input_folder_button") - - self.gridLayout_5.addWidget(self.input_folder_button, 1, 0, 1, 1) - self.output_folder_button = QPushButton(self.folder_group) self.output_folder_button.setObjectName(u"output_folder_button") + self.output_folder_button.setMinimumSize(QSize(70, 0)) - self.gridLayout_5.addWidget(self.output_folder_button, 1, 1, 1, 1) + self.gridLayout_5.addWidget(self.output_folder_button, 0, 3, 1, 1) self.verticalLayout_10.addWidget(self.folder_group) @@ -76,36 +84,38 @@ class Ui_MainWindow(object): self.groupBox.setMaximumSize(QSize(400, 16777215)) self.gridLayout_4 = QGridLayout(self.groupBox) self.gridLayout_4.setObjectName(u"gridLayout_4") - self.resize_checkbox = QCheckBox(self.groupBox) - self.resize_checkbox.setObjectName(u"resize_checkbox") + self.quality_label_2 = QLabel(self.groupBox) + self.quality_label_2.setObjectName(u"quality_label_2") - self.gridLayout_4.addWidget(self.resize_checkbox, 0, 0, 1, 1) - - self.resize_spinBox = QSpinBox(self.groupBox) - self.resize_spinBox.setObjectName(u"resize_spinBox") - self.resize_spinBox.setEnabled(False) - self.resize_spinBox.setMinimum(1) - self.resize_spinBox.setMaximum(200) - self.resize_spinBox.setSingleStep(1) - self.resize_spinBox.setValue(80) - - self.gridLayout_4.addWidget(self.resize_spinBox, 0, 1, 1, 1) - - self.image_type = QComboBox(self.groupBox) - self.image_type.addItem(u"jpg") - self.image_type.addItem(u"png") - self.image_type.addItem(u"webp") - self.image_type.setObjectName(u"image_type") - - self.gridLayout_4.addWidget(self.image_type, 1, 0, 1, 1) + self.gridLayout_4.addWidget(self.quality_label_2, 4, 0, 1, 1) self.jpg_quality_spinBox = QSpinBox(self.groupBox) self.jpg_quality_spinBox.setObjectName(u"jpg_quality_spinBox") self.jpg_quality_spinBox.setMinimum(1) self.jpg_quality_spinBox.setMaximum(100) - self.jpg_quality_spinBox.setValue(80) + self.jpg_quality_spinBox.setValue(90) - self.gridLayout_4.addWidget(self.jpg_quality_spinBox, 1, 1, 1, 1) + self.gridLayout_4.addWidget(self.jpg_quality_spinBox, 3, 3, 1, 1) + + self.label_11 = QLabel(self.groupBox) + self.label_11.setObjectName(u"label_11") + + self.gridLayout_4.addWidget(self.label_11, 0, 0, 1, 1) + + self.optimize_checkBox = QCheckBox(self.groupBox) + self.optimize_checkBox.setObjectName(u"optimize_checkBox") + + self.gridLayout_4.addWidget(self.optimize_checkBox, 0, 3, 1, 1) + + self.png_quality_Slider = QSlider(self.groupBox) + self.png_quality_Slider.setObjectName(u"png_quality_Slider") + self.png_quality_Slider.setMinimum(1) + self.png_quality_Slider.setMaximum(9) + self.png_quality_Slider.setPageStep(1) + self.png_quality_Slider.setSliderPosition(6) + self.png_quality_Slider.setOrientation(Qt.Horizontal) + + self.gridLayout_4.addWidget(self.png_quality_Slider, 4, 2, 1, 1) self.png_quality_spinBox = QSpinBox(self.groupBox) self.png_quality_spinBox.setObjectName(u"png_quality_spinBox") @@ -114,72 +124,140 @@ class Ui_MainWindow(object): self.png_quality_spinBox.setMaximum(9) self.png_quality_spinBox.setValue(6) - self.gridLayout_4.addWidget(self.png_quality_spinBox, 1, 2, 1, 1) + self.gridLayout_4.addWidget(self.png_quality_spinBox, 4, 3, 1, 1) - self.optimize_checkBox = QCheckBox(self.groupBox) - self.optimize_checkBox.setObjectName(u"optimize_checkBox") + self.quality_label_1 = QLabel(self.groupBox) + self.quality_label_1.setObjectName(u"quality_label_1") - self.gridLayout_4.addWidget(self.optimize_checkBox, 0, 2, 1, 1) + self.gridLayout_4.addWidget(self.quality_label_1, 3, 0, 1, 1) + + self.image_type = QComboBox(self.groupBox) + self.image_type.addItem(u"jpg") + self.image_type.addItem(u"png") + self.image_type.addItem(u"webp") + self.image_type.setObjectName(u"image_type") + + self.gridLayout_4.addWidget(self.image_type, 0, 2, 1, 1) + + self.jpg_quality_Slider = QSlider(self.groupBox) + self.jpg_quality_Slider.setObjectName(u"jpg_quality_Slider") + self.jpg_quality_Slider.setMinimum(1) + self.jpg_quality_Slider.setMaximum(100) + self.jpg_quality_Slider.setSliderPosition(90) + self.jpg_quality_Slider.setOrientation(Qt.Horizontal) + + self.gridLayout_4.addWidget(self.jpg_quality_Slider, 3, 2, 1, 1) + + self.label_13 = QLabel(self.groupBox) + self.label_13.setObjectName(u"label_13") + + self.gridLayout_4.addWidget(self.label_13, 5, 0, 1, 1) + + self.resize_Slider = QSlider(self.groupBox) + self.resize_Slider.setObjectName(u"resize_Slider") + self.resize_Slider.setMinimum(1) + self.resize_Slider.setMaximum(200) + self.resize_Slider.setValue(100) + self.resize_Slider.setOrientation(Qt.Horizontal) + + self.gridLayout_4.addWidget(self.resize_Slider, 5, 2, 1, 1) + + self.resize_spinBox = QSpinBox(self.groupBox) + self.resize_spinBox.setObjectName(u"resize_spinBox") + self.resize_spinBox.setEnabled(True) + self.resize_spinBox.setMinimum(1) + self.resize_spinBox.setMaximum(200) + self.resize_spinBox.setSingleStep(1) + self.resize_spinBox.setValue(100) + + self.gridLayout_4.addWidget(self.resize_spinBox, 5, 3, 1, 1) - self.png_quality_spinBox.raise_() - self.resize_checkbox.raise_() - self.resize_spinBox.raise_() - self.image_type.raise_() - self.jpg_quality_spinBox.raise_() - self.optimize_checkBox.raise_() self.verticalLayout_10.addWidget(self.groupBox) self.groupBox_2 = QGroupBox(self.tab_1) self.groupBox_2.setObjectName(u"groupBox_2") self.groupBox_2.setMaximumSize(QSize(400, 16777215)) + self.groupBox_2.setMouseTracking(False) self.gridLayout_3 = QGridLayout(self.groupBox_2) self.gridLayout_3.setObjectName(u"gridLayout_3") - self.watermark_lineEdit = QLineEdit(self.groupBox_2) - self.watermark_lineEdit.setObjectName(u"watermark_lineEdit") - self.watermark_lineEdit.setEnabled(False) + self.label_9 = QLabel(self.groupBox_2) + self.label_9.setObjectName(u"label_9") - self.gridLayout_3.addWidget(self.watermark_lineEdit, 3, 0, 1, 3) + self.gridLayout_3.addWidget(self.label_9, 1, 0, 1, 1) - self.brightness_checkbox = QCheckBox(self.groupBox_2) - self.brightness_checkbox.setObjectName(u"brightness_checkbox") + self.brightness_horizontalSlider = QSlider(self.groupBox_2) + self.brightness_horizontalSlider.setObjectName(u"brightness_horizontalSlider") + self.brightness_horizontalSlider.setMinimum(-100) + self.brightness_horizontalSlider.setMaximum(100) + self.brightness_horizontalSlider.setOrientation(Qt.Horizontal) - self.gridLayout_3.addWidget(self.brightness_checkbox, 0, 0, 1, 1) + self.gridLayout_3.addWidget(self.brightness_horizontalSlider, 1, 1, 1, 1) + + self.brightness_spinBox = QSpinBox(self.groupBox_2) + self.brightness_spinBox.setObjectName(u"brightness_spinBox") + self.brightness_spinBox.setEnabled(True) + self.brightness_spinBox.setMinimum(-100) + self.brightness_spinBox.setMaximum(100) + self.brightness_spinBox.setValue(0) + + self.gridLayout_3.addWidget(self.brightness_spinBox, 1, 2, 1, 1) + + self.contrast_spinBox = QSpinBox(self.groupBox_2) + self.contrast_spinBox.setObjectName(u"contrast_spinBox") + self.contrast_spinBox.setEnabled(True) + self.contrast_spinBox.setMinimum(-100) + self.contrast_spinBox.setMaximum(100) + self.contrast_spinBox.setValue(0) + + self.gridLayout_3.addWidget(self.contrast_spinBox, 4, 2, 1, 1) + + self.label_10 = QLabel(self.groupBox_2) + self.label_10.setObjectName(u"label_10") + + self.gridLayout_3.addWidget(self.label_10, 4, 0, 1, 1) + + self.contrast_horizontalSlider = QSlider(self.groupBox_2) + self.contrast_horizontalSlider.setObjectName(u"contrast_horizontalSlider") + self.contrast_horizontalSlider.setMinimum(-100) + self.contrast_horizontalSlider.setMaximum(100) + self.contrast_horizontalSlider.setOrientation(Qt.Horizontal) + + self.gridLayout_3.addWidget(self.contrast_horizontalSlider, 4, 1, 1, 1) self.grayscale_checkBox = QCheckBox(self.groupBox_2) self.grayscale_checkBox.setObjectName(u"grayscale_checkBox") - self.gridLayout_3.addWidget(self.grayscale_checkBox, 0, 2, 1, 1) + self.gridLayout_3.addWidget(self.grayscale_checkBox, 5, 0, 1, 2) - self.contrast_spinBox = QSpinBox(self.groupBox_2) - self.contrast_spinBox.setObjectName(u"contrast_spinBox") - self.contrast_spinBox.setEnabled(False) - self.contrast_spinBox.setMinimum(-100) - self.contrast_spinBox.setMaximum(100) - self.contrast_spinBox.setValue(10) + self.preview_Button = QPushButton(self.groupBox_2) + self.preview_Button.setObjectName(u"preview_Button") - self.gridLayout_3.addWidget(self.contrast_spinBox, 1, 1, 1, 1) + self.gridLayout_3.addWidget(self.preview_Button, 5, 2, 1, 1) - self.watermark_checkbox = QCheckBox(self.groupBox_2) - self.watermark_checkbox.setObjectName(u"watermark_checkbox") - self.gridLayout_3.addWidget(self.watermark_checkbox, 2, 0, 1, 1) + self.verticalLayout_10.addWidget(self.groupBox_2) - self.brightness_spinBox = QSpinBox(self.groupBox_2) - self.brightness_spinBox.setObjectName(u"brightness_spinBox") - self.brightness_spinBox.setEnabled(False) - self.brightness_spinBox.setMinimum(-100) - self.brightness_spinBox.setMaximum(100) - self.brightness_spinBox.setValue(-10) + self.groupBox_3 = QGroupBox(self.tab_1) + self.groupBox_3.setObjectName(u"groupBox_3") + self.groupBox_3.setEnabled(True) + self.groupBox_3.setFlat(False) + self.groupBox_3.setCheckable(False) + self.groupBox_3.setChecked(False) + self.horizontalLayout_5 = QHBoxLayout(self.groupBox_3) + self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") + self.watermark_lineEdit = QLineEdit(self.groupBox_3) + self.watermark_lineEdit.setObjectName(u"watermark_lineEdit") + self.watermark_lineEdit.setEnabled(True) - self.gridLayout_3.addWidget(self.brightness_spinBox, 0, 1, 1, 1) + self.horizontalLayout_5.addWidget(self.watermark_lineEdit) - self.contrast_checkbox = QCheckBox(self.groupBox_2) - self.contrast_checkbox.setObjectName(u"contrast_checkbox") + self.label_12 = QLabel(self.groupBox_3) + self.label_12.setObjectName(u"label_12") - self.gridLayout_3.addWidget(self.contrast_checkbox, 1, 0, 1, 1) + self.horizontalLayout_5.addWidget(self.label_12) - self.font_size_comboBox = QComboBox(self.groupBox_2) + self.font_size_comboBox = QComboBox(self.groupBox_3) self.font_size_comboBox.addItem("") self.font_size_comboBox.addItem("") self.font_size_comboBox.addItem("") @@ -187,10 +265,10 @@ class Ui_MainWindow(object): self.font_size_comboBox.addItem("") self.font_size_comboBox.setObjectName(u"font_size_comboBox") - self.gridLayout_3.addWidget(self.font_size_comboBox, 2, 1, 1, 1) + self.horizontalLayout_5.addWidget(self.font_size_comboBox) - self.verticalLayout_10.addWidget(self.groupBox_2) + self.verticalLayout_10.addWidget(self.groupBox_3) self.rename_group = QGroupBox(self.tab_1) self.rename_group.setObjectName(u"rename_group") @@ -234,6 +312,12 @@ class Ui_MainWindow(object): self.horizontalLayout_3.addWidget(self.start_button) + self.insert_exif_Button = QPushButton(self.widget_9) + self.insert_exif_Button.setObjectName(u"insert_exif_Button") + self.insert_exif_Button.setEnabled(False) + + self.horizontalLayout_3.addWidget(self.insert_exif_Button) + self.verticalLayout_10.addWidget(self.widget_9) @@ -465,29 +549,41 @@ class Ui_MainWindow(object): MainWindow.setStatusBar(self.statusBar) self.menuBar = QMenuBar(MainWindow) self.menuBar.setObjectName(u"menuBar") - self.menuBar.setGeometry(QRect(0, 0, 450, 27)) - self.menuInfo = QMenu(self.menuBar) - self.menuInfo.setObjectName(u"menuInfo") + 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.menuInfo.menuAction()) - self.menuInfo.addAction(self.actionInfo) + 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) - self.resize_checkbox.toggled.connect(self.resize_spinBox.setEnabled) - self.brightness_checkbox.toggled.connect(self.brightness_spinBox.setEnabled) - self.contrast_checkbox.toggled.connect(self.contrast_spinBox.setEnabled) - self.watermark_checkbox.toggled.connect(self.watermark_lineEdit.setEnabled) self.rename_checkbox.toggled.connect(self.filename.setEnabled) self.exif_checkbox.toggled.connect(self.exif_options_group.setEnabled) - self.exif_checkbox.toggled.connect(self.exif_copy_checkBox.setDisabled) + self.jpg_quality_Slider.valueChanged.connect(self.jpg_quality_spinBox.setValue) self.exif_copy_checkBox.toggled.connect(self.exif_checkbox.setDisabled) + self.exif_checkbox.toggled.connect(self.exif_copy_checkBox.setDisabled) + self.resize_spinBox.valueChanged.connect(self.resize_Slider.setValue) + self.png_quality_spinBox.valueChanged.connect(self.png_quality_Slider.setValue) + self.contrast_horizontalSlider.valueChanged.connect(self.contrast_spinBox.setValue) + self.resize_Slider.valueChanged.connect(self.resize_spinBox.setValue) + self.brightness_horizontalSlider.valueChanged.connect(self.brightness_spinBox.setValue) + self.exif_checkbox.toggled.connect(self.date_groupBox.setEnabled) + self.png_quality_Slider.valueChanged.connect(self.png_quality_spinBox.setValue) + self.gps_checkBox.toggled.connect(self.lat_lineEdit.setEnabled) + self.exif_checkbox.toggled.connect(self.insert_exif_Button.setEnabled) + self.gps_checkBox.toggled.connect(self.long_lineEdit.setEnabled) + self.brightness_spinBox.valueChanged.connect(self.brightness_horizontalSlider.setValue) + self.resize_Slider.valueChanged.connect(self.resize_spinBox.setValue) + self.exif_checkbox.toggled.connect(self.gps_groupBox.setEnabled) + self.contrast_spinBox.valueChanged.connect(self.contrast_horizontalSlider.setValue) self.exif_checkbox.toggled.connect(self.edit_exif_button.setEnabled) self.add_date_checkBox.toggled.connect(self.dateEdit.setEnabled) - self.exif_checkbox.toggled.connect(self.date_groupBox.setEnabled) - self.exif_checkbox.toggled.connect(self.gps_groupBox.setEnabled) - self.gps_checkBox.toggled.connect(self.lat_lineEdit.setEnabled) - self.gps_checkBox.toggled.connect(self.long_lineEdit.setEnabled) + self.jpg_quality_spinBox.valueChanged.connect(self.jpg_quality_Slider.setValue) self.tabWidget.setCurrentIndex(0) self.font_size_comboBox.setCurrentIndex(2) @@ -497,25 +593,32 @@ class Ui_MainWindow(object): # setupUi def retranslateUi(self, MainWindow): - MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"OPTIMA-35", None)) - self.actionInfo.setText(QCoreApplication.translate("MainWindow", u"Info", None)) - self.input_path.setText("") - self.input_path.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Enter input folder", None)) + MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"OptimaLab35", None)) + self.actionInfo.setText(QCoreApplication.translate("MainWindow", u"About", None)) + self.actionPreview.setText(QCoreApplication.translate("MainWindow", u"Preview image", None)) + self.actionAbout.setText(QCoreApplication.translate("MainWindow", u"About", None)) + self.input_folder_button.setText(QCoreApplication.translate("MainWindow", u"Input", None)) self.output_path.setText("") - self.output_path.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Enter output folder", None)) - self.input_folder_button.setText(QCoreApplication.translate("MainWindow", u"input", None)) - self.output_folder_button.setText(QCoreApplication.translate("MainWindow", u"output", None)) + self.output_path.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Output folder", None)) + self.input_path.setText("") + self.input_path.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Input folder", None)) + self.output_folder_button.setText(QCoreApplication.translate("MainWindow", u"Output", None)) self.groupBox.setTitle(QCoreApplication.translate("MainWindow", u"Essential group", None)) - self.resize_checkbox.setText(QCoreApplication.translate("MainWindow", u"Resize", None)) - + self.quality_label_2.setText(QCoreApplication.translate("MainWindow", u"Quality", None)) + self.label_11.setText(QCoreApplication.translate("MainWindow", u"Export Format", None)) self.optimize_checkBox.setText(QCoreApplication.translate("MainWindow", u"optimize", None)) + self.quality_label_1.setText(QCoreApplication.translate("MainWindow", u"Quality", None)) + + self.label_13.setText(QCoreApplication.translate("MainWindow", u"Resize", None)) self.groupBox_2.setTitle(QCoreApplication.translate("MainWindow", u"Extra stuff", None)) + self.label_9.setText(QCoreApplication.translate("MainWindow", u"Brightness", None)) + self.label_10.setText(QCoreApplication.translate("MainWindow", u"Contrast", None)) + self.grayscale_checkBox.setText(QCoreApplication.translate("MainWindow", u"Turn image to Black and White", None)) + self.preview_Button.setText(QCoreApplication.translate("MainWindow", u"Preview", None)) + self.groupBox_3.setTitle(QCoreApplication.translate("MainWindow", u"Watermark", None)) self.watermark_lineEdit.setText("") self.watermark_lineEdit.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Enter Watermark", None)) - self.brightness_checkbox.setText(QCoreApplication.translate("MainWindow", u"Brightness", None)) - self.grayscale_checkBox.setText(QCoreApplication.translate("MainWindow", u"Grayscale", None)) - self.watermark_checkbox.setText(QCoreApplication.translate("MainWindow", u"Watermark", None)) - self.contrast_checkbox.setText(QCoreApplication.translate("MainWindow", u"Contrast", None)) + self.label_12.setText(QCoreApplication.translate("MainWindow", u"Size", None)) self.font_size_comboBox.setItemText(0, QCoreApplication.translate("MainWindow", u"Tiny", None)) self.font_size_comboBox.setItemText(1, QCoreApplication.translate("MainWindow", u"Small", None)) self.font_size_comboBox.setItemText(2, QCoreApplication.translate("MainWindow", u"Normal", None)) @@ -529,11 +632,12 @@ class Ui_MainWindow(object): self.filename.setText("") self.filename.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Enter file name", None)) self.start_button.setText(QCoreApplication.translate("MainWindow", u"Convert", None)) + self.insert_exif_Button.setText(QCoreApplication.translate("MainWindow", u"Insert Exif", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_1), QCoreApplication.translate("MainWindow", u"Main", None)) self.exif_group.setTitle(QCoreApplication.translate("MainWindow", u"EXIF EXPERIMENTAL", None)) self.exif_checkbox.setText(QCoreApplication.translate("MainWindow", u"own exif", None)) self.exif_copy_checkBox.setText(QCoreApplication.translate("MainWindow", u"copy exif", None)) - self.edit_exif_button.setText(QCoreApplication.translate("MainWindow", u"edit exif", None)) + self.edit_exif_button.setText(QCoreApplication.translate("MainWindow", u"Edit Exif", None)) self.exif_options_group.setTitle(QCoreApplication.translate("MainWindow", u"Must", None)) self.label_7.setText(QCoreApplication.translate("MainWindow", u"Artist", None)) self.label_4.setText(QCoreApplication.translate("MainWindow", u"ISO", None)) @@ -554,6 +658,7 @@ 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.menuInfo.setTitle(QCoreApplication.translate("MainWindow", u"Info", None)) + self.menuSettings.setTitle(QCoreApplication.translate("MainWindow", u"Settings", None)) + self.menuHelp.setTitle(QCoreApplication.translate("MainWindow", u"Help", None)) # retranslateUi diff --git a/src/OptimaLab35/ui/main_window.ui b/src/OptimaLab35/ui/main_window.ui index b75e88f..1466580 100644 --- a/src/OptimaLab35/ui/main_window.ui +++ b/src/OptimaLab35/ui/main_window.ui @@ -2,12 +2,15 @@