Compare commits

...

9 commits
1.2.2 ... main

Author SHA1 Message Date
894d444f91
feat: dateEdit is now, instead of static day.
All checks were successful
ci/woodpecker/push/woodpecker_ci Pipeline was successful
ci/woodpecker/tag/woodpecker_ci Pipeline was successful
2025-05-30 12:17:12 +02:00
a89f43ba2e
fix: fixed linkage and changelog
All checks were successful
ci/woodpecker/pr/woodpecker_ci Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker_ci Pipeline was successful
ci/woodpecker/push/woodpecker_ci Pipeline was successful
ci/woodpecker/tag/woodpecker_ci Pipeline was successful
2025-04-11 15:57:58 +02:00
d93d3de859 fix: fixing woodpecker ci (#2)
All checks were successful
ci/woodpecker/push/woodpecker_ci Pipeline was successful
ci/woodpecker/tag/woodpecker_ci Pipeline was successful
Reviewed-on: #2
Co-authored-by: Mr Finchum <mr.finchum@pm.me>
Co-committed-by: Mr Finchum <mr.finchum@pm.me>
2025-04-11 13:41:24 +02:00
011249e002
ci: switch to woodpecker ci.
Some checks failed
ci/woodpecker/pr/woodpecker_ci Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker_ci Pipeline was successful
ci/woodpecker/push/woodpecker_ci Pipeline failed
ci/woodpecker/tag/woodpecker_ci Pipeline was successful
2025-04-11 13:34:29 +02:00
d4e8a25b1e fix: fixed misspelling 2025-04-01 18:37:11 +02:00
419dc1eada patch: better performance for preview window 2025-04-01 16:30:21 +00:00
63422ccd88 fix: fixed app path 2025-03-30 19:29:54 +02:00
d356937e41 fix: fixed insert exif feature 2025-03-27 11:07:58 +00:00
ef92ffb5b9 feat: image adjustments written to exif. 2025-03-24 15:34:29 +01:00
12 changed files with 165 additions and 145 deletions

1
.gitignore vendored
View file

@ -4,3 +4,4 @@ dist/
__pycache__/
.flatpak-builder/
flatpak-build-dir/
*.jpg

View file

@ -1,69 +0,0 @@
---
include:
- local: .gitlab-ci/versioning/gitversion.yml
- local: .gitlab-ci/git/create_tag.yml
stages:
- build
- release
gitversion:
extends: .versioning:gitversion
stage: .pre
tags:
- gitlab-org-docker
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when commits are pushed or merged to the default branch
build:
stage: build
image: python:3.9.21
tags:
- gitlab-org-docker
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when commits are pushed or merged to the default branch
needs:
- job: gitversion
artifacts: true
script:
- sed -i "s/0.0.1/${GitVersion_MajorMinorPatch}/" src/OptimaLab35/__init__.py
- cat src/OptimaLab35/__init__.py
- python3 -m pip install build
- python3 -m build --wheel --sdist -s src
artifacts:
paths:
- src/dist/*
expire_in: 1 day
publish:
stage: release
image: python:3.9.21
tags:
- gitlab-org-docker
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when commits are pushed or merged to the default branch
variables:
TWINE_USERNAME: "__token__"
TWINE_PASSWORD: $TWINE_API
needs:
- job: build
artifacts: true
script:
- python3 -m pip install twine
- python3 -m twine upload src/dist/*
create_tag:
extends: .git:create_tag
stage: release
tags:
- gitlab-org-docker
variables:
VERSION: $GitVersion_SemVer
TOKEN: $GITLAB_TOKEN
needs:
- job: gitversion
artifacts: true
rules:
- if: $CI_COMMIT_TAG
when: never # Do not run this job when a tag is created manually
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when commits are pushed or merged to the default branch

View file

@ -1,15 +0,0 @@
---
.git:create_tag:
image: alpine:3.21
variables:
GIT_STRATEGY: clone
GIT_DEPTH: 0
GIT_LFS_SKIP_SMUDGE: 1
VERSION: ''
TOKEN: '' # Token with push privileges
script:
- apk add git
- git remote set-url origin https://oauth2:$TOKEN@$CI_SERVER_HOST/$CI_PROJECT_PATH
- git tag $VERSION
- git push origin tag $VERSION

View file

@ -1,31 +0,0 @@
---
.versioning:gitversion:
image:
name: mcr.microsoft.com/dotnet/sdk:9.0
variables:
GIT_STRATEGY: clone
GIT_DEPTH: 0 # force a deep/non-shallow fetch need by gitversion
GIT_LFS_SKIP_SMUDGE: 1
cache: [] # caches and before / after scripts can mess things up
script:
- |
dotnet tool install --global GitVersion.Tool --version 5.*
export PATH="$PATH:/root/.dotnet/tools"
dotnet-gitversion -output buildserver
# We could just collect the output file gitversion.properties (with artifacts:report:dotenv: gitversion.properties as it is already in DOTENV format,
# however it contains ~33 variables which unnecessarily consumes many of the 50 max DOTENV variables of the free GitLab version.
# Limits are higher for licensed editions, see https://docs.gitlab.com/ee/ci/yaml/artifacts_reports.html#artifactsreportsdotenv
grep 'GitVersion_LegacySemVer=' gitversion.properties >> gitversion.env
grep 'GitVersion_SemVer=' gitversion.properties >> gitversion.env
grep 'GitVersion_FullSemVer=' gitversion.properties >> gitversion.env
grep 'GitVersion_Major=' gitversion.properties >> gitversion.env
grep 'GitVersion_Minor=' gitversion.properties >> gitversion.env
grep 'GitVersion_Patch=' gitversion.properties >> gitversion.env
grep 'GitVersion_MajorMinorPatch=' gitversion.properties >> gitversion.env
grep 'GitVersion_BuildMetaData=' gitversion.properties >> gitversion.env
artifacts:
reports:
# propagates variables into the pipeline level
dotenv: gitversion.env

View file

@ -0,0 +1,92 @@
steps:
- name: gitversion
depends_on: [] # nothing start emititly
when:
event: push
branch: main
image: mcr.microsoft.com/dotnet/sdk:9.0
environment:
CI_TOKEN:
from_secret: CI_TOKEN
commands:
- git remote set-url origin https://CodeByMrFinchum:$CI_TOKEN@code.boxyfoxy.net/$CI_REPO.git
- git fetch --unshallow --tags
- apt-get update && apt-get install -y jq
- dotnet tool install --global GitVersion.Tool --version 5.*
- export PATH="$PATH:/root/.dotnet/tools"
- dotnet-gitversion -output json > version.json
- ls
- cat version.json
- |
echo "GitVersion_SemVer=$(jq -r '.SemVer' version.json)" >> gitversion.env
echo "GitVersion_LegacySemVer=$(jq -r '.LegacySemVer' version.json)" >> gitversion.env
echo "GitVersion_FullSemVer=$(jq -r '.FullSemVer' version.json)" >> gitversion.env
echo "GitVersion_Major=$(jq -r '.Major' version.json)" >> gitversion.env
echo "GitVersion_Minor=$(jq -r '.Minor' version.json)" >> gitversion.env
echo "GitVersion_Patch=$(jq -r '.Patch' version.json)" >> gitversion.env
echo "GitVersion_MajorMinorPatch=$(jq -r '.MajorMinorPatch' version.json)" >> gitversion.env
echo "GitVersion_BuildMetaData=$(jq -r '.BuildMetaData' version.json)" >> gitversion.env
- name: tagging
depends_on: [gitversion]
when:
event: push
branch: main
image: alpine/git
environment:
CI_TOKEN:
from_secret: CI_TOKEN
commands:
- ls
- cat gitversion.env
- git config --global user.email "ci@noreply.boxyfoxy.net"
- git config --global user.name "CI Bot"
- git remote set-url origin https://CodeByMrFinchum:$${CI_TOKEN}@code.boxyfoxy.net/$${CI_REPO}.git
- . gitversion.env
- git tag $GitVersion_SemVer
- git push origin tag $GitVersion_SemVer
- name: build
depends_on: [gitversion, tagging]
when:
event: push
branch: main
image: python:3.9.21
commands:
- ls
- cat gitversion.env
- export $(cat gitversion.env | xargs)
- sed -i "s/0.0.1/$GitVersion_SemVer/" src/OptimaLab35/__init__.py
- cat src/OptimaLab35/__init__.py
- python3 -m pip install build
- python3 -m build --wheel --sdist -s src
- name: publish_pypi
depends_on: [gitversion, tagging, build]
when:
event: push
branch: main
image: python:3.9.21
environment:
TWINE_PASSWORD:
from_secret: TWINE_API
TWINE_USERNAME: "__token__"
commands:
- ls
- python3 -m pip install twine
- python3 -m twine upload src/dist/*
- name: publish_forgejo
depends_on: [gitversion, tagging, build]
when:
event: push
branch: main
image: python:3.9.21
environment:
TWINE_PASSWORD:
from_secret: PKG_TOKEN
TWINE_USERNAME: "CodeByMrFinchum"
commands:
- ls
- python3 -m pip install twine
- python3 -m twine upload --repository-url https://code.boxyfoxy.net/api/packages/CodeByMrFinchum/pypi src/dist/*

View file

@ -1,22 +1,57 @@
# Changelog
## 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
- Fixed Changelog
### 1.4.1: Fix CI
- Fixed pipline
### 1.4.0: New CI
- Migrated repo from GitLab to my forgejo instance, therefore switching to woodpecker CI
---
## 1.3.x
### 1.3.4: Fix - Spelling (25.04.01)
- Fixed misspelling in preview window.
### 1.3.3: Patch - Increased Preview Performance x2 (25.04.01)
- Reduced preview stutter: Previously, the image was updated twice when changing brightness or contrast. Now, it updates only once, improving performance by 100%.
- There is still room for improvement. Analysis shows that image processing takes the most time, while displaying in Qt is relatively fast. Reducing the image size impacts performance, so resizing to 50% is a good idea.
- There is an issue where `QRunner` does not always finish in the correct order when brightness or contrast values are changed rapidly. ATM I do not know how to fix this easly.
- The "Preview" watermark now displays the brightness and contrast levels of the shown image.
### 1.3.2: Fix - Fixed problem with app folders (25.03.30)
- Fixed a problem that the app folder path was not generated correctly.
### 1.3.1: Fix - Fixed insert exif not working (25.03.27)
- Fixed the feature that inserted exif into images without modifying them.
### 1.3.0: Feature - Write Adjustments into EXIF (25.03.24)
- Changes to contrast and brightness are now recorded in the EXIF comment section (labeled *Scanner* in the program).
- This allows users to track what adjustments were made to an image.
---
## 1.2.x
### 1.2.2 Patch: Pyproject file (25.03.23)
### 1.2.2: Patch - Pyproject file (25.03.23)
- Fixed `Development Status` Classifier
- Added <4.0 python version
### 1.2.1 Patch: Changelog file (25.03.23)
### 1.2.1: Patch - Changelog file (25.03.23)
- Patches formation in changelog file.
### 1.2.0 Refactor: Splitting Classes into Separate Files (25.03.23)
### 1.2.0: Refactor - Splitting Classes into Separate Files (25.03.23)
- Refactored `gui.py`, which previously contained almost all the code, into multiple files.
- Each window's logic is now in its own file, improving code organization.
- Window layouts remain in `.ui` folder, while their logic is now properly separated.
## 1.1.x
### 1.1.0 Feature: New Function in Preview Window (25.03.23)
### 1.1.0: Feature - New Function in Preview Window (25.03.23)
- Added a new feature to the preview window: **Hold a button to temporarily view the original (unedited) image.** This makes it easier to compare changes.
- Minor UI adjustments.
@ -149,7 +184,7 @@
- Improved error handling for updater: now displays the specific error message instead of just **"error"** when an issue occurs during update checks.
- Ensured all child windows close when the main window is closed.
### 0.8.3: Fix OptimaLab35 Not Closing After Update
### 0.8.3: Fix - OptimaLab35 Not Closing After Update
- Fixed an issue where **OptimaLab35** would not close properly when updating, resulting in an unresponsive instance and multiple running processes.
### 0.8.2: Patch for New PyPiUpdater Version

View file

@ -1,11 +1,11 @@
# **OptimaLab35**
Last updated: 06 Mar 2025 (v.1.0.0)
Developed on my [forgejo instance](https://code.boxyfoxy.net/CodeByMrFinchum), [GitLab](https://gitlab.com/CodeByMrFinchum) is used as backup.
## **Overview**
**OptimaLab35** enhances **OPTIMA35** (**Organizing, Processing, Tweaking Images, and Modifying scanned Analogs from 35mm Film**) by offering a user-friendly graphical interface for efficient image and metadata management.
It serves as a GUI for the [OPTIMA35 library](https://gitlab.com/CodeByMrFinchum/optima35), providing an intuitive way to interact with the core functionalities.
It serves as a GUI for the [optima35 library](https://code.boxyfoxy.net/CodeByMrFinchum/optima35), providing an intuitive way to interact with the core functionalities.
---
@ -55,28 +55,28 @@ The UI is OS-dependent, but a custom theme can be enabled in the settings.
**Main tab**
![main](https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/raw/main/media/mainwindow_light.png){width=40%}
![main](https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/raw/main/media/mainwindow_dark.png){width=40%}
![main](https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35/raw/branch/main/media/mainwindow_light.png){width=40%}
![main](https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35/raw/branch/main/media/mainwindow_dark.png){width=40%}
**Exif tab**
![main](https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/raw/main/media/mainwindow_exif_light.png){width=40%}
![main](https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/raw/main/media/mainwindow_exif_dark.png){width=40%}
![main](https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35/raw/branch/main/media/mainwindow_exif_light.png){width=40%}
![main](https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35/raw/branch/main/media/mainwindow_exif_dark.png){width=40%}
**Preview window**
![main](https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/raw/main/media/previewwindow_light.png){width=40%}
![main](https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/raw/main/media/previewwindow_dark.png){width=40%}
![main](https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35/raw/branch/main/media/previewwindow_light.png){width=40%}
![main](https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35/raw/branch/main/media/previewwindow_dark.png){width=40%}
**Settings**
![main](https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/raw/main/media/settingswindow_light.png){width=40%}
![main](https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/raw/main/media/settingswindow_dark.png){width=40%}
![main](https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35/raw/branch/main/media/settingswindow_light.png){width=40%}
![main](https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35/raw/branch/main/media/settingswindow_dark.png){width=40%}
**Updater**
![main](https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/raw/main/media/settingswindow_updater_light.png){width=40%}
![main](https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/raw/main/media/settingswindow_updater_dark.png){width=40%}
![main](https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35/raw/branch/main/media/settingswindow_updater_light.png){width=40%}
![main](https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35/raw/branch/main/media/settingswindow_updater_dark.png){width=40%}
---

View file

@ -1,4 +1,6 @@
**[OptimaLab35](https://gitlab.com/CodeByMrFinchum/OptimaLab35)** is a graphical interface for the [OPTIMA35 library](https://gitlab.com/CodeByMrFinchum/optima35), designed for efficient image and metadata management.
**[OptimaLab35](https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35)** is a graphical interface for the [optima35 library](https://code.boxyfoxy.net/CodeByMrFinchum/optima35), designed for efficient image and metadata management.
Developed on my [forgejo instance](https://code.boxyfoxy.net/CodeByMrFinchum), [GitLab](https://gitlab.com/CodeByMrFinchum) is used as backup.
### **Features**
- Resize, adjust brightness/contrast, and convert images to grayscale

View file

@ -25,7 +25,8 @@ from PySide6.QtCore import (
Signal,
QObject,
QRegularExpression,
Qt
Qt,
QDate
)
from PySide6.QtWidgets import (
@ -96,7 +97,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
validator = QRegularExpressionValidator(regex)
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()
@ -130,7 +131,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
<p>For more details, visit:</p>
<ul>
<li><a href="https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/blob/main/CHANGELOG.md?ref_type=heads">Changelog</a></li>
<li><a href="https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35/src/branch/main/CHANGELOG.md">Changelog</a></li>
<li><a href="https://gitlab.com/CodeByMrFinchum/OptimaLab35">OptimaLab35 GitLab</a></li>
<li><a href="https://gitlab.com/CodeByMrFinchum/optima35">optima35 GitLab</a></li>
</ul>
@ -341,7 +342,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
def control_ending(self, name_lst):
for item in name_lst:
try:
int(item[-1])
int(item[-5])
except ValueError:
return False
return True
@ -377,6 +378,7 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
return
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")
self.toggle_buttons(True)
@ -415,10 +417,14 @@ class OptimaLab35(QMainWindow, Ui_MainWindow):
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()
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.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()
return user_data
def get_selected_exif(self):

View file

@ -50,10 +50,8 @@ class PreviewWindow(QMainWindow, Ui_Preview_Window):
self.ui.reset_contrast_Button.clicked.connect(lambda: self.ui.contrast_spinBox.setValue(0))
# Connect UI elements to `on_ui_change`
self.ui.brightness_spinBox.valueChanged.connect(self.on_ui_change)
self.ui.brightness_Slider.valueChanged.connect(self.on_ui_change)
self.ui.contrast_spinBox.valueChanged.connect(self.on_ui_change)
self.ui.contrast_Slider.valueChanged.connect(self.on_ui_change)
self.ui.brightness_spinBox.valueChanged.connect(self.on_ui_change) # brightness slider changes spinbox value, do not need an event for the slider
self.ui.contrast_spinBox.valueChanged.connect(self.on_ui_change) # contrast slider changes spinbox value, do not need an event for the slider
self.ui.grayscale_checkBox.stateChanged.connect(self.on_ui_change)
self.ui_elements(False)
self.ui.show_OG_Button.pressed.connect(self.show_OG_image)
@ -154,7 +152,8 @@ class ImageProcessorWorker(QRunnable):
try:
img = self.optima_manager.process_image_object(
image_input_file = self.path,
watermark = "PREVIEW",
watermark = f"PREVIEW B:{self.brightness} C:{self.contrast}",
font_size = 1,
resize = self.resize,
grayscale = self.grayscale,
brightness = self.brightness,

View file

@ -79,7 +79,7 @@ class SettingsWindow(QMainWindow, Ui_Settings_Window):
# Connect button press/release
self.ui.check_for_update_Button.pressed.connect(self.start_long_press)
self.ui.check_for_update_Button.released.connect(self.cancel_long_press)
self.ui.label_5.setText('<li><a href="https://gitlab.com/CodeByMrFinchum/OptimaLab35/-/blob/main/CHANGELOG.md?ref_type=heads">Changelog</a></li>')
self.ui.label_5.setText('<li><a href="https://code.boxyfoxy.net/CodeByMrFinchum/OptimaLab35/src/branch/main/CHANGELOG.md">Changelog</a></li>')
self.ui.label_5.setOpenExternalLinks(True)
#settings related
self.load_settings_into_ui()

View file

@ -3,7 +3,7 @@ import os
class Utilities:
def __init__(self, app_folder_path):
self.folder_path = app_folder_path
self.folder_path = os.path.expanduser(app_folder_path)
self._ensure_program_folder_exists()
self.exif_path = os.path.expanduser(f"{app_folder_path}/exif.yaml")
self.settings_path = os.path.expanduser(f"{app_folder_path}/settings.yaml")