From 79f4313cc6dbbad816e7286fb594f73241946bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Andr=C3=A9=20Bj=C3=B8rkede?= Date: Sat, 11 Oct 2025 16:57:26 +0200 Subject: [PATCH 1/6] Add UML documentation --- docs/UML/Class Diagram.puml | 60 +++++++++++++++++++++++++++++++++ docs/UML/Component Diagram.puml | 7 ++++ 2 files changed, 67 insertions(+) create mode 100644 docs/UML/Class Diagram.puml create mode 100644 docs/UML/Component Diagram.puml diff --git a/docs/UML/Class Diagram.puml b/docs/UML/Class Diagram.puml new file mode 100644 index 0000000..bd86e3a --- /dev/null +++ b/docs/UML/Class Diagram.puml @@ -0,0 +1,60 @@ +@startuml + +class GUI{ + This is a singleton + --- + - Image currentImage + - ImageManipulation[] imageManipulations + + void initialise() +} + + +class Image{ + - imagedata image + + void setImage(imagedata) + + imagedata getImage() + + pair getDimensions() +} + +abstract class ImageManipulation{ + +void manipulateImage(Image, Parameters) + +string[] getParameters() + +string getManipulationName() +} + +class CropImage{ +- string name +} + +class GreyScaleImage{ +- string name +} + +class SmoothImage{ +- string name +} + +class RotateImage{ +- string name +} + +class HSVImage{ +- string +} + +class HueShiftImage{ +- string +} + +GUI ..> Image +GUI ..> ImageManipulation + +CropImage ..|> ImageManipulation +GreyScaleImage ..|> ImageManipulation +SmoothImage ..|> ImageManipulation +RotateImage ..|> ImageManipulation +HSVImage ..|> ImageManipulation +HueShiftImage ..|> ImageManipulation + + +@enduml \ No newline at end of file diff --git a/docs/UML/Component Diagram.puml b/docs/UML/Component Diagram.puml new file mode 100644 index 0000000..2dfe922 --- /dev/null +++ b/docs/UML/Component Diagram.puml @@ -0,0 +1,7 @@ +@startuml +[GUI] +[Image Processing] + +[GUI] ..> [Image Processing] + +@enduml \ No newline at end of file From a32cc51fb6c48a2ea4671aaa886647fdca5b6084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Andr=C3=A9=20Bj=C3=B8rkede?= Date: Sat, 11 Oct 2025 17:43:49 +0200 Subject: [PATCH 2/6] Begin refactor --- .gitignore | 177 +++++++++++++++++++++ docs/UML/Class Diagram.puml | 22 +-- src/GUI.py | 117 ++++++++++++++ src/ImageContainer.py | 25 +++ src/ImageManipulation/ImageManipulation.py | 42 +++++ src/Main.py | 5 + 6 files changed, 377 insertions(+), 11 deletions(-) create mode 100644 src/GUI.py create mode 100644 src/ImageContainer.py create mode 100644 src/ImageManipulation/ImageManipulation.py create mode 100644 src/Main.py diff --git a/.gitignore b/.gitignore index abd5980..98da2e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,179 @@ cache/* cache/** + +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/docs/UML/Class Diagram.puml b/docs/UML/Class Diagram.puml index bd86e3a..67eca8f 100644 --- a/docs/UML/Class Diagram.puml +++ b/docs/UML/Class Diagram.puml @@ -5,45 +5,45 @@ class GUI{ --- - Image currentImage - ImageManipulation[] imageManipulations - + void initialise() + + none initialise() } class Image{ - imagedata image - + void setImage(imagedata) + + none loadImage(path: str) + imagedata getImage() + pair getDimensions() } abstract class ImageManipulation{ - +void manipulateImage(Image, Parameters) - +string[] getParameters() - +string getManipulationName() + +none manipulateImage(Image, Parameters) + +str[] getParameters() + +str getManipulationName() } class CropImage{ -- string name +- str name } class GreyScaleImage{ -- string name +- str name } class SmoothImage{ -- string name +- str name } class RotateImage{ -- string name +- str name } class HSVImage{ -- string +- str } class HueShiftImage{ -- string +- str } GUI ..> Image diff --git a/src/GUI.py b/src/GUI.py new file mode 100644 index 0000000..e15cb09 --- /dev/null +++ b/src/GUI.py @@ -0,0 +1,117 @@ +import tkinter as tk +from tkinter import filedialog +from PIL import ImageTk +from ImageContainer import ImageContainer + + +class GUI: + """The GUI class responsible for the main application GUI. + + @warning This class is a singleton. + """ + _instance = None + + # If the GUI has been initialised. + _isInitialised = False + + _currentImage = None + + def __new__(cls): + if cls._instance is None: + cls._instance = super(GUI, cls).__new__(cls) + return cls._instance + + def initialise(self): + """Initialise the GUI.""" + + if self._isInitialised: + return + else: + self._isInitialised = True + + # Main window + root = tk.Tk() + root.title("Image Viewer") + root.geometry("800x600") + root.config(bg="white") + icon = tk.PhotoImage(file='icon.png') + root.tk.call('wm', 'iconphoto', root._w, icon) + root.minsize(800, 600) + + # Menus + menu = tk.Menu(root) + root.config(menu=menu) + file_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="File", menu=file_menu) + file_menu.add_command(label="Open Image", command=lambda: self._openImage()) + file_menu.add_command(label="Save Image", command=lambda: _save_image(OPENED_IMAGE)) + file_menu.add_command(label="Exit", command=root.quit) + + test_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Test", menu=test_menu) + test_menu.add_command(label="Padding", command=lambda: padding(OPENED_IMAGE, + border_width=50) if OPENED_IMAGE is not None else print( + "No image opened.")) + test_menu.add_command(label="Crop", command=lambda: crop(OPENED_IMAGE) if OPENED_IMAGE is not None else print( + "No image opened.")) + test_menu.add_command(label="Resize", + command=lambda: resize(OPENED_IMAGE) if OPENED_IMAGE is not None else print( + "No image opened.")) + test_menu.add_command(label="Copy", command=lambda: copy(OPENED_IMAGE) if OPENED_IMAGE is not None else print( + "No image opened.")) + test_menu.add_command(label="Greyscale", + command=lambda: grayscale(OPENED_IMAGE) if OPENED_IMAGE is not None else print( + "No image opened.")) + test_menu.add_command(label="Hsv", command=lambda: hsv(OPENED_IMAGE) if OPENED_IMAGE is not None else print( + "No image opened.")) + test_menu.add_command(label="Hue Shifted", + command=lambda: hue_shifted(OPENED_IMAGE, hue=50) if OPENED_IMAGE is not None else print( + "No image opened.")) + test_menu.add_command(label="Smoothed", + command=lambda: smoothing(OPENED_IMAGE) if OPENED_IMAGE is not None else print( + "No image opened.")) + test_menu.add_command(label="Rotated", command=lambda: rotation(OPENED_IMAGE, + rotation_angle=90) if OPENED_IMAGE is not None else print( + "No image opened.")) + + clipboard_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Clipboard", menu=clipboard_menu) + + image_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Image", menu=image_menu) + + tools_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Tools", menu=tools_menu) + + shapes_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Shapes", menu=shapes_menu) + + colors_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Colors", menu=colors_menu) + + layer_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Layer", menu=layer_menu) + + filter_menu = tk.Menu(menu, tearoff=0) + menu.add_cascade(label="Filter", menu=filter_menu) + # + + # Frame to hold image + imgframe = tk.Frame(root, width=500, height=500, bg="lightgray", relief="sunken", bd=2) + imgframe.pack(side="top", pady=10) + + # Label to display image + image_label = tk.Label(imgframe, width=500, height=500, bg="white") + image_label.pack(expand=True) + + root.mainloop() + + def _openImage(self) -> None: + file_path = filedialog.askopenfilename( + filetypes=[("Image files", "*.jpg *.jpeg *.png *.gif *.bmp")] + ) + if not file_path: + return + + self._currentImage = ImageContainer() + self._currentImage.loadImage(file_path) \ No newline at end of file diff --git a/src/ImageContainer.py b/src/ImageContainer.py new file mode 100644 index 0000000..4f948eb --- /dev/null +++ b/src/ImageContainer.py @@ -0,0 +1,25 @@ +import cv2 +from PIL import Image, ImageTk + +class ImageContainer: + _imageData = None + _path = None + + def loadImage(self, path: str) -> None: + """ Load image file from the path. + + :param path: Path to the image file. + """ + imgcv2 = cv2.imread(path) + height, width, channels = imgcv2.shape + + # Open and resize image (optional) + _imageData = Image.open(path) + _imageData = _imageData.resize((height, width), Image.LANCZOS) # Resize to fit frame + + _path = path + + print("Opened image:", path) + + def getDimensions(self) -> tuple[int,int]: + return self._imageData.shape \ No newline at end of file diff --git a/src/ImageManipulation/ImageManipulation.py b/src/ImageManipulation/ImageManipulation.py new file mode 100644 index 0000000..907796b --- /dev/null +++ b/src/ImageManipulation/ImageManipulation.py @@ -0,0 +1,42 @@ +from abc import ABC, abstractmethod +from typing import Any + +import sys + +sys.path.append('..') +from ..ImageContainer import ImageContainer + + +class ImageManipulation(ABC): + """Abstract base class for image manipulation operations.""" + + @abstractmethod + def manipulateImage(self, image: ImageContainer, parameters: Any) -> None: + """ + Manipulate the given image using the provided parameters. + + Args: + image: The image to manipulate + parameters: Parameters for the manipulation + """ + pass + + @abstractmethod + def getParameters(self) -> list[str]: + """ + Get the list of parameters required for this manipulation. + + Returns: + List of parameter names + """ + pass + + @abstractmethod + def getManipulationName(self) -> str: + """ + Get the name of this manipulation operation. + + Returns: + The name of the manipulation + """ + pass diff --git a/src/Main.py b/src/Main.py new file mode 100644 index 0000000..d9c6bbc --- /dev/null +++ b/src/Main.py @@ -0,0 +1,5 @@ +from GUI import GUI + +if __name__ == "__main__": + gui = GUI() + gui.initialise() \ No newline at end of file From 0dc89f2e9b1f2062de23e0ae09d7e3932fa4fe6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Andr=C3=A9=20Bj=C3=B8rkede?= Date: Sat, 11 Oct 2025 17:49:38 +0200 Subject: [PATCH 3/6] Add `.idea` configuration --- .idea/.gitignore | 8 ++++++ .idea/vcs.xml | 6 +++++ .idea/workspace.xml | 66 ++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index c26b869..7c5c8df 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,7 +1,13 @@ + + - + + + + - + + + { + "associatedIndex": 7 +} + + + + + \ No newline at end of file From b57ea23e62fcf6360f4322abcdc8698ccf5c1e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Andr=C3=A9=20Bj=C3=B8rkede?= Date: Sat, 11 Oct 2025 17:49:56 +0200 Subject: [PATCH 4/6] Add runConfigurations from PyCharm --- .idea/runConfigurations/Run_project.xml | 26 +++++++++++++++++++++++++ .idea/workspace.xml | 26 ------------------------- 2 files changed, 26 insertions(+), 26 deletions(-) create mode 100644 .idea/runConfigurations/Run_project.xml diff --git a/.idea/runConfigurations/Run_project.xml b/.idea/runConfigurations/Run_project.xml new file mode 100644 index 0000000..7b2c8e5 --- /dev/null +++ b/.idea/runConfigurations/Run_project.xml @@ -0,0 +1,26 @@ + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 7c5c8df..8db849e 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -52,32 +52,6 @@ "vue.rearranger.settings.migration": "true" } }]]> - - - - - From 3024756b9cc821097de06bc9a98ed0a08588c48c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Andr=C3=A9=20Bj=C3=B8rkede?= Date: Sat, 11 Oct 2025 18:11:09 +0200 Subject: [PATCH 5/6] Update UML --- docs/UML/Class Diagram.puml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/UML/Class Diagram.puml b/docs/UML/Class Diagram.puml index 67eca8f..ccce635 100644 --- a/docs/UML/Class Diagram.puml +++ b/docs/UML/Class Diagram.puml @@ -3,50 +3,50 @@ class GUI{ This is a singleton --- - - Image currentImage - - ImageManipulation[] imageManipulations + - ImageContainer _currentImage + - ImageManipulation[] _imageManipulations + none initialise() } -class Image{ - - imagedata image +class ImageContainer{ + - imagedata _imageData + none loadImage(path: str) + imagedata getImage() + pair getDimensions() } abstract class ImageManipulation{ - +none manipulateImage(Image, Parameters) + +none manipulateImage(ImageContainer, Parameters) +str[] getParameters() +str getManipulationName() } class CropImage{ -- str name +- str _name } class GreyScaleImage{ -- str name +- str _name } class SmoothImage{ -- str name +- str _name } class RotateImage{ -- str name +- str _name } class HSVImage{ -- str +- str _name } class HueShiftImage{ -- str +- str _name } -GUI ..> Image +GUI ..> ImageContainer GUI ..> ImageManipulation CropImage ..|> ImageManipulation From 8f1ae488ebc282a16758ac2b5712182cf1de275d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Andr=C3=A9=20Bj=C3=B8rkede?= Date: Sat, 11 Oct 2025 20:48:28 +0200 Subject: [PATCH 6/6] Add CropImage child of ImageManipulation --- README.md | 2 +- requirements.txt | 2 + src/GUI.py | 52 +++-------------------- src/ImageManipulation/CropImage.py | 39 +++++++++++++++++ src/ImageManipulation/ManipulationList.py | 4 ++ src/Main.py | 1 + 6 files changed, 52 insertions(+), 48 deletions(-) create mode 100644 requirements.txt create mode 100644 src/ImageManipulation/CropImage.py create mode 100644 src/ImageManipulation/ManipulationList.py diff --git a/README.md b/README.md index 32ca216..59e6eaf 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ pillow numpy ## run -python main.py +python src/Main.py ## menu items * file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0e0efb4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pillow~=11.3.0 +numpy~=2.3.1 diff --git a/src/GUI.py b/src/GUI.py index e15cb09..20718a4 100644 --- a/src/GUI.py +++ b/src/GUI.py @@ -2,6 +2,7 @@ import tkinter as tk from tkinter import filedialog from PIL import ImageTk from ImageContainer import ImageContainer +from ImageManipulation.ManipulationList import * class GUI: @@ -47,54 +48,11 @@ class GUI: file_menu.add_command(label="Save Image", command=lambda: _save_image(OPENED_IMAGE)) file_menu.add_command(label="Exit", command=root.quit) + test_menu = tk.Menu(menu, tearoff=0) - menu.add_cascade(label="Test", menu=test_menu) - test_menu.add_command(label="Padding", command=lambda: padding(OPENED_IMAGE, - border_width=50) if OPENED_IMAGE is not None else print( - "No image opened.")) - test_menu.add_command(label="Crop", command=lambda: crop(OPENED_IMAGE) if OPENED_IMAGE is not None else print( - "No image opened.")) - test_menu.add_command(label="Resize", - command=lambda: resize(OPENED_IMAGE) if OPENED_IMAGE is not None else print( - "No image opened.")) - test_menu.add_command(label="Copy", command=lambda: copy(OPENED_IMAGE) if OPENED_IMAGE is not None else print( - "No image opened.")) - test_menu.add_command(label="Greyscale", - command=lambda: grayscale(OPENED_IMAGE) if OPENED_IMAGE is not None else print( - "No image opened.")) - test_menu.add_command(label="Hsv", command=lambda: hsv(OPENED_IMAGE) if OPENED_IMAGE is not None else print( - "No image opened.")) - test_menu.add_command(label="Hue Shifted", - command=lambda: hue_shifted(OPENED_IMAGE, hue=50) if OPENED_IMAGE is not None else print( - "No image opened.")) - test_menu.add_command(label="Smoothed", - command=lambda: smoothing(OPENED_IMAGE) if OPENED_IMAGE is not None else print( - "No image opened.")) - test_menu.add_command(label="Rotated", command=lambda: rotation(OPENED_IMAGE, - rotation_angle=90) if OPENED_IMAGE is not None else print( - "No image opened.")) - - clipboard_menu = tk.Menu(menu, tearoff=0) - menu.add_cascade(label="Clipboard", menu=clipboard_menu) - - image_menu = tk.Menu(menu, tearoff=0) - menu.add_cascade(label="Image", menu=image_menu) - - tools_menu = tk.Menu(menu, tearoff=0) - menu.add_cascade(label="Tools", menu=tools_menu) - - shapes_menu = tk.Menu(menu, tearoff=0) - menu.add_cascade(label="Shapes", menu=shapes_menu) - - colors_menu = tk.Menu(menu, tearoff=0) - menu.add_cascade(label="Colors", menu=colors_menu) - - layer_menu = tk.Menu(menu, tearoff=0) - menu.add_cascade(label="Layer", menu=layer_menu) - - filter_menu = tk.Menu(menu, tearoff=0) - menu.add_cascade(label="Filter", menu=filter_menu) - # + menu.add_cascade(label="Filters", menu=test_menu) + for manipulation in GetImageManipulationList(): + test_menu.add_command(label=manipulation.getManipulationName(), command=lambda: manipulation.manipulateImage()) # Frame to hold image imgframe = tk.Frame(root, width=500, height=500, bg="lightgray", relief="sunken", bd=2) diff --git a/src/ImageManipulation/CropImage.py b/src/ImageManipulation/CropImage.py new file mode 100644 index 0000000..2f63014 --- /dev/null +++ b/src/ImageManipulation/CropImage.py @@ -0,0 +1,39 @@ +from .ImageManipulation import ImageManipulation +from ..ImageContainer import ImageContainer +from typing import Any, List + + +class CropImage(ImageManipulation): + """Concrete implementation for cropping images.""" + + def __init__(self): + # You can initialize any instance variables here + self.manipulation_name = "Crop" + + def manipulateImage(self, image: ImageContainer, parameters: Any) -> None: + """ + Manipulate the given image using the provided parameters. + + Args: + image: The image to manipulate + parameters: Parameters for the manipulation (e.g., crop coordinates) + """ + pass + + def getParameters(self) -> List[str]: + """ + Get the list of parameters required for cropping. + + Returns: + List of parameter names + """ + return ["width", "height"] + + def getManipulationName(self) -> str: + """ + Get the name of this manipulation operation. + + Returns: + The name of the manipulation + """ + return "Crop Image" \ No newline at end of file diff --git a/src/ImageManipulation/ManipulationList.py b/src/ImageManipulation/ManipulationList.py new file mode 100644 index 0000000..aeff678 --- /dev/null +++ b/src/ImageManipulation/ManipulationList.py @@ -0,0 +1,4 @@ +from src.ImageManipulation.CropImage import * + +def GetImageManipulationList() -> list: + return [CropImage()] \ No newline at end of file diff --git a/src/Main.py b/src/Main.py index d9c6bbc..d79ee9f 100644 --- a/src/Main.py +++ b/src/Main.py @@ -1,3 +1,4 @@ +#!/usr/bin/python from GUI import GUI if __name__ == "__main__":