From 193bf9b5b4da2ab97c570c8edac79a5060062a4c Mon Sep 17 00:00:00 2001 From: vb Date: Fri, 7 Nov 2025 16:09:13 +0100 Subject: [PATCH] adding pre-existing flip/rotation functionalities to the image menu, and setting fixed options for vertical/horizontal flip and left/right 90 degree rotation --- README.md | 10 ++++---- src/GUI.py | 48 +++++++++++++++++++++++++++++++++++++ src/utils/area_selection.py | 7 ++++-- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b2adc5e..d73603a 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,13 @@ Image menu - Select - [x] Rectangular selection - [x] Free-form selection (Lasso) - - [ ] Polygon selection + - [x] Polygon selection - [x] Crop - [x] Resize -- [ ] Rotate right 90 degrees -- [ ] Rotate Left 90 degrees -- [ ] Flip vertical -- [ ] Flip horizontal  +- [x] Rotate right 90 degrees +- [x] Rotate Left 90 degrees +- [x] Flip vertical +- [x] Flip horizontal  Tools menu - [ ] Zoom (Zoom In, Zoom Out) diff --git a/src/GUI.py b/src/GUI.py index 030e453..424bd17 100644 --- a/src/GUI.py +++ b/src/GUI.py @@ -5,6 +5,8 @@ from ImageContainer import ImageContainer from ImageManipulation.ManipulationList import * from ImageManipulation.Filters import GaussianBlur, SobelEdge, BinaryThreshold, HistogramThreshold from ImageManipulation.ZoomImage import ZoomImage +from ImageManipulation.FlipImage import FlipImage +from ImageManipulation.RotateImage import RotateImage from SelectionArea import SelectionArea from utils.area_selection import AreaSelectionHandler from utils.brush import BrushHandler @@ -121,6 +123,12 @@ class GUI: image_menu.add_separator() image_menu.add_command(label="Crop", command=lambda: self._cropImage()) image_menu.add_command(label="Resize", command=lambda: self._resizeImage()) + image_menu.add_separator() + image_menu.add_command(label="Flip Horizontal", command=lambda: self._flipHorizontal()) + image_menu.add_command(label="Flip Vertical", command=lambda: self._flipVertical()) + image_menu.add_separator() + image_menu.add_command(label="Rotate Right 90°", command=lambda: self._rotateRight()) + image_menu.add_command(label="Rotate Left 90°", command=lambda: self._rotateLeft()) filter_menu = tk.Menu(menu, tearoff=0) menu.add_cascade(label="Filter", menu=filter_menu) filter_menu.add_command(label="Gaussian Filter", command=lambda: self._applyGaussianFilter()) @@ -404,6 +412,46 @@ class GUI: self._areaSelectionHandler.set_pending_operation_callback( lambda: self._areaSelectionHandler.resize_to_selection_dimensions() ) + + def _flipHorizontal(self) -> None: + """Flip the image horizontally.""" + if self._currentImage is None: + messagebox.showwarning("No Image", "Please load an image first.") + return + + manipulation = FlipImage() + params = {"mode": "horizontal"} + self._applyManipulation(manipulation, params) + + def _flipVertical(self) -> None: + """Flip the image vertically.""" + if self._currentImage is None: + messagebox.showwarning("No Image", "Please load an image first.") + return + + manipulation = FlipImage() + params = {"mode": "vertical"} + self._applyManipulation(manipulation, params) + + def _rotateRight(self) -> None: + """Rotate the image 90 degrees to the right (clockwise).""" + if self._currentImage is None: + messagebox.showwarning("No Image", "Please load an image first.") + return + + manipulation = RotateImage() + params = {"angle": -90} # Negative angle for clockwise rotation + self._applyManipulation(manipulation, params) + + def _rotateLeft(self) -> None: + """Rotate the image 90 degrees to the left (counter-clockwise).""" + if self._currentImage is None: + messagebox.showwarning("No Image", "Please load an image first.") + return + + manipulation = RotateImage() + params = {"angle": 90} # Positive angle for counter-clockwise rotation + self._applyManipulation(manipulation, params) def _onMouseClick(self, event) -> None: """Handle mouse click for selection or brush.""" diff --git a/src/utils/area_selection.py b/src/utils/area_selection.py index 76d7377..48364e9 100644 --- a/src/utils/area_selection.py +++ b/src/utils/area_selection.py @@ -97,11 +97,11 @@ class AreaSelectionHandler: """Set the selection shape directly. Args: - shape: One of "rectangle", "circle", or "lasso" + shape: One of "rectangle", "circle", "lasso", or "polygon" """ if self._selectionArea is None: return - if shape in ("rectangle", "circle", "lasso"): + if shape in ("rectangle", "circle", "lasso", "polygon"): self._selectionArea.set_shape(shape) self._selectionType = shape # Enable selection mode if not already enabled @@ -408,6 +408,9 @@ class AreaSelectionHandler: def on_right_click(self, event, get_default_params: Callable) -> bool: """Handle right-click to show context menu for manipulation options. + Works for all selection types: rectangle, circle, lasso, and polygon. + The selection must be active and valid (completed) before the context menu appears. + Args: event: The mouse event get_default_params: Function to get default parameters for a manipulation