Skip to content

Conversation

t0mpr1c3
Copy link
Contributor

@t0mpr1c3 t0mpr1c3 commented Mar 29, 2025

Addressing #776

Draft format described here: AllYarnsAreBeautiful/ayab-patterns#4 (comment)

Up-to-date testing builds for this branch can be found at: https://nightly.link/AllYarnsAreBeautiful/ayab-desktop/workflows/build-multi-os/memo

Summary by CodeRabbit

  • New Features

    • Added support for displaying and tracking memo data for each pattern row during knitting operations.
    • The knit progress table now includes a memo column, showing relevant notes per row when available.
  • Improvements

    • Enhanced logging and status updates to include memo information, providing more context during knitting.
    • Memo data is now extracted from compatible image files and maintained through image transformations.
  • Bug Fixes

    • Improved error handling when accessing memo data, ensuring stable operation if memo entries are missing.
  • Style

    • Updated color schemes in the knit progress table for better visual distinction.
  • Chores

    • Refactored internal naming for image transformation classes to improve consistency.

Copy link
Contributor

coderabbitai bot commented Mar 29, 2025

"""

Walkthrough

This change implements the propagation and display of "memo" data throughout the AYAB application. Memos are now extracted from PNG metadata in AyabImage, stored as a list, and passed through the engine and pattern classes. The control logic integrates memos into logs and status updates. The knitting progress UI is updated to display a memo column if memos are present, and the status object now includes a memo field. Several classes and method signatures are updated to accommodate memo handling, and class renaming from Transform to ImageTransform is performed for image transformations.

Changes

File(s) Change Summary
src/main/python/main/ayab/engine/control.py Added memos attribute to Control, updated start and related methods to handle memos, and integrated memo data into logging and status updates.
src/main/python/main/ayab/engine/engine.py Added memos attribute to Engine, updated knit_config to accept AyabImage and extract memos, and passed memos to control.start.
src/main/python/main/ayab/engine/pattern.py Updated Pattern constructor to accept memos, added __memos attribute and property, and updated internal handling to include memos.
src/main/python/main/ayab/engine/status.py Added memo attribute to Status, initialized and copied in reset and copy methods.
src/main/python/main/ayab/gui_fsm.py Changed argument to knit_config from image to AyabImage in the state transition lambda.
src/main/python/main/ayab/image.py Added memos list to AyabImage, extracted memos from PNG metadata, updated transformation methods to manage memos, and changed imports to use ImageTransform.
src/main/python/main/ayab/knitprogress.py Enhanced KnitProgress to display a memo column, added color constants, updated UI logic to show memos, and adjusted row/column handling for memo integration.
src/main/python/main/ayab/tests/test_control.py Updated tests to pass an empty list for memos to Pattern constructor instead of a config object.
src/main/python/main/ayab/ayab.py Changed import from Transform to ImageTransform and updated menu action logic to use the new class.
src/main/python/main/ayab/transforms.py Renamed class Transform to ImageTransform and updated method references accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant GUI
    participant AyabImage
    participant Engine
    participant Pattern
    participant Control
    participant KnitProgress

    User->>GUI: Load PNG with memo metadata
    GUI->>AyabImage: Open image
    AyabImage->>AyabImage: Extract memos from PNG metadata
    GUI->>Engine: knit_config(AyabImage)
    Engine->>AyabImage: Access image and memos
    Engine->>Pattern: Create Pattern(image, memos, config)
    Engine->>Control: start(Pattern, memos, options, operation)
    Control->>Control: Integrate memos into logging and status
    Control->>KnitProgress: Update progress with memo info
    KnitProgress->>GUI: Display memo column in progress table
Loading

Possibly related issues

Suggested reviewers

  • dl1com
    """

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 593551c and 2958126.

📒 Files selected for processing (1)
  • src/main/python/main/ayab/image.py (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/python/main/ayab/image.py
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Create and upload Linux AppImage release
  • GitHub Check: Create and upload Windows build
  • GitHub Check: Create and upload Mac OSX build
  • GitHub Check: Create and upload Linux AppImage release
  • GitHub Check: Create and upload Windows build
  • GitHub Check: Create and upload Mac OSX build

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
src/main/python/main/ayab/gui_fsm.py (1)

148-148: Formatting: Added whitespace line.

This is a minor formatting change adding a blank line for better readability.

src/main/python/main/ayab/image.py (1)

116-129: Implementation for extracting memo information from PNG metadata.

The implementation looks good overall, but there are some improvements that could be made:

  1. Consider adding validation for integer parsing to handle non-integer characters in the comment
  2. There's no error handling if int() conversion fails
  3. The memo list size is dependent on the comment length, which might not be ideal

Here's an improved implementation with better error handling:

 if suffix == ".png":
     # check metadata for memo information
     self.image.load()
     if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
         comment = str(self.image.info["Comment"])
         if comment.startswith("AYAB:"):
             # update memo information
             self.memo = []
-            for i in range(len(comment) - 5):
-                self.memo.append(int(comment[i + 5]))
+            memo_data = comment[5:]  # Extract everything after "AYAB:"
+            try:
+                # Parse each character as an integer
+                for char in memo_data:
+                    if char.isdigit():
+                        self.memo.append(int(char))
+                    else:
+                        logging.warning(f"Skipping non-digit character '{char}' in memo data")
+            except Exception as e:
+                logging.error(f"Error parsing memo data: {e}")
         # report metadata
         logging.info("File metadata Comment tag: " + comment)
         logging.info("File memo information: " + str(self.memo))
src/main/python/main/ayab/engine/control.py (2)

237-240: Improve exception handling pattern.

The current implementation uses a bare except block which silently swallows all exceptions. This can hide potential issues and make debugging difficult.

Consider using contextlib.suppress for clearer intention or specifying the exact exception type you expect:

-try:
-    msg = msg + " memo: " + str(self.memo[self.pat_row])
-except:
-    pass
+import contextlib
+
+with contextlib.suppress(IndexError):
+    msg = msg + " memo: " + str(self.memo[self.pat_row])

Or with explicit exception type:

-try:
-    msg = msg + " memo: " + str(self.memo[self.pat_row])
-except:
-    pass
+try:
+    msg = msg + " memo: " + str(self.memo[self.pat_row])
+except IndexError:
+    pass  # Skip memo logging if index out of range
🧰 Tools
🪛 Ruff (0.8.2)

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


274-277: Improve exception handling for status memo update.

Similar to the previous issue, this code uses a bare except block.

Apply the same improvement pattern here:

-try:
-    self.status.memo = self.memo[self.pat_row]
-except:
-    self.status.memo = 0
+try:
+    self.status.memo = self.memo[self.pat_row]
+except IndexError:
+    self.status.memo = 0  # Default value when memo index is out of range
🧰 Tools
🪛 Ruff (0.8.2)

276-276: Do not use bare except

(E722)

src/main/python/main/ayab/knitprogress.py (1)

39-39: Remove redundant TODO comment.

This TODO comment about displaying memo information is no longer needed since the feature is being implemented in this PR.

-# TODO (TP): display memo information
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 201c9ad and 4601e40.

📒 Files selected for processing (8)
  • src/main/python/main/ayab/engine/control.py (4 hunks)
  • src/main/python/main/ayab/engine/engine.py (6 hunks)
  • src/main/python/main/ayab/engine/pattern.py (2 hunks)
  • src/main/python/main/ayab/engine/status.py (3 hunks)
  • src/main/python/main/ayab/gui_fsm.py (2 hunks)
  • src/main/python/main/ayab/image.py (4 hunks)
  • src/main/python/main/ayab/knitprogress.py (7 hunks)
  • src/main/python/main/ayab/pattern_import.py (1 hunks)
🧰 Additional context used
🧬 Code Definitions (6)
src/main/python/main/ayab/engine/status.py (1)
src/main/python/main/ayab/engine/pattern.py (1)
  • memo (204-205)
src/main/python/main/ayab/gui_fsm.py (1)
src/main/python/main/ayab/engine/engine.py (1)
  • knit_config (122-160)
src/main/python/main/ayab/image.py (2)
src/main/python/main/ayab/engine/pattern.py (1)
  • memo (204-205)
src/main/python/main/ayab/utils.py (1)
  • display_blocking_popup (44-65)
src/main/python/main/ayab/engine/control.py (3)
src/main/python/main/ayab/engine/pattern.py (2)
  • memo (204-205)
  • Pattern (33-208)
src/main/python/main/ayab/engine/options.py (1)
  • OptionsTab (59-252)
src/main/python/main/ayab/engine/engine_fsm.py (1)
  • Operation (39-41)
src/main/python/main/ayab/engine/pattern.py (2)
src/main/python/main/ayab/image.py (1)
  • AyabImage (46-205)
src/main/python/main/ayab/engine/options.py (2)
  • OptionsTab (59-252)
  • Alignment (255-266)
src/main/python/main/ayab/knitprogress.py (1)
src/main/python/main/ayab/engine/pattern.py (1)
  • memo (204-205)
🪛 Ruff (0.8.2)
src/main/python/main/ayab/engine/control.py

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


276-276: Do not use bare except

(E722)

🔇 Additional comments (26)
src/main/python/main/ayab/pattern_import.py (1)

8-8: Appropriate TODO comment for future implementation.

This comment aligns with the PR's objective to extract memo information from PNG files and display it in the Knitting Progress window.

src/main/python/main/ayab/engine/status.py (1)

164-164: Appropriate copying of memo value in Status.copy method.

The copy method correctly assigns the memo from the source status object.

src/main/python/main/ayab/gui_fsm.py (1)

110-111: Updated parameter to match new knit_config method signature.

This change correctly passes the entire ayabimage object instead of just the image attribute, allowing knit_config to access the newly added memo information.

src/main/python/main/ayab/image.py (3)

59-59: Added memo attribute to store metadata information.

The addition of the memo list allows storing integers extracted from PNG metadata.


90-93: Removed FIXME comments from error handling.

The removal of FIXME comments simplifies the code while maintaining the same functionality.


96-98: Removed FIXME comments from error handling.

Similar to the previous change, this simplifies the error handling code.

src/main/python/main/ayab/engine/control.py (3)

64-64: Added new member variable for memo data.

The addition of this attribute supports the main PR objective of extracting and displaying memo information from PNG files.


89-90: Updated method signature to include memo parameter.

This change appropriately propagates the memo information from the engine to the control layer.


97-97: Initialized memo member variable.

Correctly initializes the class's memo attribute from the parameter.

src/main/python/main/ayab/engine/pattern.py (5)

29-29: Added import for AyabImage class.

This import supports the updated constructor parameter type.


34-37: Updated constructor to use AyabImage instead of Image.Image.

This change correctly modifies the parameter type and accesses the image through the AyabImage.image property.


38-38: Added memo attribute to store memo data.

This attribute properly stores the memo information from the AyabImage object.


39-40: Added explicit type annotations.

These annotations improve code clarity and provide better type checking.


203-205: Added memo property to access the memo data.

This property correctly exposes the private memo attribute, following Python's property access pattern.

src/main/python/main/ayab/engine/engine.py (7)

38-38: Added import for AyabImage class.

This import supports the updated knit_config parameter type.


54-54: Added memo class attribute.

This attribute declaration improves code readability by explicitly stating the class attribute's type.


71-71: Initialized memo instance attribute.

Properly initializes the memo as an empty list.


122-122: Updated knit_config method signature.

Changed parameter from Image.Image to AyabImage to support the memo feature.


131-131: Updated image transformation code.

Now correctly accesses the image through the AyabImage.image property.


135-136: Updated pattern initialization and added memo extraction.

These lines properly initialize the pattern with the AyabImage object and extract the memo information.


173-173: Updated control.start call to include memo parameter.

This change correctly passes the memo information to the control layer.

src/main/python/main/ayab/knitprogress.py (5)

49-52: Updated color constants.

The changes to existing color constants and addition of new ones provide better visual representation for the memo information.


94-94: Updated UI state change detection.

This change correctly detects when the memo information has changed and triggers a UI update.


142-142: Added memo column to the progress table.

This line appends a memo item to the columns list, allowing the memo information to be displayed in the UI.


175-178: Added header for memo column.

These lines correctly set up the memo column header with appropriate styling.


275-284: Added memo cell renderer.

This method creates a table cell for displaying the memo value, with appropriate styling and handling of zero values.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
src/main/python/main/ayab/image.py (2)

59-59: Add type annotations for consistency

The memos attribute is defined with just list type, but based on the usage in the file it's meant to store integers. Consider using a more specific type annotation like list[int] for better type checking and code clarity.

-        self.memos: list = []
+        self.memos: list[int] = []

124-136: Consider using a more flexible memo format

The current implementation only allows for single-digit integers (0-9) in memos. Consider using a delimiter like commas or spaces to support multi-digit values, which would be more versatile for storing memo information.

                if comment.startswith("AYAB:"):
                    # update memo information
                    self.memos = []
-                    for i in range(len(comment) - 5):
-                        self.memos.append(int(comment[i + 5]))
+                    # Parse comma-separated values after "AYAB:"
+                    memo_str = comment[5:]
+                    try:
+                        for val in memo_str.split(','):
+                            if val.strip():
+                                self.memos.append(int(val.strip()))
+                    except ValueError as e:
+                        logging.error(f"Error parsing memo value: {str(e)}")
src/main/python/main/ayab/engine/control.py (3)

237-240: Improve exception handling

The current code uses a bare except clause which catches all exceptions and silently ignores them. This can hide bugs and make debugging difficult.

        try:
            msg = msg + " memo: " + str(self.memos[self.pat_row])
-        except:
-            pass
+        except IndexError:
+            self.logger.debug(f"No memo available for pattern row {self.pat_row}")
+        except Exception as e:
+            self.logger.error(f"Error accessing memo data: {str(e)}")

Alternatively, use contextlib.suppress as suggested by the static analysis:

-        try:
-            msg = msg + " memo: " + str(self.memos[self.pat_row])
-        except:
-            pass
+        from contextlib import suppress
+        with suppress(IndexError):
+            msg = msg + " memo: " + str(self.memos[self.pat_row])
🧰 Tools
🪛 Ruff (0.8.2)

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


274-277: Improve exception handling for status update

Similar to the previous comment, this code uses a bare except clause. Specify the expected exception type and provide a log message to help with debugging.

        try:
            self.status.memo = self.memos[self.pat_row]
-        except:
+        except IndexError:
+            self.logger.debug(f"No memo available for pattern row {self.pat_row}, defaulting to 0")
            self.status.memo = 0
+        except Exception as e:
+            self.logger.error(f"Error setting memo status: {str(e)}")
+            self.status.memo = 0
🧰 Tools
🪛 Ruff (0.8.2)

276-276: Do not use bare except

(E722)


294-294: Consider more selective logging

Logging the entire memos list on every status update could be excessive and make logs difficult to read, especially for large lists.

-        self.logger.debug(str(self.memos))
+        # Only log if needed for debugging specific issues
+        if self.logger.isEnabledFor(logging.DEBUG):
+            self.logger.debug(f"Memos (count: {len(self.memos)}): {str(self.memos)}")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4601e40 and f671dec.

📒 Files selected for processing (4)
  • src/main/python/main/ayab/engine/control.py (5 hunks)
  • src/main/python/main/ayab/engine/engine.py (6 hunks)
  • src/main/python/main/ayab/engine/pattern.py (2 hunks)
  • src/main/python/main/ayab/image.py (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/python/main/ayab/engine/engine.py
  • src/main/python/main/ayab/engine/pattern.py
🧰 Additional context used
🧬 Code Definitions (2)
src/main/python/main/ayab/image.py (2)
src/main/python/main/ayab/engine/pattern.py (1)
  • memos (204-205)
src/main/python/main/ayab/utils.py (1)
  • display_blocking_popup (44-65)
src/main/python/main/ayab/engine/control.py (3)
src/main/python/main/ayab/engine/pattern.py (2)
  • memos (204-205)
  • Pattern (33-208)
src/main/python/main/ayab/engine/options.py (1)
  • OptionsTab (59-252)
src/main/python/main/ayab/engine/engine_fsm.py (1)
  • Operation (39-41)
🪛 Ruff (0.8.2)
src/main/python/main/ayab/engine/control.py

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


276-276: Do not use bare except

(E722)

🔇 Additional comments (4)
src/main/python/main/ayab/image.py (1)

63-69: The clone method looks good

Nice implementation of the clone method to properly copy all AyabImage attributes including the new memos list.

src/main/python/main/ayab/engine/control.py (3)

64-64: Good use of type annotation

The type annotation list[int] for the memos attribute clearly indicates the expected type of elements in the list, which is better than just using list.


89-90: Function signature update looks good

Correctly updated the function signature to include the new memos parameter, maintaining consistency with other parameters.


97-97: Properly storing the memos parameter

The implementation correctly saves the passed memos parameter to the instance variable.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/main/python/main/ayab/image.py (1)

116-132: ⚠️ Potential issue

Improve error handling for memo parsing

The current implementation attempts to parse digits from the Comment tag but uses a bare except clause which is flagged by static analysis. Additionally, this approach silently converts parsing errors to zeros, which could lead to unexpected behavior.

        if suffix == ".png":
            # check metadata for memo information
            self.image.load()
            if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
                comment = str(self.image.info["Comment"])
                if comment.startswith("AYAB:"):
                    # update memo information
                    self.memos = []
-                    for i in range(len(comment) - 5):
-                        try:
-                            self.memos.append(int(comment[i + 5]))
-                        except:
-                            self.memos.append(0)
+                    try:
+                        for i in range(len(comment) - 5):
+                            char = comment[i + 5]
+                            if char.isdigit():
+                                self.memos.append(int(char))
+                            else:
+                                logging.warning(f"Non-digit character '{char}' found in memo data")
+                    except Exception as e:
+                        logging.error(f"Error parsing memo data: {str(e)}")
                # report metadata
                logging.info("File metadata Comment tag: " + comment)
                logging.info("File memo information: " + str(self.memos))
🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)

🧹 Nitpick comments (2)
src/main/python/main/ayab/image.py (2)

116-132: Consider supporting multi-digit numbers in memo format

The current implementation assumes each character after "AYAB:" is a single-digit integer. If you need to support multi-digit numbers or other structured data formats, you may want to use a different parsing approach.

For example, you could use a more structured format like "AYAB:1,2,3,10,20" and parse it with:

        if suffix == ".png":
            # check metadata for memo information
            self.image.load()
            if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
                comment = str(self.image.info["Comment"])
                if comment.startswith("AYAB:"):
                    # update memo information
                    self.memos = []
-                    for i in range(len(comment) - 5):
-                        try:
-                            self.memos.append(int(comment[i + 5]))
-                        except:
-                            self.memos.append(0)
+                    try:
+                        # Parse comma-separated values after "AYAB:"
+                        memo_str = comment[5:].strip()
+                        if memo_str:
+                            for num_str in memo_str.split(','):
+                                if num_str.strip().isdigit():
+                                    self.memos.append(int(num_str.strip()))
+                                else:
+                                    logging.warning(f"Non-digit value '{num_str}' found in memo data")
+                    except Exception as e:
+                        logging.error(f"Error parsing memo data: {str(e)}")
                # report metadata
                logging.info("File metadata Comment tag: " + comment)
                logging.info("File memo information: " + str(self.memos))
🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)


119-131: Consider documenting the AYAB memo format

The code adds parsing for a custom "AYAB:" memo format in PNG comments, but this format isn't documented in the code. Consider adding a comment explaining the expected format and meaning of these memo values.

            if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
                comment = str(self.image.info["Comment"])
+               # The AYAB memo format uses "AYAB:" prefix followed by single-digit integers
+               # representing specific knitting instructions or metadata
                if comment.startswith("AYAB:"):
                    # update memo information
🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e34662e and 1811363.

📒 Files selected for processing (1)
  • src/main/python/main/ayab/image.py (4 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
src/main/python/main/ayab/image.py (2)
src/main/python/main/ayab/engine/pattern.py (1)
  • memos (203-204)
src/main/python/main/ayab/utils.py (1)
  • display_blocking_popup (44-65)
🪛 Ruff (0.8.2)
src/main/python/main/ayab/image.py

127-127: Do not use bare except

(E722)

🔇 Additional comments (2)
src/main/python/main/ayab/image.py (2)

59-59: New attribute added for memo information

Added memos attribute to store the integers extracted from PNG file Comment metadata. This is well-typed as list[int] and properly initialized as an empty list.


92-92: Code cleanup: FIXME comments removed

Removed FIXME comments from display_blocking_popup calls, making the code cleaner.

Also applies to: 97-97

@t0mpr1c3 t0mpr1c3 marked this pull request as draft March 29, 2025 09:16
@AllYarnsAreBeautiful AllYarnsAreBeautiful deleted a comment from coderabbitai bot Mar 29, 2025
@jpcornil-git
Copy link

Why not using a JSON string to store memo ?
This would allow to:

  • add more data moving forward, e;g. version, carriage to use for a row, colors, ...
  • not be limited to single digit number
  • just use a json.loads to get a dictionnary with everything in one line of code rather than parse string

Why not use an new exif or native iTXt "ayab" key rather than use a "Comment" key with value starting wth "AYAB:" ?

See https://discord.com/channels/1079442610332110849/1094306201451384944/1355294497696911410

@t0mpr1c3
Copy link
Contributor Author

t0mpr1c3 commented Mar 30, 2025

Why not using a JSON string to store memo ? This would allow to:

* add more data moving forward, e;g. version, carriage to use for a row, colors, ...

* not be limited to single digit number

* just use a json.loads to get a dictionnary with everything in one line of code rather than parse string

Why not use an new exif or native iTXt "ayab" key rather than use a "Comment" key with value starting wth "AYAB:" ?

See https://discord.com/channels/1079442610332110849/1094306201451384944/1355294497696911410

There is no need from the point of view of feature parity, it is harder to implement, it makes manual editing more difficult.

In any case, this is still a draft. It might turn out that the way that DAK implements memo information requires a more flexible format and if so we can change it or add a new one.

@jpcornil-git
Copy link

Not sure I understand why you said it is harder to implement (on the contrary) and that it makes manual editing more difficult (same to me), see below

Current code
info["Comment"] = 'AYAB:12345'

            if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
                comment = str(self.image.info["Comment"])
                if comment.startswith("AYAB:"):
                    # update memo information
                    self.memos = []
                    for i in range(len(comment) - 5):
                        self.memos.append(int(comment[i + 5]))

Use JSON instead of raw/single digit data

info["Comment"] = 'AYAB:{"memo":[1,12,23,34,45], "color":[1,2,3,4,5], "carriage":["K", "K", "L", "L", "G"], "version":1}'

            if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
                comment = str(self.image.info["Comment"])
                if comment.startswith("AYAB:"):
                    metadata = json.loads(comment[5:])
                    # update memo information
                    self.memos = metadata["memo"]

=> no parsing in the code anymore, extend beyond "memo"

Use JSON with a dedicated metadata key

info["AYAB"] = '{"memo":[1,12,23,34,45], "color":[1,2,3,4,5], "carriage":["K", "K", "L", "L", "G"], "version":1}'

            if "AYAB" in self.image.info:
                metadata = json.loads(self.image.info["AYAB"])
                # update memo information
                self.memos = metadata["memo"]

=> Same as above + no need to check for a string prefix

Code is shorter/generic and give you much more flexibility for free ?

@t0mpr1c3 t0mpr1c3 marked this pull request as ready for review March 30, 2025 23:32
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/main/python/main/ayab/image.py (1)

117-132: Improve error handling in memo parsing

While the implementation correctly extracts memo information from PNG metadata, it uses a bare except clause which can mask unexpected errors.

                if comment.startswith("AYAB:"):
                    # update memo information
                    for i in range(len(comment) - 5):
                        try:
                            self.memos.append(int(comment[i + 5]))
-                       except:
+                       except ValueError:
                            self.memos.append(0)

Additionally, consider adding a comment explaining what these memo values represent or are used for, to help future developers understand the feature's purpose.

🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)

src/main/python/main/ayab/engine/control.py (2)

237-240: Use specific exception type for robust error handling

The current implementation uses a bare except clause which can catch and silence unexpected exceptions.

Consider using a specific exception type and Python's contextlib.suppress for cleaner code:

-        try:
-            msg = msg + " memo: " + str(self.memos[self.pat_row])
-        except:
-            pass
+        from contextlib import suppress
+        with suppress(IndexError):
+            msg = msg + " memo: " + str(self.memos[self.pat_row])

This approach makes it clear that we're specifically handling index out-of-bounds errors and provides a more pythonic solution.

🧰 Tools
🪛 Ruff (0.8.2)

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


274-277: Improve exception handling for setting status memo

Similar to the previous comment, this code uses a bare except clause.

-        try:
-            self.status.memo = self.memos[self.pat_row]
-        except:
-            self.status.memo = 0
+        try:
+            self.status.memo = self.memos[self.pat_row]
+        except IndexError:
+            self.status.memo = 0

This makes it explicit that we're handling out-of-bounds index errors specifically, improving code clarity and safety.

🧰 Tools
🪛 Ruff (0.8.2)

276-276: Do not use bare except

(E722)

src/main/python/main/ayab/knitprogress.py (2)

39-39: Remove completed TODO comment

The TODO comment about displaying memo information can be removed since the implementation is now complete.

-# TODO (TP): display memo information

277-286: Clean implementation of memo cell rendering

The __memo method is well-implemented, displaying memo values greater than 0 and using appropriate styling for visual clarity.

Consider adding a tooltip to explain what the memo values represent to users, especially as this appears to be a new feature:

    def __memo(
        self,
        memo: int,
    ) -> QTableWidgetItem:
        cell = QTableWidgetItem()
        cell.setBackground(QBrush(QColor(f"#{self.smoke:06x}")))
        if memo > 0:
            cell.setText(str(memo))
+            cell.setToolTip("Memo information from image metadata")
        return cell
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1811363 and 6555412.

📒 Files selected for processing (3)
  • src/main/python/main/ayab/engine/control.py (4 hunks)
  • src/main/python/main/ayab/image.py (5 hunks)
  • src/main/python/main/ayab/knitprogress.py (8 hunks)
🧰 Additional context used
🧬 Code Definitions (3)
src/main/python/main/ayab/knitprogress.py (1)
src/main/python/main/ayab/engine/pattern.py (2)
  • memos (203-204)
  • knit_start_needle (183-184)
src/main/python/main/ayab/engine/control.py (2)
src/main/python/main/ayab/engine/pattern.py (2)
  • memos (203-204)
  • Pattern (32-207)
src/main/python/main/ayab/engine/engine_fsm.py (1)
  • Operation (39-41)
src/main/python/main/ayab/image.py (2)
src/main/python/main/ayab/engine/pattern.py (1)
  • memos (203-204)
src/main/python/main/ayab/utils.py (1)
  • display_blocking_popup (44-65)
🪛 Ruff (0.8.2)
src/main/python/main/ayab/engine/control.py

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


276-276: Do not use bare except

(E722)

src/main/python/main/ayab/image.py

127-127: Do not use bare except

(E722)

🔇 Additional comments (10)
src/main/python/main/ayab/image.py (2)

59-59: Addition of a new memo feature

The introduction of the memos list as a class attribute provides a clean way to store integer metadata associated with the image. This attribute will be propagated through the system to display in the UI.


106-106: Good initialization practice

Properly clearing the memos list at the beginning of the __open method ensures a clean state before processing a new image.

src/main/python/main/ayab/engine/control.py (3)

64-64: Addition of memos attribute

The new memos class attribute extends the Control class to support the memo feature. This is a clean way to propagate memo information through the control system.


89-89: Updated method signature to include memos

The start method signature now includes the memos parameter, allowing the memo information to be passed from the engine to the control component.


97-97: Setting the memos attribute

Properly initializing the memos attribute in the start method ensures that memo information is available throughout the control component's lifecycle.

src/main/python/main/ayab/knitprogress.py (5)

49-52: Updated color constants

The modifications to existing color constants and addition of new ones provide a consistent color scheme for the UI components, including the new memo column.


84-84: Dynamic memo column visibility

Good approach to conditionally show the memo column based on the presence of memo data. This ensures the UI remains clean when no memo information is available.


95-95: UI state tracking for memo changes

Adding memo status to the UI state change tracking ensures that the progress display updates correctly when memo information changes.


143-144: Conditional memo column addition

The implementation correctly adds the memo column only when memo information is available, keeping the UI clean and focused.


175-189: Good attention to UI layout adjustment

The code properly adjusts the needle calculation and column headers to account for the presence of the memo column. The use of color-coded headers enhances visual distinction.

@t0mpr1c3
Copy link
Contributor Author

The format implemented in the code is consistent with AllYarnsAreBeautiful/ayab-patterns#7

If you want to change one, you'll want to change the other.

@jonathanperret
Copy link
Contributor

I've added a commit that points the patterns submodule to include the already-enhanced patterns (up to number 124 in kh930).

I have a couple of nits to pick with the code as it is (mainly the issues reported by SonarQube above) but the UI part feels ready for initial testing.

So I've triggered a build from this PR so that we can collect tester feedback: https://github.com/jonathanperret/ayab-desktop/releases/tag/1.0.0-memo1

@jonathanperret
Copy link
Contributor

jonathanperret commented Mar 31, 2025

Writing some Discord feedback here so we can have it in one place:

  • the new M column should probably be frozen so that it can't scroll out of view when there are more needles than can be shown at once.
    image

@jonathanperret
Copy link
Contributor

jonathanperret commented Mar 31, 2025

A couple more issues that I found/remembered:

  • when a cell in the M column gets the focus, the text at the bottom shows stitch Left-M which isn't correct. I'd suggest preventing selection of cells in the column altogether.
image
  • Image Actions (Stretch/Repeat/Reflect…) currently generally break Memo data, except when they don't change the number or order of rows (i.e. Invert, Horizontal Flip, Horizontal Repeat are safe, most others are not). I don't think it's useful to work very hard to preserve Memo data for e.g. a Rotate, but Vertical Repeat in particular is one that makes sense on lace patterns, that users are likely to reach for (I've witnessed Vertical Repeat being preferred over Infinite Repeat), and the current (lack of) implementation creates a false sense of security by preserving Memo data on the first repeat and losing it on extra repeats. Do we think there's room in this PR for repeating Memo data on Vertical Repeat, and perhaps erasing it on unsafe operations to avoid garbage output?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/main/python/main/ayab/image.py (1)

117-132: ⚠️ Potential issue

Fix type mismatch and bare except in memo extraction.

Several issues in the memo extraction implementation need to be fixed:

  1. The memos list is defined to store integers (list[int]), but you're appending string characters
  2. Using a bare except clause is discouraged
  3. The error handling logic doesn't maintain type consistency
if suffix == ".png":
    # check metadata for memo information
    self.image.load()
    if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
        comment = str(self.image.info["Comment"])
        if comment.startswith("AYAB:"):
            # update memo information
-           for i in range(len(comment) - 5):
-               try:
-                   self.memos.append(comment[i + 5])
-               except:
-                   self.memos.append('0')
+           for i in range(len(comment) - 5):
+               try:
+                   char = comment[i + 5]
+                   if char.isdigit():
+                       self.memos.append(int(char))
+                   else:
+                       logging.warning(f"Non-digit character '{char}' found in memo information, ignoring")
+               except Exception as e:
+                   logging.warning(f"Error processing memo character: {e}")
        # report metadata
        logging.info("File metadata Comment tag: " + comment)
        logging.info("File memo information: " + str(self.memos))
🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)

🪛 GitHub Actions: Python Tests

[error] 126-126: Argument 1 to 'append' of 'list' has incompatible type 'str'; expected 'int' [arg-type]


[error] 128-128: Argument 1 to 'append' of 'list' has incompatible type 'str'; expected 'int' [arg-type]

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e926463 and f50b4dc.

📒 Files selected for processing (2)
  • src/main/python/main/ayab/image.py (5 hunks)
  • src/main/python/main/ayab/knitprogress.py (8 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
src/main/python/main/ayab/knitprogress.py (1)
src/main/python/main/ayab/engine/pattern.py (2)
  • memos (203-204)
  • knit_start_needle (183-184)
src/main/python/main/ayab/image.py (2)
src/main/python/main/ayab/engine/pattern.py (1)
  • memos (203-204)
src/main/python/main/ayab/utils.py (1)
  • display_blocking_popup (44-65)
🪛 GitHub Actions: Python Tests
src/main/python/main/ayab/knitprogress.py

[error] 283-283: Argument 1 to 'len' has incompatible type 'int'; expected 'Sized' [arg-type]


[error] 283-283: Non-overlapping equality check (left operand type: 'int', right operand type: 'Literal['0']') [comparison-overlap]


[error] 284-284: Argument 1 to 'setText' of 'QTableWidgetItem' has incompatible type 'int'; expected 'str' [arg-type]

src/main/python/main/ayab/image.py

[error] 126-126: Argument 1 to 'append' of 'list' has incompatible type 'str'; expected 'int' [arg-type]


[error] 128-128: Argument 1 to 'append' of 'list' has incompatible type 'str'; expected 'int' [arg-type]

🪛 Ruff (0.8.2)
src/main/python/main/ayab/image.py

127-127: Do not use bare except

(E722)

🔇 Additional comments (7)
src/main/python/main/ayab/image.py (2)

59-59: New memos list attribute properly defined.

The addition of the memos list attribute to store integers is a good implementation choice for capturing the memo information from image metadata.


106-106: LGTM!

Properly initializing the memos list at the beginning of the __open method ensures a clean state before processing any new image.

src/main/python/main/ayab/knitprogress.py (5)

51-52: LGTM!

Defining color constants for grey and smoke provides good consistency for UI styling.


84-84: LGTM!

The flag show_memo_column is a good approach to conditionally display the memo column only when memo information is available.


95-95: LGTM!

Adding status.memo to the UI state change check ensures the progress display updates when memo information changes.


143-144: LGTM!

Conditionally adding the memo column to the display based on the show_memo_column flag is a good implementation.


175-189: LGTM!

The adjustments to needle calculation and column headers are correctly implemented to accommodate the memo column when needed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
src/main/python/main/ayab/image.py (1)

124-129: ⚠️ Potential issue

Improve memo data extraction and error handling

The current implementation has several issues:

  1. The bare except clause is too broad
  2. Converting string characters to integers can throw exceptions
  3. The default value of '0' is a string, but should be an integer based on type expectations
-                    for i in range(len(comment) - 5):
-                        try:
-                            self.memos.append(comment[i + 5])
-                        except:
-                            self.memos.append('0')
+                    for i in range(len(comment) - 5):
+                        try:
+                            # Convert character to integer
+                            char = comment[i + 5]
+                            if char.isdigit():
+                                self.memos.append(int(char))
+                            else:
+                                logging.warning(f"Non-digit character '{char}' found in memo, defaulting to 0")
+                                self.memos.append(0)
+                        except Exception as e:
+                            logging.error(f"Error processing memo at position {i}: {str(e)}")
+                            self.memos.append(0)
🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)

🧹 Nitpick comments (3)
src/main/python/main/ayab/image.py (1)

130-131: Add comment format verification logging

It would be helpful to log whether the extracted memo information matches expectations, especially given the PR discussion about JSON vs. simple format.

                # report metadata
                logging.info("File metadata Comment tag: " + comment)
-               logging.info("File memo information: " + str(self.memos))
+               logging.info(f"File memo information: {self.memos} (extracted {len(self.memos)} memo values)")
+               if not all(isinstance(m, int) for m in self.memos):
+                   logging.warning("Memo data contains non-integer values, which may cause issues")
src/main/python/main/ayab/engine/control.py (1)

237-240: Improve error handling for memo logging

Instead of using a bare except clause with a silent pass, use contextlib.suppress for better readability or provide proper error handling.

+        from contextlib import suppress
-        try:
-            msg = msg + " memo: " + str(self.memos[self.pat_row])
-        except:
-            pass
+        with suppress(IndexError):
+            msg = msg + " memo: " + str(self.memos[self.pat_row])

Alternatively, with explicit error handling:

-        try:
-            msg = msg + " memo: " + str(self.memos[self.pat_row])
-        except:
-            pass
+        try:
+            msg = msg + " memo: " + str(self.memos[self.pat_row])
+        except IndexError:
+            self.logger.debug(f"No memo available for row {self.pat_row}")
+        except Exception as e:
+            self.logger.warning(f"Error accessing memo for row {self.pat_row}: {str(e)}")
🧰 Tools
🪛 Ruff (0.8.2)

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)

src/main/python/main/ayab/engine/pattern.py (1)

32-33: Consider JSON format for memo data

Based on the PR discussion, using JSON for storing memo data would allow for more flexibility and easier extension. While the current implementation works for single-digit values, JSON would support more complex data.

As suggested in the PR comments, JSON would allow you to:

  1. Store more complex data types (not just single digits)
  2. Add versioning, carriage information, colors, etc.
  3. Simplify parsing with json.loads
  4. Remove the limitation of single-digit numbers

This could be implemented while maintaining backward compatibility by detecting the format (JSON vs. simple string) during parsing.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f50b4dc and d87711e.

📒 Files selected for processing (5)
  • src/main/python/main/ayab/engine/control.py (4 hunks)
  • src/main/python/main/ayab/engine/engine.py (6 hunks)
  • src/main/python/main/ayab/engine/pattern.py (2 hunks)
  • src/main/python/main/ayab/engine/status.py (3 hunks)
  • src/main/python/main/ayab/image.py (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/python/main/ayab/engine/status.py
  • src/main/python/main/ayab/engine/engine.py
🧰 Additional context used
🧬 Code Definitions (3)
src/main/python/main/ayab/image.py (2)
src/main/python/main/ayab/engine/pattern.py (1)
  • memos (203-204)
src/main/python/main/ayab/utils.py (1)
  • display_blocking_popup (44-65)
src/main/python/main/ayab/engine/pattern.py (1)
src/main/python/main/ayab/engine/options.py (2)
  • OptionsTab (59-252)
  • Alignment (255-266)
src/main/python/main/ayab/engine/control.py (3)
src/main/python/main/ayab/engine/pattern.py (2)
  • memos (203-204)
  • Pattern (32-207)
src/main/python/main/ayab/engine/options.py (1)
  • OptionsTab (59-252)
src/main/python/main/ayab/engine/engine_fsm.py (1)
  • Operation (39-41)
🪛 Ruff (0.8.2)
src/main/python/main/ayab/image.py

127-127: Do not use bare except

(E722)

src/main/python/main/ayab/engine/control.py

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


276-276: Do not use bare except

(E722)

🪛 GitHub Actions: Python Tests
src/main/python/main/ayab/engine/pattern.py

[error] 37-37: Incompatible types in assignment (expression has type 'list[int]', variable has type 'list[str]')


[error] 204-204: Incompatible return value type (got 'list[str]', expected 'list[int]')

src/main/python/main/ayab/engine/control.py

[error] 97-97: Incompatible types in assignment (expression has type 'list[int]', variable has type 'list[str]')


[error] 277-277: Incompatible types in assignment (expression has type 'int', variable has type 'str')

🔇 Additional comments (1)
src/main/python/main/ayab/engine/pattern.py (1)

202-204: Fix return type annotation for memos property

The memos property is declared to return list[int] but actually returns __memos which is inconsistently typed as list[str].

This property looks good once the type annotation for __memos is fixed to match the return type annotation.

🧰 Tools
🪛 GitHub Actions: Python Tests

[error] 204-204: Incompatible return value type (got 'list[str]', expected 'list[int]')

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (5)
src/main/python/main/ayab/engine/control.py (3)

64-64: ⚠️ Potential issue

Fix type annotation for memos attribute

The memos attribute is defined with the wrong type, causing pipeline failures. It should be list[int] to match the parameter type in the start method.

-    memos: list[str]
+    memos: list[int]

274-277: ⚠️ Potential issue

Fix status memo type mismatch and improve error handling

There's a type mismatch here: self.status.memo is expected to be a string but is being assigned an integer value of 0. Additionally, the bare except should be avoided.

-        try:
-            self.status.memo = self.memos[self.pat_row]
-        except:
-            self.status.memo = 0
+        try:
+            self.status.memo = str(self.memos[self.pat_row])
+        except IndexError:
+            self.status.memo = "0"
+        except Exception as e:
+            self.logger.warning(f"Error setting memo status: {str(e)}")
+            self.status.memo = "0"
🧰 Tools
🪛 Ruff (0.8.2)

276-276: Do not use bare except

(E722)

🪛 GitHub Actions: Python Tests

[error] 277-277: Incompatible types in assignment (expression has type 'int', variable has type 'str') [assignment]


89-90: ⚠️ Potential issue

Ensure consistent type annotations between parameter and class attribute

The memos parameter is typed as list[str] here, but needs to be consistent with the class attribute type and how it's used in the code.

-            self, pattern: Pattern, memos: list[str], options: OptionsTab, operation: Operation
+            self, pattern: Pattern, memos: list[int], options: OptionsTab, operation: Operation
src/main/python/main/ayab/engine/pattern.py (2)

37-37: ⚠️ Potential issue

Fix type annotation for __memos attribute

There's a type mismatch. The memos parameter should be typed as list[int] but the __memos attribute is typed as list[str], causing a pipeline failure.

-        self.__memos : list[str] = memos
+        self.__memos : list[int] = memos

33-33: ⚠️ Potential issue

Ensure consistent type annotations between files

The memos parameter is typed as list[str] here, but Control class expects it to be list[int], causing pipeline failures. Ensure consistent typing across the codebase.

-    def __init__(self, image: Image.Image, memos: list[str], config: OptionsTab, num_colors: int = 2):
+    def __init__(self, image: Image.Image, memos: list[int], config: OptionsTab, num_colors: int = 2):
🧹 Nitpick comments (1)
src/main/python/main/ayab/engine/control.py (1)

237-240: Improve exception handling using contextlib.suppress

Instead of using a bare except clause, which can mask important errors, use contextlib.suppress for cleaner code.

-        try:
-            msg = msg + " memo: " + str(self.memos[self.pat_row])
-        except:
-            pass
+        from contextlib import suppress
+        with suppress(IndexError):
+            msg = msg + " memo: " + str(self.memos[self.pat_row])
🧰 Tools
🪛 Ruff (0.8.2)

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c38e23 and 9dac431.

📒 Files selected for processing (2)
  • src/main/python/main/ayab/engine/control.py (4 hunks)
  • src/main/python/main/ayab/engine/pattern.py (2 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
src/main/python/main/ayab/engine/pattern.py (1)
src/main/python/main/ayab/engine/options.py (2)
  • OptionsTab (59-252)
  • Alignment (255-266)
src/main/python/main/ayab/engine/control.py (2)
src/main/python/main/ayab/engine/pattern.py (2)
  • memos (203-204)
  • Pattern (32-207)
src/main/python/main/ayab/engine/engine_fsm.py (1)
  • Operation (39-41)
🪛 Ruff (0.8.2)
src/main/python/main/ayab/engine/control.py

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


276-276: Do not use bare except

(E722)

🪛 GitHub Actions: Python Tests
src/main/python/main/ayab/engine/control.py

[error] 277-277: Incompatible types in assignment (expression has type 'int', variable has type 'str') [assignment]

🔇 Additional comments (1)
src/main/python/main/ayab/engine/pattern.py (1)

38-39: Good practice: Explicit type annotations

Adding explicit type annotations for class attributes improves code readability and helps with static type checking. This is a good practice.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/main/python/main/ayab/knitprogress.py (1)

277-285: ⚠️ Potential issue

Fix type mismatches in the memo display method.

The __memo method has several type-related issues that need to be fixed:

  1. While the parameter type is now correctly set as str, the implementation still assumes string behavior
  2. The function should handle various data types more robustly
def __memo(
    self,
    memo: str,
) -> QTableWidgetItem:
    cell = QTableWidgetItem()
    cell.setBackground(QBrush(QColor(f"#{self.smoke:06x}")))
-   if len(memo) > 0 and memo != "0":
-       cell.setText(memo)
+   if memo and memo != "0":
+       cell.setText(str(memo))
    return cell

Additionally, consider adding a brief docstring explaining the purpose of this method:

def __memo(
    self,
    memo: str,
) -> QTableWidgetItem:
+   """
+   Create a table cell for displaying memo information.
+   
+   Args:
+       memo: The memo string to display
+   
+   Returns:
+       A QTableWidgetItem configured to display the memo
+   """
    cell = QTableWidgetItem()
    cell.setBackground(QBrush(QColor(f"#{self.smoke:06x}")))
    if memo and memo != "0":
        cell.setText(str(memo))
    return cell
🧹 Nitpick comments (2)
src/main/python/main/ayab/knitprogress.py (2)

39-39: Remove completed TODO comment.

This TODO comment can now be removed since the memo information display feature has been implemented in this PR.

-# TODO (TP): display memo information

177-180: Consider adding a tooltip to explain the "M" header.

While using "M" as the header for the memo column is space-efficient, it might not be immediately clear to users what it represents. Consider adding a tooltip to provide more context.

                header = QTableWidgetItem("M")
                header.setForeground(QBrush(QColor(f"#{self.grey:06x}")))
+               header.setToolTip("Memo information")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9dac431 and 0c8e2c0.

📒 Files selected for processing (1)
  • src/main/python/main/ayab/knitprogress.py (8 hunks)
🔇 Additional comments (5)
src/main/python/main/ayab/knitprogress.py (5)

49-52: LGTM: Updated color constants and added new ones.

The color constants are appropriately defined with the new grey and smoke colors for the memo display functionality.


84-84: LGTM: Smart decision to conditionally show memo column.

Good approach to show the memo column only when there are memos present in the scene.


95-95: LGTM: UI state check updated for memo changes.

Added check for memo status changes to trigger a UI refresh when needed.


143-144: LGTM: Conditionally add memo column to display.

The implementation correctly adds the memo column only when memos are present.


175-175: LGTM: Proper needle index adjustment.

The code correctly adjusts the needle index calculation when the memo column is present to maintain proper needle numbering.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/main/python/main/ayab/engine/control.py (1)

274-277: ⚠️ Potential issue

Fix status memo type handling and improve error handling.

There's a type inconsistency and the bare except should be avoided as it catches all exceptions, potentially hiding unexpected errors.

-        try:
-            self.status.memo = self.memos[self.pat_row]
-        except:
-            self.status.memo = "0"
+        try:
+            self.status.memo = str(self.memos[self.pat_row])
+        except IndexError:
+            self.status.memo = "0"
+        except Exception as e:
+            self.logger.warning(f"Error setting memo status: {str(e)}")
+            self.status.memo = "0"

This ensures the memo is always converted to a string type before assignment and provides more specific error handling.

🧰 Tools
🪛 Ruff (0.8.2)

276-276: Do not use bare except

(E722)

🧹 Nitpick comments (2)
src/main/python/main/ayab/engine/control.py (2)

237-240: Improve error handling for memo logging.

The current implementation uses a bare except which catches all exceptions, potentially hiding unexpected errors. Additionally, it silently passes, which could make debugging difficult.

-        try:
-            msg = msg + " memo: " + str(self.memos[self.pat_row])
-        except:
-            pass
+        try:
+            msg = msg + " memo: " + str(self.memos[self.pat_row])
+        except IndexError:
+            msg = msg + " memo: <none>"
+        except Exception as e:
+            self.logger.debug(f"Error retrieving memo for logging: {str(e)}")

Alternatively, use contextlib.suppress as suggested by the static analysis:

-        try:
-            msg = msg + " memo: " + str(self.memos[self.pat_row])
-        except:
-            pass
+        from contextlib import suppress
+        with suppress(Exception):
+            msg = msg + " memo: " + str(self.memos[self.pat_row])
🧰 Tools
🪛 Ruff (0.8.2)

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


237-240: Consider refactoring memo access to reduce code duplication.

Both try-except blocks for memo access have similar logic but handle errors differently. A helper method would make this more consistent and maintainable.

+    def _get_memo(self, row_index: int, default_value: str = "0") -> str:
+        """Get the memo for the specified row index with error handling."""
+        try:
+            return str(self.memos[row_index])
+        except IndexError:
+            return default_value
+        except Exception as e:
+            self.logger.warning(f"Error accessing memo: {str(e)}")
+            return default_value
+

Then you could use it in both places:

-        try:
-            msg = msg + " memo: " + str(self.memos[self.pat_row])
-        except:
-            pass
+        memo = self._get_memo(self.pat_row, "<none>")
+        msg = msg + " memo: " + memo

And:

-        try:
-            self.status.memo = self.memos[self.pat_row]
-        except:
-            self.status.memo = "0"
+        self.status.memo = self._get_memo(self.pat_row)

Also applies to: 274-277

🧰 Tools
🪛 Ruff (0.8.2)

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c8e2c0 and 78f63e9.

📒 Files selected for processing (1)
  • src/main/python/main/ayab/engine/control.py (4 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
src/main/python/main/ayab/engine/control.py

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


276-276: Do not use bare except

(E722)

🔇 Additional comments (3)
src/main/python/main/ayab/engine/control.py (3)

64-64: Fix type annotation for memos attribute.

There's a type inconsistency in the PR. The memos attribute is defined as list[str] here, but in a previous review comment, it was suggested to be list[int]. Based on line 277 where a string value "0" is assigned to self.status.memo, it appears that the type should indeed be list[str].


89-90: Good implementation of the memos parameter.

The parameter is correctly added to the method signature and properly assigned to the instance variable. This matches the PR objectives of propagating memo information through the control flow.

Also applies to: 97-97


237-240:

❓ Verification inconclusive

Verify that the memo implementation is robust for future data formats.

Based on the PR comments, there was a discussion about using JSON for memo data to improve flexibility. While the current implementation uses simple strings, consider whether this approach will be robust enough for future requirements.

If you plan to extend memo functionality in the future, a more structured format like JSON might be beneficial. You could implement this more robustly now, or document your decision to keep it simple with a comment explaining the tradeoffs.

Also applies to: 274-277


🏁 Script executed:

#!/bin/bash
# Check how memo data is extracted from PNG files to understand the format
rg -A 5 "extract.*memo" --glob "*.py"

Length of output: 441


Action Required: Revisit Memo Handling for Future Scalability

The current implementation in src/main/python/main/ayab/engine/control.py (lines 237–240 and 274–277) simply concatenates a memo string using a try/except block. Although this works now, a recent discussion suggested using a structured format like JSON for memo data to better support future requirements. Notably, a related TODO in src/main/python/main/ayab/pattern_import.py highlights that memo extraction still needs attention.

  • Consider: Whether a JSON-based approach or a similarly structured format might be more future-proof.
  • Alternatively: Document the design decision for using simple strings by adding an inline comment, specifying the tradeoffs involved.
  • Note: Evaluate if the current try/except block might hide errors during memo processing, which could become problematic as the memo format evolves.
🧰 Tools
🪛 Ruff (0.8.2)

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/main/python/main/ayab/image.py (2)

59-59: ⚠️ Potential issue

Fix type annotation for memos list.

There's a type mismatch across the codebase. In this file, memos is typed as list[str], but in other files it's expected to be list[int]. Pipeline failures confirm this inconsistency.

-        self.memos: list[str] = []
+        self.memos: list[int] = []

117-131: 🛠️ Refactor suggestion

Improve error handling in memo parsing.

The implementation has potential issues with error handling:

  1. Using a bare except block (line 127) can catch unexpected exceptions and mask errors.
  2. The code would benefit from more robust error handling with specific error types.
  3. The logging is helpful, but could be more specific about what failed.
        if suffix == ".png":
            # check metadata for memo information
            self.image.load()
            if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
                comment = str(self.image.info["Comment"])
                if comment.startswith("AYAB:"):
                    # update memo information
-                    for i in range(len(comment) - 5):
-                        try:
-                            self.memos.append(comment[i + 5])
-                        except:
-                            self.memos.append('0')
+                    try:
+                        for i in range(len(comment) - 5):
+                            char = comment[i + 5]
+                            if char.isdigit():
+                                self.memos.append(int(char))
+                            else:
+                                logging.warning(f"Non-digit character '{char}' found in memo data")
+                    except Exception as e:
+                        logging.error(f"Error parsing memo data: {str(e)}")
                # report metadata
                logging.info("File metadata Comment tag: " + comment)
                logging.info("File memo information: " + str(self.memos))
🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 274b352 and c5a6c30.

📒 Files selected for processing (3)
  • src/main/python/main/ayab/ayab.py (2 hunks)
  • src/main/python/main/ayab/image.py (8 hunks)
  • src/main/python/main/ayab/transforms.py (2 hunks)
🧰 Additional context used
🧬 Code Definitions (3)
src/main/python/main/ayab/image.py (1)
src/main/python/main/ayab/transforms.py (10)
  • ImageTransform (29-131)
  • Mirrors (134-147)
  • invert (48-54)
  • repeat (65-79)
  • stretch (112-123)
  • reflect (82-109)
  • hflip (57-58)
  • vflip (61-62)
  • rotate_left (38-40)
  • rotate_right (43-45)
src/main/python/main/ayab/transforms.py (1)
src/main/python/main/ayab/image.py (3)
  • hflip (174-175)
  • repeat (139-152)
  • vflip (177-178)
src/main/python/main/ayab/ayab.py (1)
src/main/python/main/ayab/transforms.py (1)
  • ImageTransform (29-131)
🪛 Ruff (0.8.2)
src/main/python/main/ayab/image.py

127-127: Do not use bare except

(E722)

src/main/python/main/ayab/transforms.py

99-99: Undefined name InageTransform

(F821)

🪛 GitHub Actions: Python Tests
src/main/python/main/ayab/transforms.py

[error] 99-99: F821 undefined name 'InageTransform'

🔇 Additional comments (8)
src/main/python/main/ayab/transforms.py (2)

29-29: Class renaming completed correctly.

The class has been successfully renamed from Transform to ImageTransform. This is a good change for clarity as it better represents the class's purpose of transforming images.


100-100: Method calls updated correctly.

The method calls have been properly updated to use the new class name ImageTransform. This ensures consistency across the codebase.

Also applies to: 105-106

src/main/python/main/ayab/image.py (4)

30-30: Import statement updated correctly.

The import statement has been properly updated to use ImageTransform instead of Transform, maintaining consistency with the class renaming.


106-106: Proper initialization of memos list.

The code correctly resets the memos list at the beginning of the __open method, ensuring there's no leftover data from previous operations.


137-137: Method calls updated correctly.

All the method calls have been properly updated to use ImageTransform instead of Transform, maintaining consistency with the class renaming.

Also applies to: 152-152, 167-167, 172-172, 175-175, 178-178, 181-181, 184-184


92-97: Improved error messages in display_blocking_popup calls.

The removal of "FIXME" comments from the display_blocking_popup calls improves the code quality. Error messages are now cleaner and more user-friendly.

src/main/python/main/ayab/ayab.py (2)

35-35: Import statement updated correctly.

The import statement has been properly updated to use ImageTransform instead of Transform, maintaining consistency with the class renaming.


121-121: Comment and code updated correctly.

The comment and code have been properly updated to reference ImageTransform instead of Transform, maintaining consistency with the class renaming.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/main/python/main/ayab/image.py (1)

59-59: ⚠️ Potential issue

Fix type annotation for memos list

There's a type mismatch across the codebase. In this file, memos is typed as list[str], but in other files it's expected to be list[int] based on the Pattern class's memos accessor method. This inconsistency could lead to type errors or unexpected behavior.

-        self.memos: list[str] = []
+        self.memos: list[int] = []
🧹 Nitpick comments (1)
src/main/python/main/ayab/image.py (1)

117-131: Consider using JSON format for memo data

Based on PR discussions, using JSON would provide more flexibility for future extensions (versioning, carriage information, colors, etc.) and simplify the parsing process.

            if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
                comment = str(self.image.info["Comment"])
                if comment.startswith("AYAB:"):
-                    # update memo information
-                    for i in range(len(comment) - 5):
-                        try:
-                            self.memos.append(comment[i + 5])
-                        except:
-                            self.memos.append('0')
+                    # Parse JSON data after "AYAB:" prefix
+                    try:
+                        import json
+                        memo_data = json.loads(comment[5:])
+                        if "memos" in memo_data and isinstance(memo_data["memos"], list):
+                            self.memos = [int(m) for m in memo_data["memos"] if isinstance(m, (int, str)) and (isinstance(m, int) or m.isdigit())]
+                    except json.JSONDecodeError as e:
+                        logging.error(f"Invalid JSON in memo data: {str(e)}")
+                    except Exception as e:
+                        logging.error(f"Error parsing memo data: {str(e)}")
🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5a6c30 and 830ed2b.

📒 Files selected for processing (3)
  • src/main/python/main/ayab/ayab.py (2 hunks)
  • src/main/python/main/ayab/image.py (8 hunks)
  • src/main/python/main/ayab/transforms.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/python/main/ayab/transforms.py
  • src/main/python/main/ayab/ayab.py
🧰 Additional context used
🧬 Code Definitions (1)
src/main/python/main/ayab/image.py (2)
src/main/python/main/ayab/transforms.py (10)
  • ImageTransform (29-131)
  • Mirrors (134-147)
  • invert (48-54)
  • repeat (65-79)
  • stretch (112-123)
  • reflect (82-109)
  • hflip (57-58)
  • vflip (61-62)
  • rotate_left (38-40)
  • rotate_right (43-45)
src/main/python/main/ayab/engine/pattern.py (1)
  • memos (203-204)
🪛 Ruff (0.8.2)
src/main/python/main/ayab/image.py

127-127: Do not use bare except

(E722)

🔇 Additional comments (3)
src/main/python/main/ayab/image.py (3)

30-30: Transform class changes look good

The consistent update from Transform to ImageTransform across all transform method calls is properly implemented. The code now uses the ImageTransform class which extends Image.Image and provides static methods for image transformations.

Also applies to: 137-137, 152-152, 167-167, 172-172, 175-175, 178-178, 181-181, 184-184


92-92: Cleanup of error message blocks

The removal of FIXME comments from the display_blocking_popup calls is a good cleanup.

Also applies to: 97-97


106-106: Memo reset properly implemented

Clearing the memos list at the beginning of the __open method ensures that memo information is properly refreshed when loading a new image.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/main/python/main/ayab/image.py (2)

59-59: ⚠️ Potential issue

Fix type annotation for memos list

There's a type inconsistency between this declaration and how memos are used elsewhere in the codebase. In this file, memos is typed as list[str], but other parts of the codebase expect it to be list[int].

-        self.memos: list[str] = []
+        self.memos: list[int] = []

124-128: ⚠️ Potential issue

Improve memo parsing with proper validation and error handling

The current implementation has several issues:

  1. Using a bare except clause is considered poor practice
  2. There's inconsistency between storing string values but handling them as integers elsewhere
  3. No validation is performed on the input characters
-                    for i in range(len(comment) - 5):
-                        try:
-                            self.memos.append(comment[i + 5])
-                        except:
-                            self.memos.append('0')
+                    memo_data = comment[5:]
+                    try:
+                        for char in memo_data:
+                            if char.isdigit():
+                                self.memos.append(int(char))
+                            else:
+                                logging.warning(f"Non-digit character '{char}' found in memo data, skipping")
+                    except Exception as e:
+                        logging.error(f"Error parsing memo data: {str(e)}")
🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)

🧹 Nitpick comments (3)
src/main/python/main/ayab/image.py (3)

183-184: Consider reversing memos instead of clearing them on vertical flip

If memos correspond to rows in the image, a vertical flip should reverse the memos list rather than clearing it, which would preserve the memo information in the correct order for the flipped image.

        self.apply_transform(ImageTransform.vflip)
-        self.memos = []
+        self.memos = self.memos[::-1]  # Reverse the list

153-154: Consider horizontal repetition for memos

When repeating the image, you only multiply the memos list by the vertical repeat factor. If memos correspond to rows, this makes sense, but consider also including a comment to explain this behavior for clarity.

        self.apply_transform(ImageTransform.repeat, v[0], h[0])
-        self.memos = self.memos * v[0]
+        # Multiply memos by vertical repeat factor only since memos correspond to rows
+        self.memos = self.memos * v[0]

117-131: Consider using JSON format for memo data as suggested in PR comments

As discussed in the PR comments, using JSON for memo data would provide more flexibility and simplify parsing. The current string-based approach limits you to single characters and requires more complex parsing logic.

                comment = str(self.image.info["Comment"])
                if comment.startswith("AYAB:"):
                    # update memo information
-                    for i in range(len(comment) - 5):
-                        try:
-                            self.memos.append(comment[i + 5])
-                        except:
-                            self.memos.append('0')
+                    try:
+                        import json
+                        # Extract JSON data after "AYAB:"
+                        json_data = comment[5:]
+                        memo_data = json.loads(json_data)
+                        if isinstance(memo_data, list):
+                            self.memos = [int(m) for m in memo_data if isinstance(m, (int, str)) and str(m).isdigit()]
+                        else:
+                            logging.warning(f"Invalid memo data format: {memo_data}")
+                    except json.JSONDecodeError as e:
+                        logging.error(f"Error parsing JSON memo data: {str(e)}")
+                        # Fall back to old format for backward compatibility
+                        for char in comment[5:]:
+                            if char.isdigit():
+                                self.memos.append(int(char))
+                            else:
+                                logging.warning(f"Non-digit character '{char}' found in memo data, skipping")
+                    except Exception as e:
+                        logging.error(f"Error processing memo data: {str(e)}")

Do you think using JSON would be a better approach for storing memo data? This would align with the suggestion from contributor jpcornil-git in the PR comments.

🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 830ed2b and 6b125a1.

📒 Files selected for processing (1)
  • src/main/python/main/ayab/image.py (8 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
src/main/python/main/ayab/image.py (1)
src/main/python/main/ayab/transforms.py (10)
  • ImageTransform (29-131)
  • Mirrors (134-147)
  • invert (48-54)
  • repeat (65-79)
  • stretch (112-123)
  • reflect (82-109)
  • hflip (57-58)
  • vflip (61-62)
  • rotate_left (38-40)
  • rotate_right (43-45)
🪛 Ruff (0.8.2)
src/main/python/main/ayab/image.py

127-127: Do not use bare except

(E722)

@t0mpr1c3
Copy link
Contributor Author

t0mpr1c3 commented Mar 31, 2025

Treatment of Image Transforms:

  • Invert and HFlip should be safe for Memos.
  • Repeat repeats Memos by the vertical scale factor.
  • Vertical flip, Reflect, and Stretch considered unsafe until we know with certainty how Brother machines treat Memo data for these transforms.
  • All other image transforms (rotations) are potentially unsafe and result in the deletion of Memos.

@t0mpr1c3
Copy link
Contributor Author

I don't know how to freeze the Memo column. @X-sam ?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/main/python/main/ayab/engine/control.py (1)

274-277: 🛠️ Refactor suggestion

Fix error handling in status update

The code uses a bare except clause which can mask unexpected errors. Additionally, specify the type of exceptions you expect.

-        try:
-            self.status.memo = self.memos[self.pat_row]
-        except:
-            self.status.memo = "0"
+        try:
+            self.status.memo = self.memos[self.pat_row]
+        except IndexError:
+            self.status.memo = "0"
+        except Exception as e:
+            self.logger.warning(f"Error setting memo status: {str(e)}")
+            self.status.memo = "0"
🧰 Tools
🪛 Ruff (0.8.2)

276-276: Do not use bare except

(E722)

🧹 Nitpick comments (3)
src/main/python/main/ayab/image.py (3)

169-170: Document memo behavior for image transformations

The stretch transform clears memos while repeat multiplies them. Consider adding a comment explaining why these transformations affect memo data differently.

        self.apply_transform(ImageTransform.stretch, v[0], h[0])
-        self.memos = []
+        # Clear memos as stretching distorts the relationship between rows and memo data
+        self.memos = []

175-176: Consider consistent documentation for memo behavior

Multiple transformations clear the memo data without explanation. Add consistent documentation to explain the memo handling policy for each transform.

        self.apply_transform(ImageTransform.reflect, m.mirrors)
-        self.memos = []
+        # Clear memos as reflection changes the structure of the image rows
+        self.memos = []

Apply similar comments to the other transforms that clear memo data.

Also applies to: 183-184, 187-188, 191-192


124-128: Consider implementing reviewer suggestion for JSON memo format

As discussed in the PR comments, using JSON for memo data would provide more flexibility and extensibility. This would allow storing structured data rather than just single characters.

Example implementation for JSON-based memos:

import json

# When parsing the comment:
if comment.startswith("AYAB:"):
    try:
        json_data = comment[5:]
        memo_dict = json.loads(json_data)
        # This allows for future expansion with fields like "version", "colors", etc.
        self.memos = memo_dict.get("memos", [])
        logging.info(f"Parsed JSON memo data: {memo_dict}")
    except json.JSONDecodeError:
        # Fallback for backward compatibility
        logging.info("Using legacy memo format")
        self.memos = [char for char in comment[5:]]
    except Exception as e:
        logging.error(f"Error parsing memo data: {str(e)}")
        self.memos = []

This would support both the current simple format and a more flexible JSON format for future enhancements.

🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b125a1 and 98982d7.

📒 Files selected for processing (11)
  • src/main/python/main/ayab/ayab.py (2 hunks)
  • src/main/python/main/ayab/engine/control.py (4 hunks)
  • src/main/python/main/ayab/engine/engine.py (6 hunks)
  • src/main/python/main/ayab/engine/pattern.py (2 hunks)
  • src/main/python/main/ayab/engine/status.py (3 hunks)
  • src/main/python/main/ayab/gui_fsm.py (2 hunks)
  • src/main/python/main/ayab/image.py (8 hunks)
  • src/main/python/main/ayab/knitprogress.py (8 hunks)
  • src/main/python/main/ayab/pattern_import.py (1 hunks)
  • src/main/python/main/ayab/tests/test_control.py (7 hunks)
  • src/main/python/main/ayab/transforms.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/main/python/main/ayab/pattern_import.py
  • src/main/python/main/ayab/engine/status.py
  • src/main/python/main/ayab/gui_fsm.py
  • src/main/python/main/ayab/transforms.py
  • src/main/python/main/ayab/knitprogress.py
  • src/main/python/main/ayab/engine/pattern.py
  • src/main/python/main/ayab/ayab.py
  • src/main/python/main/ayab/tests/test_control.py
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/main/python/main/ayab/engine/control.py (3)
src/main/python/main/ayab/engine/pattern.py (2)
  • memos (203-204)
  • Pattern (32-207)
src/main/python/main/ayab/engine/options.py (1)
  • OptionsTab (59-252)
src/main/python/main/ayab/engine/engine_fsm.py (1)
  • Operation (39-41)
src/main/python/main/ayab/image.py (2)
src/main/python/main/ayab/transforms.py (10)
  • ImageTransform (29-131)
  • Mirrors (134-147)
  • invert (48-54)
  • repeat (65-79)
  • stretch (112-123)
  • reflect (82-109)
  • hflip (57-58)
  • vflip (61-62)
  • rotate_left (38-40)
  • rotate_right (43-45)
src/main/python/main/ayab/engine/pattern.py (1)
  • memos (203-204)
🪛 Ruff (0.8.2)
src/main/python/main/ayab/engine/control.py

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


276-276: Do not use bare except

(E722)

src/main/python/main/ayab/image.py

127-127: Do not use bare except

(E722)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Create and upload Linux AppImage release
  • GitHub Check: Create and upload Windows build
  • GitHub Check: Create and upload Mac OSX build
  • GitHub Check: Create and upload Mac OSX build
  • GitHub Check: Create and upload Linux AppImage release
  • GitHub Check: Create and upload Windows build
🔇 Additional comments (15)
src/main/python/main/ayab/engine/control.py (2)

64-64: Fix type annotation for memos attribute

The memos attribute should be typed as list[str] to match the implementation. This is consistent with how memo data is stored in the AyabImage class (line 59) and used throughout the codebase.


89-90: LGTM: Updated method signature to include memo information

The start method now accepts a memos parameter, maintaining consistency with the memo implementation across the codebase.

Also applies to: 97-97

src/main/python/main/ayab/engine/engine.py (7)

38-38: LGTM: Updated import to use AyabImage

The import statement now correctly imports AyabImage, which is necessary for accessing the memo data.


54-54: LGTM: Added memos attribute to Engine class

The memos attribute is correctly added to the Engine class and initialized in the constructor.


71-71: LGTM: Properly initialized memos list

The memos attribute is correctly initialized as an empty list in the constructor.


122-122: LGTM: Updated method signature to use AyabImage

The knit_config method now correctly accepts an AyabImage instead of a PIL Image.Image, allowing access to the memo data.


131-131: LGTM: Updated image processing to use AyabImage

The code correctly accesses the image attribute of the AyabImage object.


135-136: LGTM: Proper assignment of pattern and memos

The code correctly passes the image and memos to the Pattern constructor and assigns im.memos to self.memos.


173-173: LGTM: Updated control.start call to include memos

The run method now correctly passes self.memos to self.control.start.

src/main/python/main/ayab/image.py (6)

30-30: LGTM: Updated import to use ImageTransform

The import statement now correctly uses ImageTransform instead of Transform. This is consistent with the actual class name used in the transforms module.


59-59: LGTM: Added memos attribute to AyabImage class

The memos attribute is correctly initialized as an empty list of strings.


106-106: LGTM: Properly initialize memos list

The memos list is correctly initialized at the beginning of the __open method.


137-138: LGTM: Updated transform call and maintained memo data

The code correctly calls ImageTransform.invert and preserves memo data, which is appropriate for this transform.


153-154: LGTM: Updated transform call with memo handling

The code correctly multiplies the memo list by the vertical repeat factor, ensuring alignment with the expanded image rows.


179-180: LGTM: Maintained memo data for horizontal flip

The code correctly preserves memo data for horizontal flipping, which is appropriate since it doesn't affect row organization.

Comment on lines 117 to 132
if suffix == ".png":
# check metadata for memo information
self.image.load()
if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
comment = str(self.image.info["Comment"])
if comment.startswith("AYAB:"):
# update memo information
for i in range(len(comment) - 5):
try:
self.memos.append(comment[i + 5])
except:
self.memos.append('0')
# report metadata
logging.info("File metadata Comment tag: " + comment)
logging.info("File memo information: " + str(self.memos))
self.image = self.image.convert("RGBA")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve memo extraction with proper validation and error handling

The current implementation has several issues:

  1. It uses a bare except clause which can mask unexpected errors
  2. There's no validation that the characters after "AYAB:" are valid memo data
  3. The error handling simply defaults to '0' without logging the issue
        if suffix == ".png":
            # check metadata for memo information
            self.image.load()
            if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
                comment = str(self.image.info["Comment"])
                if comment.startswith("AYAB:"):
                    # update memo information
-                    for i in range(len(comment) - 5):
-                        try:
-                            self.memos.append(comment[i + 5])
-                        except:
-                            self.memos.append('0')
+                    memo_data = comment[5:]
+                    logging.info(f"Extracting memo data: '{memo_data}'")
+                    for char in memo_data:
+                        self.memos.append(char)
                # report metadata
                logging.info("File metadata Comment tag: " + comment)
                logging.info("File memo information: " + str(self.memos))

Consider the JSON format suggested in PR comments for more flexibility:

import json
# Within the if comment.startswith("AYAB:") block:
try:
    json_data = comment[5:]
    memo_dict = json.loads(json_data)
    self.memos = memo_dict.get("memos", [])
    logging.info(f"Parsed JSON memo data: {self.memos}")
except json.JSONDecodeError as e:
    logging.warning(f"Invalid JSON in memo data: {e}")
    # Fall back to character-by-character parsing
    self.memos = [char for char in comment[5:]]
except Exception as e:
    logging.error(f"Error parsing memo data: {str(e)}")
🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (5)
src/main/python/main/ayab/engine/control.py (3)

64-64: ⚠️ Potential issue

Fix type annotation for the memos attribute.

The memos attribute is defined with the type list[str] here, but there's a type mismatch in the codebase. Based on past reviews, there appear to be expectations that this should be list[int] in some places.

-    memos: list[str]
+    memos: list[int]

237-240: 🛠️ Refactor suggestion

Improve error handling for memo logging.

The bare except clause is considered poor practice as it catches all exceptions, including KeyboardInterrupt and SystemExit. Additionally, the silent pass can mask unexpected errors.

-        try:
-            msg = msg + " memo: " + str(self.memos[self.pat_row])
-        except:
-            pass
+        from contextlib import suppress
+        with suppress(IndexError):
+            msg = msg + " memo: " + str(self.memos[self.pat_row])

Alternatively, you could use more specific exception handling:

-        try:
-            msg = msg + " memo: " + str(self.memos[self.pat_row])
-        except:
-            pass
+        try:
+            msg = msg + " memo: " + str(self.memos[self.pat_row])
+        except IndexError:
+            # Handle the case when pat_row is out of bounds
+            pass
+        except Exception as e:
+            self.logger.debug(f"Error logging memo: {str(e)}")
🧰 Tools
🪛 Ruff (0.8.2)

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


274-277: ⚠️ Potential issue

Fix status memo type handling.

There's a type mismatch here: self.status.memo is expected to be a string, but if self.memos is intended to be a list of integers (as suggested by other parts of the codebase), you should explicitly convert the value to a string. Also, the bare except should be replaced with specific exception handling.

-        try:
-            self.status.memo = self.memos[self.pat_row]
-        except:
-            self.status.memo = "0"
+        try:
+            self.status.memo = str(self.memos[self.pat_row])
+        except IndexError:
+            self.status.memo = "0"
+        except Exception as e:
+            self.logger.warning(f"Error setting memo status: {str(e)}")
+            self.status.memo = "0"
🧰 Tools
🪛 Ruff (0.8.2)

276-276: Do not use bare except

(E722)

src/main/python/main/ayab/image.py (2)

59-59: ⚠️ Potential issue

Fix type annotation for memos list.

There's a type mismatch across the codebase. In this file, memos is typed as list[str], but in other files, it might be expected to be list[int]. This inconsistency could lead to type errors.

-        self.memos: list[str] = []
+        self.memos: list[int] = []

124-128: 🛠️ Refactor suggestion

Improve memo parsing with proper validation and error handling.

The current implementation has several issues:

  1. Using a bare except clause is considered poor practice
  2. There's no validation that the characters are valid memo data
  3. The error handling simply defaults to '0' without logging the issue
-                    for i in range(len(comment) - 5):
-                        try:
-                            self.memos.append(comment[i + 5])
-                        except:
-                            self.memos.append('0')
+                    memo_data = comment[5:]
+                    try:
+                        for char in memo_data:
+                            if char.isdigit():
+                                self.memos.append(int(char))
+                            else:
+                                logging.warning(f"Non-digit character '{char}' found in memo data, skipping")
+                    except Exception as e:
+                        logging.error(f"Error parsing memo data: {str(e)}")
🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)

🧹 Nitpick comments (2)
src/main/python/main/ayab/image.py (2)

117-132: Consider using JSON format for memo data.

Based on the PR discussion, jpcornil-git suggested using a JSON format for storing memo data, which would provide greater flexibility for future extensions. This would allow for adding more data types like versioning, carriage information, and colors.

Consider implementing a JSON-based approach:

        if suffix == ".png":
            # check metadata for memo information
            self.image.load()
            if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
                comment = str(self.image.info["Comment"])
                if comment.startswith("AYAB:"):
-                    # update memo information
-                    for i in range(len(comment) - 5):
-                        try:
-                            self.memos.append(comment[i + 5])
-                        except:
-                            self.memos.append('0')
+                    # Parse JSON data
+                    import json
+                    try:
+                        json_data = comment[5:]
+                        memo_dict = json.loads(json_data)
+                        if "memos" in memo_dict and isinstance(memo_dict["memos"], list):
+                            self.memos = [int(m) if isinstance(m, (int, str)) and str(m).isdigit() else 0 
+                                          for m in memo_dict["memos"]]
+                        logging.info(f"Parsed JSON memo data: {self.memos}")
+                    except json.JSONDecodeError as e:
+                        logging.warning(f"Invalid JSON in memo data: {e}")
+                        # Fall back to character-by-character parsing
+                        self.memos = [int(char) if char.isdigit() else 0 for char in comment[5:]]
+                    except Exception as e:
+                        logging.error(f"Error parsing memo data: {str(e)}")
                # report metadata
                logging.info("File metadata Comment tag: " + comment)
                logging.info("File memo information: " + str(self.memos))

This approach provides:

  1. Flexibility to add more data in the future
  2. Proper error handling with specific exceptions
  3. A fallback mechanism for backward compatibility
🧰 Tools
🪛 Ruff (0.8.2)

127-127: Do not use bare except

(E722)


169-170: Communicate memo clearing to users.

When performing operations that clear memo data (stretch, reflect, vflip, rotate_left, rotate_right), consider logging this action and potentially informing the user that memo information will be lost.

        self.apply_transform(ImageTransform.stretch, v[0], h[0])
+        if self.memos:
+            logging.info("Clearing memo information after stretch operation")
        self.memos = []

Apply similar changes to the other methods that clear memo data.

Also applies to: 175-176, 183-184, 187-188, 191-192

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d07b55d and 12b877c.

📒 Files selected for processing (11)
  • src/main/python/main/ayab/ayab.py (2 hunks)
  • src/main/python/main/ayab/engine/control.py (4 hunks)
  • src/main/python/main/ayab/engine/engine.py (6 hunks)
  • src/main/python/main/ayab/engine/pattern.py (2 hunks)
  • src/main/python/main/ayab/engine/status.py (3 hunks)
  • src/main/python/main/ayab/gui_fsm.py (2 hunks)
  • src/main/python/main/ayab/image.py (8 hunks)
  • src/main/python/main/ayab/knitprogress.py (8 hunks)
  • src/main/python/main/ayab/pattern_import.py (1 hunks)
  • src/main/python/main/ayab/tests/test_control.py (7 hunks)
  • src/main/python/main/ayab/transforms.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/main/python/main/ayab/pattern_import.py
  • src/main/python/main/ayab/gui_fsm.py
  • src/main/python/main/ayab/transforms.py
  • src/main/python/main/ayab/ayab.py
  • src/main/python/main/ayab/engine/status.py
  • src/main/python/main/ayab/tests/test_control.py
  • src/main/python/main/ayab/engine/pattern.py
  • src/main/python/main/ayab/engine/engine.py
  • src/main/python/main/ayab/knitprogress.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/python/main/ayab/engine/control.py (2)
src/main/python/main/ayab/engine/pattern.py (2)
  • memos (203-204)
  • Pattern (32-207)
src/main/python/main/ayab/engine/engine_fsm.py (1)
  • Operation (39-41)
🪛 Ruff (0.8.2)
src/main/python/main/ayab/engine/control.py

237-240: Use contextlib.suppress(Exception) instead of try-except-pass

Replace with contextlib.suppress(Exception)

(SIM105)


239-239: Do not use bare except

(E722)


276-276: Do not use bare except

(E722)

src/main/python/main/ayab/image.py

127-127: Do not use bare except

(E722)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Create and upload Mac OSX build
  • GitHub Check: Create and upload Linux AppImage release
  • GitHub Check: run-pytest
  • GitHub Check: Create and upload Windows build
  • GitHub Check: Create and upload Windows build
  • GitHub Check: Create and upload Linux AppImage release
  • GitHub Check: Create and upload Mac OSX build
  • GitHub Check: run-pytest
🔇 Additional comments (1)
src/main/python/main/ayab/image.py (1)

153-154: Verify memo handling in repeat operations.

The implementation assumes that memos correspond to rows in the image and simply multiplies the list by the vertical repeat count. Ensure this is the correct behavior, especially if memos contain row-specific information.

If memos contain more complex data or have specific meanings per row, consider more sophisticated handling, such as:

-        self.memos = self.memos * v[0]
+        # Create new memo list with appropriate values for each repeated row
+        original_memos = self.memos.copy()
+        self.memos = []
+        for _ in range(v[0]):
+            self.memos.extend(original_memos)
+        logging.info(f"Repeated memos {v[0]} times, new count: {len(self.memos)}")

@dl1com
Copy link
Contributor

dl1com commented Apr 17, 2025

What's the status of this PR?
There are still some CodeRabbit and SonarQube findings to clarify.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
src/main/python/main/ayab/image.py (1)

203-204: 🛠️ Refactor suggestion

Add error handling to the transform application

The previous version of this method likely had try-except error handling which has been removed. Consider adding appropriate error handling to prevent crashes if a transform operation fails.

    def apply_transform(
        self,
        transform: Callable[..., Image.Image],
        *args: tuple[int, int] | list[int] | int,
    ) -> None:
        """Executes an image transform specified by function and args."""
-        self.image = transform(self.image, args)
+        try:
+            self.image = transform(self.image, args)
+        except Exception as e:
+            display_blocking_popup(
+                QCoreApplication.translate("Image", "Error applying transform"), "error"
+            )
+            logging.error(f"Error in transform {transform.__name__}: {str(e)}")
+            return
♻️ Duplicate comments (2)
src/main/python/main/ayab/image.py (2)

59-59: ⚠️ Potential issue

Fix type annotation for memos list

There's a type mismatch across the codebase. In this file, memos is typed as list[str], but in other files it's expected to be list[int]. This inconsistency can lead to runtime errors.

-        self.memos: list[str] = []
+        self.memos: list[int] = []

124-126: ⚠️ Potential issue

Improve memo parsing with proper validation and error handling

The current implementation lacks validation for the memo characters and doesn't handle potential errors. This could cause issues if the memo format is unexpected.

-                    for i in range(len(comment) - 5):
-                        self.memos.append(comment[i + 5])
+                    memo_data = comment[5:]
+                    try:
+                        for char in memo_data:
+                            if char.isdigit():
+                                self.memos.append(int(char))
+                            else:
+                                logging.warning(f"Non-digit character '{char}' found in memo data, skipping")
+                    except Exception as e:
+                        logging.error(f"Error parsing memo data: {str(e)}")
🧹 Nitpick comments (1)
src/main/python/main/ayab/image.py (1)

117-128: Consider JSON format for better extensibility

As discussed in the PR comments, using JSON for memo data would provide better extensibility and simplify parsing. It would allow for additional metadata and remove the limitation of single-digit numbers.

            if "Comment" in self.image.info and len(str(self.image.info["Comment"])) > 0:
                comment = str(self.image.info["Comment"])
                if comment.startswith("AYAB:"):
                    # update memo information
-                    for i in range(len(comment) - 5):
-                        self.memos.append(comment[i + 5])
+                    try:
+                        import json
+                        json_data = comment[5:]
+                        memo_dict = json.loads(json_data)
+                        self.memos = memo_dict.get("memos", [])
+                        logging.info(f"Parsed JSON memo data: {self.memos}")
+                    except json.JSONDecodeError:
+                        # Fall back to character-by-character parsing for backward compatibility
+                        memo_data = comment[5:]
+                        self.memos = []
+                        for char in memo_data:
+                            if char.isdigit():
+                                self.memos.append(int(char))
+                            else:
+                                logging.warning(f"Non-digit character '{char}' found in memo data, skipping")
+                    except Exception as e:
+                        logging.error(f"Error parsing memo data: {str(e)}")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14a2246 and 12e40c3.

📒 Files selected for processing (1)
  • src/main/python/main/ayab/image.py (8 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Create and upload Linux AppImage release
  • GitHub Check: Create and upload Windows build
  • GitHub Check: Create and upload Mac OSX build
  • GitHub Check: Create and upload Mac OSX build
  • GitHub Check: Create and upload Windows build
  • GitHub Check: Create and upload Linux AppImage release
🔇 Additional comments (1)
src/main/python/main/ayab/image.py (1)

150-151: Verify memo multiplication in repeat function

When repeating an image vertically, the memos are multiplied by the vertical repeat count. Ensure this is the desired behavior - will users expect to see the same memo value repeated for all repeated rows, or should the memo values only apply to the original rows?

Consider validating this behavior with users to confirm it matches their expectations.

Comment on lines +166 to +167
self.apply_transform(ImageTransform.stretch, v[0], h[0])
self.memos = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider preserving memo data for safe transforms

The current implementation clears memos for several transforms (stretch, reflect, vflip, rotate_left, rotate_right). According to the PR discussion, some of these might be safe in specific circumstances.

For user experience consistency, consider adding a warning dialog when a transform would clear memo data, giving users the option to proceed or cancel.

def _clear_memos_with_warning(self, transform_name):
    """Display warning and clear memos if user confirms."""
    result = display_warning_dialog(
        f"The {transform_name} transform will clear memo data. Proceed?",
        buttons=["Yes", "No"]
    )
    if result == "Yes":
        self.memos = []
        return True
    return False

# Then in transform methods like stretch:
def stretch(self):
    # ... existing code ...
    if self.memos and not self._clear_memos_with_warning("Stretch"):
        return  # User canceled
    self.apply_transform(ImageTransform.stretch, v[0], h[0])

Also applies to: 172-173, 180-181, 184-185, 188-189

Copy link

@t0mpr1c3
Copy link
Contributor Author

Not impressed with the remaining CodeRabbit suggestions.

@dl1com
Copy link
Contributor

dl1com commented Apr 20, 2025

Not impressed with the remaining CodeRabbit suggestions.

Please comment and resolve them, so we can proceed with the testing step.

But out of curiosity: #779 (comment) suggests improvements for handling the transforms where memo is about to be deleted, something you also mentioned in #779 (comment). So may be worth to follow the suggestion?

Comment on lines -127 to -128
if n_cols < 4:
self.hideColumn(5)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing those two lines (which do look like dead code, as the number of columns is synchronized with the size of the columns array earlier, so column 5 can't exist if len(columns) < 4) makes the n_cols assignment above obsolete as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants