From 330304b93b8265d37c53492cddbe1fd7c6ada296 Mon Sep 17 00:00:00 2001 From: dl1com <1631996+dl1com@users.noreply.github.com> Date: Wed, 19 Mar 2025 21:16:37 +0100 Subject: [PATCH 01/25] Extract memo information from PNG file Comment tag and display in Knitting Progress window --- src/main/python/main/ayab/engine/control.py | 12 +++++++- src/main/python/main/ayab/engine/engine.py | 12 +++++--- src/main/python/main/ayab/engine/pattern.py | 16 +++++++---- src/main/python/main/ayab/engine/status.py | 3 ++ src/main/python/main/ayab/gui_fsm.py | 3 +- src/main/python/main/ayab/image.py | 19 ++++++++++-- src/main/python/main/ayab/knitprogress.py | 32 ++++++++++++++++----- src/main/python/main/ayab/pattern_import.py | 2 ++ 8 files changed, 79 insertions(+), 20 deletions(-) diff --git a/src/main/python/main/ayab/engine/control.py b/src/main/python/main/ayab/engine/control.py index 82d33760..101495d8 100644 --- a/src/main/python/main/ayab/engine/control.py +++ b/src/main/python/main/ayab/engine/control.py @@ -61,6 +61,7 @@ class Control(SignalSender): initial_position: int len_pat_expanded: int line_block: int + memo: list[int] mode: Mode mode_func: ModeFuncType num_colors: int @@ -85,7 +86,7 @@ def __init__(self, parent: GuiMain, engine: Engine): self.api_version: int = self.FIRST_SUPPORTED_API_VERSION def start( - self, pattern: Pattern, options: OptionsTab, operation: Operation + self, pattern: Pattern, memo: list[int], options: OptionsTab, operation: Operation ) -> None: self.machine = options.machine if operation == Operation.KNIT: @@ -93,6 +94,7 @@ def start( self.line_block = 0 self.pattern_repeats = 0 self.pattern = pattern + self.memo = memo self.pat_height = pattern.pat_height self.num_colors = options.num_colors self.start_row = options.start_row @@ -232,6 +234,10 @@ def cnf_line_API6(self, line_number: int) -> bool: + " pat_row: " + str(self.pat_row) ) + try: + msg = msg + " memo: " + str(self.memo[self.pat_row]) + except: + pass if blank_line: msg += " BLANK LINE" else: @@ -265,6 +271,10 @@ def __update_status(self, line_number: int, color: int, bits: bitarray) -> None: self.status.total_rows = self.pat_height self.status.current_row = self.pat_row + 1 self.status.line_number = line_number + try: + self.status.memo = self.memo[self.pat_row] + except: + self.status.memo = 0 if self.inf_repeat: self.status.repeats = self.pattern_repeats if self.mode != Mode.SINGLEBED: diff --git a/src/main/python/main/ayab/engine/engine.py b/src/main/python/main/ayab/engine/engine.py index f78c9b99..5b7abe20 100644 --- a/src/main/python/main/ayab/engine/engine.py +++ b/src/main/python/main/ayab/engine/engine.py @@ -36,6 +36,7 @@ from .dock_gui import Ui_Dock from typing import TYPE_CHECKING, Literal, Optional, cast from ..signal_sender import SignalSender +from ..image import AyabImage if TYPE_CHECKING: from ..ayab import GuiMain @@ -51,6 +52,7 @@ class Engine(SignalSender, QDockWidget): port_opener = Signal() + memo: list[int] pattern: Pattern status: StatusTab @@ -67,6 +69,7 @@ def __init__(self, parent: GuiMain): parent.ui.dock_container_layout.addWidget(self) self.pattern: Pattern = None # type:ignore + self.memo = [] self.control = Control(parent, self) self.__feedback = FeedbackHandler(parent) self.__logger = logging.getLogger(type(self).__name__) @@ -117,7 +120,7 @@ def __populate_ports(self, port_list: Optional[list[str]] = None) -> None: def __read_portname(self) -> str: return self.ui.serial_port_dropdown.currentText() - def knit_config(self, image: Image.Image) -> None: + def knit_config(self, im: AyabImage) -> None: """ Read and check configuration options from options dock UI. """ @@ -126,11 +129,12 @@ def knit_config(self, image: Image.Image) -> None: self.__logger.debug(self.config.as_dict()) # start to knit with the bottom first - image = image.transpose(Image.FLIP_TOP_BOTTOM) + im.image = im.image.transpose(Image.FLIP_TOP_BOTTOM) # TODO: detect if previous conf had the same # image to avoid re-generating. - self.pattern = Pattern(image, self.config, self.config.num_colors) + self.pattern = Pattern(im, self.config, self.config.num_colors) + self.memo = im.memo # validate configuration options valid, msg = self.validate() @@ -167,7 +171,7 @@ def run(self, operation: Operation) -> None: # setup knitting controller self.config.portname = self.__read_portname() - self.control.start(self.pattern, self.config, operation) + self.control.start(self.pattern, self.memo, self.config, operation) with keep.presenting(on_fail="pass"): while True: diff --git a/src/main/python/main/ayab/engine/pattern.py b/src/main/python/main/ayab/engine/pattern.py index 49794f70..e542fb84 100644 --- a/src/main/python/main/ayab/engine/pattern.py +++ b/src/main/python/main/ayab/engine/pattern.py @@ -26,16 +26,18 @@ from PIL import Image from .options import Alignment, OptionsTab from .mode import Mode +from ..image import AyabImage from ..machine import Machine class Pattern(object): - def __init__(self, image: Image.Image, config: OptionsTab, num_colors: int = 2): - self.__pattern = ( - image.transpose(Image.FLIP_LEFT_RIGHT) if config.auto_mirror else image + def __init__(self, im: AyabImage, config: OptionsTab, num_colors: int = 2): + self.__pattern : Image.Image = ( + im.image.transpose(Image.FLIP_LEFT_RIGHT) if config.auto_mirror else im.image ) - self.__num_colors = num_colors - self.__alignment = Alignment.CENTER + self.__memo : list[int] = im.memo + self.__num_colors : int = num_colors + self.__alignment : Alignment = Alignment.CENTER self.__pat_start_needle: int = -1 self.__pat_end_needle: int = -1 self.__knit_start_needle: int = 0 @@ -198,5 +200,9 @@ def pat_width(self) -> int: def pattern_expanded(self) -> list[bitarray]: return self.__pattern_expanded + @property + def memo(self) -> list[int]: + return self.__memo + def array2rgb(self, a: list[int]) -> int: return (a[0] & 0xFF) * 0x10000 + (a[1] & 0xFF) * 0x100 + (a[2] & 0xFF) diff --git a/src/main/python/main/ayab/engine/status.py b/src/main/python/main/ayab/engine/status.py index c9919ab6..9cb94c41 100644 --- a/src/main/python/main/ayab/engine/status.py +++ b/src/main/python/main/ayab/engine/status.py @@ -119,6 +119,7 @@ class Status(object): current_row: int firmware_state: int line_number: int + memo: int repeats: int total_rows: int # carriage info @@ -145,6 +146,7 @@ def reset(self) -> None: self.current_row = -1 self.firmware_state = -1 self.line_number = -1 + self.memo = 0 self.repeats = -1 self.total_rows = -1 # carriage info @@ -159,6 +161,7 @@ def copy(self, status: Status) -> None: self.firmware_state = status.firmware_state self.current_row = status.current_row self.line_number = status.line_number + self.memo = status.memo self.repeats = status.repeats self.color_symbol = status.color_symbol self.color = status.color diff --git a/src/main/python/main/ayab/gui_fsm.py b/src/main/python/main/ayab/gui_fsm.py index 91bff5fd..bb759523 100644 --- a/src/main/python/main/ayab/gui_fsm.py +++ b/src/main/python/main/ayab/gui_fsm.py @@ -107,7 +107,7 @@ def set_transitions(self, parent: GuiMain) -> None: self.CONFIGURING.entered.connect(parent.menu.add_image_actions) self.CONFIGURING.entered.connect(parent.progbar.reset) self.CHECKING.entered.connect( - lambda: parent.engine.knit_config(parent.scene.ayabimage.image) + lambda: parent.engine.knit_config(parent.scene.ayabimage) ) self.KNITTING.entered.connect(parent.start_knitting) self.TESTING.entered.connect(parent.start_testing) @@ -145,6 +145,7 @@ def set_properties(self, parent: GuiMain) -> None: self.CONFIGURING.assignProperty(parent.ui.cancel_button, "enabled", "False") self.KNITTING.assignProperty(parent.ui.cancel_button, "enabled", "True") self.TESTING.assignProperty(parent.ui.cancel_button, "enabled", "False") + # Cancel Knitting menu action self.NO_IMAGE.assignProperty(parent.menu.ui.action_cancel, "enabled", "False") self.CONFIGURING.assignProperty( diff --git a/src/main/python/main/ayab/image.py b/src/main/python/main/ayab/image.py index 0259357f..2d0f51ef 100644 --- a/src/main/python/main/ayab/image.py +++ b/src/main/python/main/ayab/image.py @@ -18,6 +18,7 @@ # Andreas Müller, Christian Gerbrandt # https://github.com/AllYarnsAreBeautiful/ayab-desktop + from __future__ import annotations import logging from math import ceil @@ -55,6 +56,7 @@ def __init__(self, parent: GuiMain): super().__init__(parent.signal_receiver) self.__parent = parent self.image: Image.Image = None # type: ignore + self.memo: list = [] self.filename: Optional[str] = None self.filename_input = self.__parent.ui.filename_lineedit @@ -87,12 +89,12 @@ def __load(self, filename: str) -> None: display_blocking_popup( QCoreApplication.translate("Image", "Unable to load image file"), "error", - ) # FIXME translate + ) logging.error("Unable to load " + str(filename)) except Exception as e: display_blocking_popup( QCoreApplication.translate("Image", "Error loading image file"), "error" - ) # FIXME translate + ) logging.error("Error loading image: " + str(e)) raise else: @@ -111,6 +113,19 @@ def __open(self, filename: str) -> None: self.image = CutPatternConverter().pattern2im(filename) else: self.image = Image.open(filename) + 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])) + # report metadata + logging.info("File metadata Comment tag: " + comment) + logging.info("File memo information: " + str(self.memo)) self.image = self.image.convert("RGBA") self.emit_got_image_flag() self.emit_image_resizer() diff --git a/src/main/python/main/ayab/knitprogress.py b/src/main/python/main/ayab/knitprogress.py index 70b9507c..793829a1 100644 --- a/src/main/python/main/ayab/knitprogress.py +++ b/src/main/python/main/ayab/knitprogress.py @@ -36,6 +36,8 @@ from .engine.status import Status +# TODO (TP): display memo information + class KnitProgress(QTableWidget): """ Class for the knit progress window, implemented as a subclass of `QScrollArea`. @@ -44,8 +46,10 @@ class KnitProgress(QTableWidget): @date June 2020 """ - green = 0xBBCCBB - orange = 0xEECC99 + green = 0x99DD99 + orange = 0xEECC77 + grey = 0xCCCCCC + smoke = 0xF5F5F5 def __init__(self, parent: GuiMain): super().__init__(parent.ui.graphics_splitter) @@ -87,6 +91,7 @@ def uiStateChanged(self, status: Status) -> bool: if ( status.line_number != self.previousStatus.line_number + or status.memo != self.previousStatus.memo or status.current_row != self.previousStatus.current_row or status.color_symbol != self.previousStatus.color_symbol or status.carriage_type != self.previousStatus.carriage_type @@ -124,8 +129,6 @@ def update_progress(self, status: Status) -> None: if self.columnCount() != len(columns): self.setColumnCount(len(columns)) n_cols = len(columns) - if n_cols < 4: - self.hideColumn(5) self.instantiate_row_from_columns(status, columns) self.previousStatus = status @@ -136,6 +139,7 @@ def update_progress(self, status: Status) -> None: def load_columns_from_status( self, status: Status, columns: List[QTableWidgetItem] ) -> None: + columns.append(self.__memo(status.memo)) for c in range(0, len(status.bits)): needle = status.knit_start_needle + c needle_number_from_r1 = needle - status.machine_width // 2 @@ -168,17 +172,19 @@ def instantiate_row_from_columns( self.horizontalHeader().setVisible(True) needle = status.knit_start_needle + i needle_number_from_r1 = needle - status.machine_width // 2 - if needle_number_from_r1 < 0: + if i == 0: + header = QTableWidgetItem("M") + header.setForeground(QBrush(QColor(f"#{self.grey:06x}"))) + elif needle_number_from_r1 < 0: header = QTableWidgetItem(f"{-needle_number_from_r1}") header.font().setBold(True) header.setForeground(QBrush(QColor(f"#{self.orange:06x}"))) header.setTextAlignment(Qt.AlignmentFlag.AlignCenter) - self.setHorizontalHeaderItem(i, header) else: header = QTableWidgetItem(f"{1 + needle_number_from_r1}") header.setForeground(QBrush(QColor(f"#{self.green:06x}"))) header.setTextAlignment(Qt.AlignmentFlag.AlignCenter) - self.setHorizontalHeaderItem(i, header) + self.setHorizontalHeaderItem(i, header) def make_row_with_spacer(self) -> None: self.removeRow(1) @@ -231,6 +237,7 @@ def format_row_header_text(self, status: Optional[Status]) -> QTableWidgetItem: carriage = status.carriage_type direction = status.carriage_direction info_text = info_text + (" " + carriage.symbol + " " + direction.symbol) + info_header.setText(info_text) return info_header @@ -265,6 +272,17 @@ def __stitch( stitch.setBackground(QBrush(bg_color)) return stitch + def __memo( + self, + memo: int, + ) -> QTableWidgetItem: + cell = QTableWidgetItem() + cell.setBackground(QBrush(QColor(f"#{self.smoke:06x}"))) + if memo > 0: + cell.setText(str(memo)) + return cell + + def onStitchSelect(self, current: QTableWidgetItem | None) -> None: if current is None: self.__progbar.set_selection_label("") diff --git a/src/main/python/main/ayab/pattern_import.py b/src/main/python/main/ayab/pattern_import.py index 59d8c313..772d4b9f 100644 --- a/src/main/python/main/ayab/pattern_import.py +++ b/src/main/python/main/ayab/pattern_import.py @@ -5,6 +5,8 @@ # .cut and .pal file formats # documented at https://www.fileformat.info/format/drhalo/egff.htm +# TODO (TP): extract memo information + from __future__ import annotations import sys from typing import Optional, cast From 542ee010324c02189cddbc2fd102f6b03f00936c Mon Sep 17 00:00:00 2001 From: Tom Price Date: Sat, 29 Mar 2025 04:30:00 -0400 Subject: [PATCH 02/25] Reverse image properly --- src/main/python/main/ayab/engine/control.py | 11 ++++++----- src/main/python/main/ayab/engine/engine.py | 13 +++++++------ src/main/python/main/ayab/engine/pattern.py | 6 +++--- src/main/python/main/ayab/image.py | 16 ++++++++++++---- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/main/python/main/ayab/engine/control.py b/src/main/python/main/ayab/engine/control.py index 101495d8..eff68e60 100644 --- a/src/main/python/main/ayab/engine/control.py +++ b/src/main/python/main/ayab/engine/control.py @@ -61,7 +61,7 @@ class Control(SignalSender): initial_position: int len_pat_expanded: int line_block: int - memo: list[int] + memos: list[int] mode: Mode mode_func: ModeFuncType num_colors: int @@ -86,7 +86,7 @@ def __init__(self, parent: GuiMain, engine: Engine): self.api_version: int = self.FIRST_SUPPORTED_API_VERSION def start( - self, pattern: Pattern, memo: list[int], options: OptionsTab, operation: Operation + self, pattern: Pattern, memos: list[int], options: OptionsTab, operation: Operation ) -> None: self.machine = options.machine if operation == Operation.KNIT: @@ -94,7 +94,7 @@ def start( self.line_block = 0 self.pattern_repeats = 0 self.pattern = pattern - self.memo = memo + self.memos = memos self.pat_height = pattern.pat_height self.num_colors = options.num_colors self.start_row = options.start_row @@ -235,7 +235,7 @@ def cnf_line_API6(self, line_number: int) -> bool: + str(self.pat_row) ) try: - msg = msg + " memo: " + str(self.memo[self.pat_row]) + msg = msg + " memo: " + str(self.memos[self.pat_row]) except: pass if blank_line: @@ -272,7 +272,7 @@ def __update_status(self, line_number: int, color: int, bits: bitarray) -> None: self.status.current_row = self.pat_row + 1 self.status.line_number = line_number try: - self.status.memo = self.memo[self.pat_row] + self.status.memo = self.memos[self.pat_row] except: self.status.memo = 0 if self.inf_repeat: @@ -291,6 +291,7 @@ def __update_status(self, line_number: int, color: int, bits: bitarray) -> None: self.status.carriage_direction = self.initial_direction else: self.status.carriage_direction = self.initial_direction.reverse() + self.logger.debug(str(self.memos)) def select_needles_API6( self, color: int, row_index: int, blank_line: bool diff --git a/src/main/python/main/ayab/engine/engine.py b/src/main/python/main/ayab/engine/engine.py index 5b7abe20..9a62afe2 100644 --- a/src/main/python/main/ayab/engine/engine.py +++ b/src/main/python/main/ayab/engine/engine.py @@ -52,7 +52,7 @@ class Engine(SignalSender, QDockWidget): port_opener = Signal() - memo: list[int] + memos: list[int] pattern: Pattern status: StatusTab @@ -69,7 +69,7 @@ def __init__(self, parent: GuiMain): parent.ui.dock_container_layout.addWidget(self) self.pattern: Pattern = None # type:ignore - self.memo = [] + self.memos = [] self.control = Control(parent, self) self.__feedback = FeedbackHandler(parent) self.__logger = logging.getLogger(type(self).__name__) @@ -129,12 +129,13 @@ def knit_config(self, im: AyabImage) -> None: self.__logger.debug(self.config.as_dict()) # start to knit with the bottom first - im.image = im.image.transpose(Image.FLIP_TOP_BOTTOM) + im_rev = im.clone() + im_rev.image = im.image.transpose(Image.FLIP_TOP_BOTTOM) # TODO: detect if previous conf had the same # image to avoid re-generating. - self.pattern = Pattern(im, self.config, self.config.num_colors) - self.memo = im.memo + self.pattern = Pattern(im_rev, self.config, self.config.num_colors) + self.memos = im.memos # validate configuration options valid, msg = self.validate() @@ -171,7 +172,7 @@ def run(self, operation: Operation) -> None: # setup knitting controller self.config.portname = self.__read_portname() - self.control.start(self.pattern, self.memo, self.config, operation) + self.control.start(self.pattern, self.memos, self.config, operation) with keep.presenting(on_fail="pass"): while True: diff --git a/src/main/python/main/ayab/engine/pattern.py b/src/main/python/main/ayab/engine/pattern.py index e542fb84..1ae61db7 100644 --- a/src/main/python/main/ayab/engine/pattern.py +++ b/src/main/python/main/ayab/engine/pattern.py @@ -35,7 +35,7 @@ def __init__(self, im: AyabImage, config: OptionsTab, num_colors: int = 2): self.__pattern : Image.Image = ( im.image.transpose(Image.FLIP_LEFT_RIGHT) if config.auto_mirror else im.image ) - self.__memo : list[int] = im.memo + self.__memos : list[int] = im.memos self.__num_colors : int = num_colors self.__alignment : Alignment = Alignment.CENTER self.__pat_start_needle: int = -1 @@ -201,8 +201,8 @@ def pattern_expanded(self) -> list[bitarray]: return self.__pattern_expanded @property - def memo(self) -> list[int]: - return self.__memo + def memos(self) -> list[int]: + return self.__memos def array2rgb(self, a: list[int]) -> int: return (a[0] & 0xFF) * 0x10000 + (a[1] & 0xFF) * 0x100 + (a[2] & 0xFF) diff --git a/src/main/python/main/ayab/image.py b/src/main/python/main/ayab/image.py index 2d0f51ef..7ac37d09 100644 --- a/src/main/python/main/ayab/image.py +++ b/src/main/python/main/ayab/image.py @@ -56,10 +56,18 @@ def __init__(self, parent: GuiMain): super().__init__(parent.signal_receiver) self.__parent = parent self.image: Image.Image = None # type: ignore - self.memo: list = [] + self.memos: list = [] self.filename: Optional[str] = None self.filename_input = self.__parent.ui.filename_lineedit + def clone(self) -> AyabImage: + im: AyabImage = AyabImage(self.__parent) + im.image = self.image + im.memos = self.memos + im.filename = self.filename + im.filename_input = self.filename_input + return im + def select_file(self) -> None: filename = self.filename_input.text() if filename == "": @@ -120,12 +128,12 @@ def __open(self, filename: str) -> None: comment = str(self.image.info["Comment"]) if comment.startswith("AYAB:"): # update memo information - self.memo = [] + self.memos = [] for i in range(len(comment) - 5): - self.memo.append(int(comment[i + 5])) + self.memos.append(int(comment[i + 5])) # report metadata logging.info("File metadata Comment tag: " + comment) - logging.info("File memo information: " + str(self.memo)) + logging.info("File memo information: " + str(self.memos)) self.image = self.image.convert("RGBA") self.emit_got_image_flag() self.emit_image_resizer() From a1531f16ed0de09457c990ff23f5387c4fd076ab Mon Sep 17 00:00:00 2001 From: Tom Price Date: Sat, 29 Mar 2025 04:45:13 -0400 Subject: [PATCH 03/25] Fix tests --- src/main/python/main/ayab/engine/engine.py | 5 ++--- src/main/python/main/ayab/engine/pattern.py | 7 +++---- src/main/python/main/ayab/image.py | 8 -------- src/main/python/main/ayab/tests/test_control.py | 14 +++++++------- 4 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/main/python/main/ayab/engine/engine.py b/src/main/python/main/ayab/engine/engine.py index 9a62afe2..5573c251 100644 --- a/src/main/python/main/ayab/engine/engine.py +++ b/src/main/python/main/ayab/engine/engine.py @@ -129,12 +129,11 @@ def knit_config(self, im: AyabImage) -> None: self.__logger.debug(self.config.as_dict()) # start to knit with the bottom first - im_rev = im.clone() - im_rev.image = im.image.transpose(Image.FLIP_TOP_BOTTOM) + image_rev: Image.Image = im.image.transpose(Image.FLIP_TOP_BOTTOM) # TODO: detect if previous conf had the same # image to avoid re-generating. - self.pattern = Pattern(im_rev, self.config, self.config.num_colors) + self.pattern = Pattern(image_rev, im.memos, self.config, self.config.num_colors) self.memos = im.memos # validate configuration options diff --git a/src/main/python/main/ayab/engine/pattern.py b/src/main/python/main/ayab/engine/pattern.py index 1ae61db7..6e9dce0f 100644 --- a/src/main/python/main/ayab/engine/pattern.py +++ b/src/main/python/main/ayab/engine/pattern.py @@ -26,16 +26,15 @@ from PIL import Image from .options import Alignment, OptionsTab from .mode import Mode -from ..image import AyabImage from ..machine import Machine class Pattern(object): - def __init__(self, im: AyabImage, config: OptionsTab, num_colors: int = 2): + def __init__(self, image: Image.Image, memos: list[int], config: OptionsTab, num_colors: int = 2): self.__pattern : Image.Image = ( - im.image.transpose(Image.FLIP_LEFT_RIGHT) if config.auto_mirror else im.image + image.transpose(Image.FLIP_LEFT_RIGHT) if config.auto_mirror else image ) - self.__memos : list[int] = im.memos + self.__memos : list[int] = memos self.__num_colors : int = num_colors self.__alignment : Alignment = Alignment.CENTER self.__pat_start_needle: int = -1 diff --git a/src/main/python/main/ayab/image.py b/src/main/python/main/ayab/image.py index 7ac37d09..23a09d48 100644 --- a/src/main/python/main/ayab/image.py +++ b/src/main/python/main/ayab/image.py @@ -60,14 +60,6 @@ def __init__(self, parent: GuiMain): self.filename: Optional[str] = None self.filename_input = self.__parent.ui.filename_lineedit - def clone(self) -> AyabImage: - im: AyabImage = AyabImage(self.__parent) - im.image = self.image - im.memos = self.memos - im.filename = self.filename - im.filename_input = self.filename_input - return im - def select_file(self) -> None: filename = self.filename_input.text() if filename == "": diff --git a/src/main/python/main/ayab/tests/test_control.py b/src/main/python/main/ayab/tests/test_control.py index 379b5ad3..0ec0b1c1 100644 --- a/src/main/python/main/ayab/tests/test_control.py +++ b/src/main/python/main/ayab/tests/test_control.py @@ -55,7 +55,7 @@ def setUp(self): def test__singlebed(self): control = Control(self.parent, self.parent.engine) - control.pattern = Pattern(Image.new("P", (1, 3)), Config(Machine(0)), 2) + control.pattern = Pattern(Image.new("P", (1, 3)), [], Config(Machine(0)), 2) control.num_colors = 2 control.start_row = 0 control.inf_repeat = False @@ -73,7 +73,7 @@ def test__singlebed(self): def test__classic_ribber_2col(self): control = Control(self.parent, self.parent.engine) control.pattern = Pattern( - Image.new("P", (1, 5)), Config(Machine(0), Mode.CLASSIC_RIBBER), 2 + Image.new("P", (1, 5)), [], Config(Machine(0), Mode.CLASSIC_RIBBER), 2 ) control.num_colors = 2 control.start_row = 0 @@ -102,7 +102,7 @@ def test__classic_ribber_2col(self): def test__classic_ribber_multicol(self): control = Control(self.parent, self.parent.engine) control.pattern = Pattern( - Image.new("P", (1, 3)), Config(Machine(0), Mode.CLASSIC_RIBBER), 3 + Image.new("P", (1, 3)), [], Config(Machine(0), Mode.CLASSIC_RIBBER), 3 ) control.num_colors = 3 control.start_row = 0 @@ -136,7 +136,7 @@ def test__classic_ribber_multicol(self): def test__middlecolorstwice_ribber(self): control = Control(self.parent, self.parent.engine) control.pattern = Pattern( - Image.new("P", (1, 5)), Config(Machine(0), Mode.MIDDLECOLORSTWICE_RIBBER), 3 + Image.new("P", (1, 5)), [], Config(Machine(0), Mode.MIDDLECOLORSTWICE_RIBBER), 3 ) control.mode = Mode.MIDDLECOLORSTWICE_RIBBER control.num_colors = 3 @@ -178,7 +178,7 @@ def test__middlecolorstwice_ribber(self): def test__heartofpluto_ribber(self): control = Control(self.parent, self.parent.engine) control.pattern = Pattern( - Image.new("P", (1, 5)), Config(Machine(0), Mode.HEARTOFPLUTO_RIBBER), 3 + Image.new("P", (1, 5)), [], Config(Machine(0), Mode.HEARTOFPLUTO_RIBBER), 3 ) control.mode = Mode.HEARTOFPLUTO_RIBBER control.num_colors = 3 @@ -220,7 +220,7 @@ def test__heartofpluto_ribber(self): def test__circular_ribber(self): control = Control(self.parent, self.parent.engine) control.pattern = Pattern( - Image.new("P", (1, 3)), Config(Machine(0), Mode.CIRCULAR_RIBBER), 3 + Image.new("P", (1, 3)), [], Config(Machine(0), Mode.CIRCULAR_RIBBER), 3 ) control.num_colors = 3 control.start_row = 0 @@ -262,7 +262,7 @@ def test_select_needles_API6(self): im = Image.new("P", (40, 3), 1) im1 = Image.new("P", (40, 1), 0) im.paste(im1, (0, 0)) - pattern = Pattern(im, Config(Machine(0), Mode.SINGLEBED), 2) + pattern = Pattern(im, [], Config(Machine(0), Mode.SINGLEBED), 2) pattern.alignment = Alignment.LEFT assert pattern.pat_start_needle == 0 assert pattern.pat_end_needle == 40 From 02fbb4ddbff3f3babbff4376ac44031d94042d9e Mon Sep 17 00:00:00 2001 From: Tom Price Date: Sat, 29 Mar 2025 04:53:28 -0400 Subject: [PATCH 04/25] Use default memo value --- src/main/python/main/ayab/image.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/python/main/ayab/image.py b/src/main/python/main/ayab/image.py index 23a09d48..f0c785b8 100644 --- a/src/main/python/main/ayab/image.py +++ b/src/main/python/main/ayab/image.py @@ -56,7 +56,7 @@ def __init__(self, parent: GuiMain): super().__init__(parent.signal_receiver) self.__parent = parent self.image: Image.Image = None # type: ignore - self.memos: list = [] + self.memos: list[int] = [] self.filename: Optional[str] = None self.filename_input = self.__parent.ui.filename_lineedit @@ -122,7 +122,10 @@ def __open(self, filename: str) -> None: # update memo information self.memos = [] for i in range(len(comment) - 5): - self.memos.append(int(comment[i + 5])) + try: + self.memos.append(int(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)) From b3d74f4f11a49b2e092bae8f05d86e982b04b0e9 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Sat, 29 Mar 2025 17:31:09 -0400 Subject: [PATCH 05/25] Remove memo column from knit progress window when no memo information --- src/main/python/main/ayab/engine/control.py | 1 - src/main/python/main/ayab/image.py | 2 +- src/main/python/main/ayab/knitprogress.py | 6 ++++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/python/main/ayab/engine/control.py b/src/main/python/main/ayab/engine/control.py index eff68e60..79250396 100644 --- a/src/main/python/main/ayab/engine/control.py +++ b/src/main/python/main/ayab/engine/control.py @@ -291,7 +291,6 @@ def __update_status(self, line_number: int, color: int, bits: bitarray) -> None: self.status.carriage_direction = self.initial_direction else: self.status.carriage_direction = self.initial_direction.reverse() - self.logger.debug(str(self.memos)) def select_needles_API6( self, color: int, row_index: int, blank_line: bool diff --git a/src/main/python/main/ayab/image.py b/src/main/python/main/ayab/image.py index f0c785b8..f26c36d7 100644 --- a/src/main/python/main/ayab/image.py +++ b/src/main/python/main/ayab/image.py @@ -103,6 +103,7 @@ def __load(self, filename: str) -> None: self.__parent.engine.config.refresh() def __open(self, filename: str) -> None: + self.memos = [] # check for files that need conversion suffix = filename[-4:].lower() if suffix == ".pat": @@ -120,7 +121,6 @@ def __open(self, filename: str) -> None: 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])) diff --git a/src/main/python/main/ayab/knitprogress.py b/src/main/python/main/ayab/knitprogress.py index 793829a1..8453220e 100644 --- a/src/main/python/main/ayab/knitprogress.py +++ b/src/main/python/main/ayab/knitprogress.py @@ -81,6 +81,7 @@ def start(self) -> None: self.setRowCount(0) self.setCurrentCell(-1, -1) self.color = True + self.memo_column = len(self.scene.ayabimage.memos) > 0 def uiStateChanged(self, status: Status) -> bool: if not self.previousStatus: @@ -139,7 +140,8 @@ def update_progress(self, status: Status) -> None: def load_columns_from_status( self, status: Status, columns: List[QTableWidgetItem] ) -> None: - columns.append(self.__memo(status.memo)) + if self.memo_column: + columns.append(self.__memo(status.memo)) for c in range(0, len(status.bits)): needle = status.knit_start_needle + c needle_number_from_r1 = needle - status.machine_width // 2 @@ -172,7 +174,7 @@ def instantiate_row_from_columns( self.horizontalHeader().setVisible(True) needle = status.knit_start_needle + i needle_number_from_r1 = needle - status.machine_width // 2 - if i == 0: + if self.memo_column and i == 0: header = QTableWidgetItem("M") header.setForeground(QBrush(QColor(f"#{self.grey:06x}"))) elif needle_number_from_r1 < 0: From e4d8271799854b8b43612a07e3680b51043e7aea Mon Sep 17 00:00:00 2001 From: Tom Price Date: Sat, 29 Mar 2025 17:37:54 -0400 Subject: [PATCH 06/25] Rename variable --- src/main/python/main/ayab/knitprogress.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/python/main/ayab/knitprogress.py b/src/main/python/main/ayab/knitprogress.py index 8453220e..8af00994 100644 --- a/src/main/python/main/ayab/knitprogress.py +++ b/src/main/python/main/ayab/knitprogress.py @@ -81,7 +81,7 @@ def start(self) -> None: self.setRowCount(0) self.setCurrentCell(-1, -1) self.color = True - self.memo_column = len(self.scene.ayabimage.memos) > 0 + self.show_memo_column = len(self.scene.ayabimage.memos) > 0 def uiStateChanged(self, status: Status) -> bool: if not self.previousStatus: @@ -140,7 +140,7 @@ def update_progress(self, status: Status) -> None: def load_columns_from_status( self, status: Status, columns: List[QTableWidgetItem] ) -> None: - if self.memo_column: + if self.show_memo_column: columns.append(self.__memo(status.memo)) for c in range(0, len(status.bits)): needle = status.knit_start_needle + c @@ -174,7 +174,7 @@ def instantiate_row_from_columns( self.horizontalHeader().setVisible(True) needle = status.knit_start_needle + i needle_number_from_r1 = needle - status.machine_width // 2 - if self.memo_column and i == 0: + if self.show_memo_column and i == 0: header = QTableWidgetItem("M") header.setForeground(QBrush(QColor(f"#{self.grey:06x}"))) elif needle_number_from_r1 < 0: From a86dc9e2d41de390bc06abf0e16368b7a407eb15 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Sat, 29 Mar 2025 17:45:38 -0400 Subject: [PATCH 07/25] fix needle numbers in knitting progress window when memo information column is shown --- src/main/python/main/ayab/knitprogress.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/python/main/ayab/knitprogress.py b/src/main/python/main/ayab/knitprogress.py index 8af00994..de2c2199 100644 --- a/src/main/python/main/ayab/knitprogress.py +++ b/src/main/python/main/ayab/knitprogress.py @@ -172,7 +172,7 @@ def instantiate_row_from_columns( self.horizontalHeader().setVisible(False) else: self.horizontalHeader().setVisible(True) - needle = status.knit_start_needle + i + needle = status.knit_start_needle + i - self.show_memo_column needle_number_from_r1 = needle - status.machine_width // 2 if self.show_memo_column and i == 0: header = QTableWidgetItem("M") From c0b4c96613899745473956428a10503f429892b4 Mon Sep 17 00:00:00 2001 From: Jonathan Perret Date: Mon, 31 Mar 2025 13:16:36 +0200 Subject: [PATCH 08/25] Update patterns submodule to include WIP converted patterns --- src/main/resources/base/patterns | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/base/patterns b/src/main/resources/base/patterns index adc7f9a3..48e15765 160000 --- a/src/main/resources/base/patterns +++ b/src/main/resources/base/patterns @@ -1 +1 @@ -Subproject commit adc7f9a3468915efff03053bd88f636cdb7de295 +Subproject commit 48e157656121dd2a6485e5024873f296a0ddcad8 From aea8e89b57c67a598b6a64d57591e41563d1ed0e Mon Sep 17 00:00:00 2001 From: Tom Price Date: Mon, 31 Mar 2025 14:31:45 -0400 Subject: [PATCH 09/25] Allow non-digit memo characters to be displayed --- src/main/python/main/ayab/image.py | 4 ++-- src/main/python/main/ayab/knitprogress.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/python/main/ayab/image.py b/src/main/python/main/ayab/image.py index f26c36d7..34881e36 100644 --- a/src/main/python/main/ayab/image.py +++ b/src/main/python/main/ayab/image.py @@ -123,9 +123,9 @@ def __open(self, filename: str) -> None: # update memo information for i in range(len(comment) - 5): try: - self.memos.append(int(comment[i + 5])) + self.memos.append(comment[i + 5]) except: - self.memos.append(0) + self.memos.append('0') # report metadata logging.info("File metadata Comment tag: " + comment) logging.info("File memo information: " + str(self.memos)) diff --git a/src/main/python/main/ayab/knitprogress.py b/src/main/python/main/ayab/knitprogress.py index de2c2199..58f04175 100644 --- a/src/main/python/main/ayab/knitprogress.py +++ b/src/main/python/main/ayab/knitprogress.py @@ -280,8 +280,8 @@ def __memo( ) -> QTableWidgetItem: cell = QTableWidgetItem() cell.setBackground(QBrush(QColor(f"#{self.smoke:06x}"))) - if memo > 0: - cell.setText(str(memo)) + if len(memo) > 0 and memo != "0": + cell.setText(memo) return cell From 2836dde6ab723cc04014fe7fd50b63682df3bd0f Mon Sep 17 00:00:00 2001 From: Tom Price Date: Mon, 31 Mar 2025 14:41:49 -0400 Subject: [PATCH 10/25] Fix type of --- src/main/python/main/ayab/engine/control.py | 2 +- src/main/python/main/ayab/engine/engine.py | 2 +- src/main/python/main/ayab/engine/pattern.py | 2 +- src/main/python/main/ayab/engine/status.py | 2 +- src/main/python/main/ayab/image.py | 2 +- src/main/resources/base/patterns | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/python/main/ayab/engine/control.py b/src/main/python/main/ayab/engine/control.py index 79250396..11f4a3bc 100644 --- a/src/main/python/main/ayab/engine/control.py +++ b/src/main/python/main/ayab/engine/control.py @@ -61,7 +61,7 @@ class Control(SignalSender): initial_position: int len_pat_expanded: int line_block: int - memos: list[int] + memos: list[str] mode: Mode mode_func: ModeFuncType num_colors: int diff --git a/src/main/python/main/ayab/engine/engine.py b/src/main/python/main/ayab/engine/engine.py index 5573c251..f31629ed 100644 --- a/src/main/python/main/ayab/engine/engine.py +++ b/src/main/python/main/ayab/engine/engine.py @@ -52,7 +52,7 @@ class Engine(SignalSender, QDockWidget): port_opener = Signal() - memos: list[int] + memos: list[str] pattern: Pattern status: StatusTab diff --git a/src/main/python/main/ayab/engine/pattern.py b/src/main/python/main/ayab/engine/pattern.py index 6e9dce0f..34360c9a 100644 --- a/src/main/python/main/ayab/engine/pattern.py +++ b/src/main/python/main/ayab/engine/pattern.py @@ -34,7 +34,7 @@ def __init__(self, image: Image.Image, memos: list[int], config: OptionsTab, num self.__pattern : Image.Image = ( image.transpose(Image.FLIP_LEFT_RIGHT) if config.auto_mirror else image ) - self.__memos : list[int] = memos + self.__memos : list[str] = memos self.__num_colors : int = num_colors self.__alignment : Alignment = Alignment.CENTER self.__pat_start_needle: int = -1 diff --git a/src/main/python/main/ayab/engine/status.py b/src/main/python/main/ayab/engine/status.py index 9cb94c41..6945871b 100644 --- a/src/main/python/main/ayab/engine/status.py +++ b/src/main/python/main/ayab/engine/status.py @@ -119,7 +119,7 @@ class Status(object): current_row: int firmware_state: int line_number: int - memo: int + memo: str repeats: int total_rows: int # carriage info diff --git a/src/main/python/main/ayab/image.py b/src/main/python/main/ayab/image.py index 34881e36..150dc2b0 100644 --- a/src/main/python/main/ayab/image.py +++ b/src/main/python/main/ayab/image.py @@ -56,7 +56,7 @@ def __init__(self, parent: GuiMain): super().__init__(parent.signal_receiver) self.__parent = parent self.image: Image.Image = None # type: ignore - self.memos: list[int] = [] + self.memos: list[str] = [] self.filename: Optional[str] = None self.filename_input = self.__parent.ui.filename_lineedit diff --git a/src/main/resources/base/patterns b/src/main/resources/base/patterns index 48e15765..adc7f9a3 160000 --- a/src/main/resources/base/patterns +++ b/src/main/resources/base/patterns @@ -1 +1 @@ -Subproject commit 48e157656121dd2a6485e5024873f296a0ddcad8 +Subproject commit adc7f9a3468915efff03053bd88f636cdb7de295 From b6f55379fb571173dd2395bbe1f77afca1cbdcd7 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Mon, 31 Mar 2025 14:47:58 -0400 Subject: [PATCH 11/25] Fix type of memo --- src/main/python/main/ayab/engine/status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/python/main/ayab/engine/status.py b/src/main/python/main/ayab/engine/status.py index 6945871b..ccab7488 100644 --- a/src/main/python/main/ayab/engine/status.py +++ b/src/main/python/main/ayab/engine/status.py @@ -146,7 +146,7 @@ def reset(self) -> None: self.current_row = -1 self.firmware_state = -1 self.line_number = -1 - self.memo = 0 + self.memo = '0' self.repeats = -1 self.total_rows = -1 # carriage info From 3b7c243157ab8369256adfa308b49298d00b0036 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Mon, 31 Mar 2025 14:51:46 -0400 Subject: [PATCH 12/25] Fix type of memos --- src/main/python/main/ayab/engine/pattern.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/python/main/ayab/engine/pattern.py b/src/main/python/main/ayab/engine/pattern.py index 34360c9a..823cf24c 100644 --- a/src/main/python/main/ayab/engine/pattern.py +++ b/src/main/python/main/ayab/engine/pattern.py @@ -30,7 +30,7 @@ class Pattern(object): - def __init__(self, image: Image.Image, memos: list[int], config: OptionsTab, num_colors: int = 2): + def __init__(self, image: Image.Image, memos: list[str], config: OptionsTab, num_colors: int = 2): self.__pattern : Image.Image = ( image.transpose(Image.FLIP_LEFT_RIGHT) if config.auto_mirror else image ) From 8793fededd6ea7214dd34d4313abac97a3eab9bd Mon Sep 17 00:00:00 2001 From: Tom Price Date: Mon, 31 Mar 2025 14:55:08 -0400 Subject: [PATCH 13/25] Fix type of memos --- src/main/python/main/ayab/engine/control.py | 2 +- src/main/python/main/ayab/engine/pattern.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/python/main/ayab/engine/control.py b/src/main/python/main/ayab/engine/control.py index 11f4a3bc..71f34b7c 100644 --- a/src/main/python/main/ayab/engine/control.py +++ b/src/main/python/main/ayab/engine/control.py @@ -86,7 +86,7 @@ def __init__(self, parent: GuiMain, engine: Engine): self.api_version: int = self.FIRST_SUPPORTED_API_VERSION def start( - self, pattern: Pattern, memos: list[int], options: OptionsTab, operation: Operation + self, pattern: Pattern, memos: list[str], options: OptionsTab, operation: Operation ) -> None: self.machine = options.machine if operation == Operation.KNIT: diff --git a/src/main/python/main/ayab/engine/pattern.py b/src/main/python/main/ayab/engine/pattern.py index 823cf24c..301d4f54 100644 --- a/src/main/python/main/ayab/engine/pattern.py +++ b/src/main/python/main/ayab/engine/pattern.py @@ -200,7 +200,7 @@ def pattern_expanded(self) -> list[bitarray]: return self.__pattern_expanded @property - def memos(self) -> list[int]: + def memos(self) -> list[str]: return self.__memos def array2rgb(self, a: list[int]) -> int: From 9abe187da6cb7c48d61a74de4148e07faa7a0ab3 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Mon, 31 Mar 2025 15:03:36 -0400 Subject: [PATCH 14/25] Fix type of memo --- src/main/python/main/ayab/knitprogress.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/python/main/ayab/knitprogress.py b/src/main/python/main/ayab/knitprogress.py index 58f04175..f32fcb3f 100644 --- a/src/main/python/main/ayab/knitprogress.py +++ b/src/main/python/main/ayab/knitprogress.py @@ -276,11 +276,11 @@ def __stitch( def __memo( self, - memo: int, + memo: str, ) -> QTableWidgetItem: cell = QTableWidgetItem() cell.setBackground(QBrush(QColor(f"#{self.smoke:06x}"))) - if len(memo) > 0 and memo != "0": + if len(memo) > 0 and memo != "0": cell.setText(memo) return cell From 7822017f34a90cda5b82893cdd84637573c32b40 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Mon, 31 Mar 2025 15:28:20 -0400 Subject: [PATCH 15/25] Update control.py --- src/main/python/main/ayab/engine/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/python/main/ayab/engine/control.py b/src/main/python/main/ayab/engine/control.py index 71f34b7c..57551621 100644 --- a/src/main/python/main/ayab/engine/control.py +++ b/src/main/python/main/ayab/engine/control.py @@ -274,7 +274,7 @@ def __update_status(self, line_number: int, color: int, bits: bitarray) -> None: try: self.status.memo = self.memos[self.pat_row] except: - self.status.memo = 0 + self.status.memo = "0" if self.inf_repeat: self.status.repeats = self.pattern_repeats if self.mode != Mode.SINGLEBED: From 764d261c9d99f7845cb3d3e93c8c0618cc95ca3d Mon Sep 17 00:00:00 2001 From: Jonathan Perret Date: Tue, 1 Apr 2025 00:25:06 +0200 Subject: [PATCH 16/25] Update patterns submodule to include WIP converted patterns --- src/main/resources/base/patterns | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/base/patterns b/src/main/resources/base/patterns index adc7f9a3..55d2f190 160000 --- a/src/main/resources/base/patterns +++ b/src/main/resources/base/patterns @@ -1 +1 @@ -Subproject commit adc7f9a3468915efff03053bd88f636cdb7de295 +Subproject commit 55d2f190a4b95b4d3d93491e1e9b06194424bbfb From 2340df060fe8c82b19c88537848f41b1d9c83cb8 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Mon, 31 Mar 2025 18:46:47 -0400 Subject: [PATCH 17/25] Fix fragile selection side identification in knit progress window --- src/main/python/main/ayab/knitprogress.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/python/main/ayab/knitprogress.py b/src/main/python/main/ayab/knitprogress.py index f32fcb3f..471102eb 100644 --- a/src/main/python/main/ayab/knitprogress.py +++ b/src/main/python/main/ayab/knitprogress.py @@ -74,6 +74,10 @@ def __init__(self, parent: GuiMain): self.previousStatus: Optional[Status] = None self.scene = parent.scene self.currentItemChanged.connect(self.onStitchSelect) + self.qcolor_green: QColor = QColor(f"#{self.green:06x}") + self.qcolor_orange: QColor = QColor(f"#{self.orange:06x}") + self.qcolor_grey: QColor = QColor(f"#{self.grey:06x}") + self.qcolor_smoke: QColor = QColor(f"#{self.smoke:06x}") def start(self) -> None: self.clearContents() @@ -176,15 +180,15 @@ def instantiate_row_from_columns( needle_number_from_r1 = needle - status.machine_width // 2 if self.show_memo_column and i == 0: header = QTableWidgetItem("M") - header.setForeground(QBrush(QColor(f"#{self.grey:06x}"))) + header.setForeground(QBrush(self.qcolor_grey)) elif needle_number_from_r1 < 0: header = QTableWidgetItem(f"{-needle_number_from_r1}") header.font().setBold(True) - header.setForeground(QBrush(QColor(f"#{self.orange:06x}"))) + header.setForeground(QBrush(self.qcolor_orange)) header.setTextAlignment(Qt.AlignmentFlag.AlignCenter) else: header = QTableWidgetItem(f"{1 + needle_number_from_r1}") - header.setForeground(QBrush(QColor(f"#{self.green:06x}"))) + header.setForeground(QBrush(self.qcolor_green)) header.setTextAlignment(Qt.AlignmentFlag.AlignCenter) self.setHorizontalHeaderItem(i, header) @@ -279,7 +283,7 @@ def __memo( memo: str, ) -> QTableWidgetItem: cell = QTableWidgetItem() - cell.setBackground(QBrush(QColor(f"#{self.smoke:06x}"))) + cell.setBackground(QBrush(self.qcolor_smoke)) if len(memo) > 0 and memo != "0": cell.setText(memo) return cell @@ -290,7 +294,7 @@ def onStitchSelect(self, current: QTableWidgetItem | None) -> None: self.__progbar.set_selection_label("") return header = self.horizontalHeaderItem(current.column()) - if header is not None and header.foreground().color().red() == 187: + if header is not None and header.foreground().color() == self.qcolor_green: side = "Right" else: side = "Left" From 80d09cbcca83cab10e1366da17b8f63d291f49db Mon Sep 17 00:00:00 2001 From: Tom Price Date: Mon, 31 Mar 2025 19:04:07 -0400 Subject: [PATCH 18/25] Rename class Transform to class ImageTransform --- src/main/python/main/ayab/ayab.py | 6 +++--- src/main/python/main/ayab/image.py | 18 +++++++++--------- src/main/python/main/ayab/transforms.py | 10 +++++----- src/main/resources/base/patterns | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/python/main/ayab/ayab.py b/src/main/python/main/ayab/ayab.py index 9fd424ee..32643873 100644 --- a/src/main/python/main/ayab/ayab.py +++ b/src/main/python/main/ayab/ayab.py @@ -32,7 +32,7 @@ from .audio import AudioPlayer from .menu import Menu from .scene import Scene -from .transforms import Transform +from .transforms import ImageTransform from .firmware_flash import FirmwareFlash from .hw_test import HardwareTestDialog from .preferences import Preferences @@ -117,8 +117,8 @@ def __activate_menu(self) -> None: self.menu.ui.action_cancel.triggered.connect(self.engine.cancel) self.menu.ui.action_set_preferences.triggered.connect(self.__set_prefs) self.menu.ui.action_about.triggered.connect(self.about.show) - # get names of image actions from Transform methods - transforms = filter(lambda x: x[0] != "_", Transform.__dict__.keys()) + # get names of image actions from ImageTransform methods + transforms = filter(lambda x: x[0] != "_", ImageTransform.__dict__.keys()) for t in transforms: action = getattr(self.menu.ui, "action_" + t) slot = getattr(self.scene.ayabimage, t) diff --git a/src/main/python/main/ayab/image.py b/src/main/python/main/ayab/image.py index 150dc2b0..60cdf757 100644 --- a/src/main/python/main/ayab/image.py +++ b/src/main/python/main/ayab/image.py @@ -27,7 +27,7 @@ from PySide6.QtCore import QCoreApplication from PySide6.QtWidgets import QInputDialog, QDialog, QFileDialog -from .transforms import Transform, Mirrors +from .transforms import ImageTransform, Mirrors from .signal_sender import SignalSender from .utils import display_blocking_popup from .machine import Machine @@ -134,7 +134,7 @@ def __open(self, filename: str) -> None: self.emit_image_resizer() def invert(self) -> None: - self.apply_transform(Transform.invert) + self.apply_transform(ImageTransform.invert) def repeat(self) -> None: machine_width = Machine(self.__parent.prefs.value("machine")).width @@ -149,7 +149,7 @@ def repeat(self) -> None: minValue=1, maxValue=ceil(machine_width / self.image.width), ) - self.apply_transform(Transform.repeat, v[0], h[0]) + self.apply_transform(ImageTransform.repeat, v[0], h[0]) def stretch(self) -> None: machine_width = Machine(self.__parent.prefs.value("machine")).width @@ -164,24 +164,24 @@ def stretch(self) -> None: minValue=1, maxValue=ceil(machine_width / self.image.width), ) - self.apply_transform(Transform.stretch, v[0], h[0]) + self.apply_transform(ImageTransform.stretch, v[0], h[0]) def reflect(self) -> None: m = Mirrors() if m.result == QDialog.DialogCode.Accepted: - self.apply_transform(Transform.reflect, m.mirrors) + self.apply_transform(ImageTransform.reflect, m.mirrors) def hflip(self) -> None: - self.apply_transform(Transform.hflip) + self.apply_transform(ImageTransform.hflip) def vflip(self) -> None: - self.apply_transform(Transform.vflip) + self.apply_transform(ImageTransform.vflip) def rotate_left(self) -> None: - self.apply_transform(Transform.rotate_left) + self.apply_transform(ImageTransform.rotate_left) def rotate_right(self) -> None: - self.apply_transform(Transform.rotate_right) + self.apply_transform(ImageTransform.rotate_right) def zoom_in(self) -> None: self.__parent.scene.set_zoom(+1) diff --git a/src/main/python/main/ayab/transforms.py b/src/main/python/main/ayab/transforms.py index ecb787c3..e5d97772 100644 --- a/src/main/python/main/ayab/transforms.py +++ b/src/main/python/main/ayab/transforms.py @@ -26,7 +26,7 @@ from .mirrors_gui import Ui_Mirrors -class Transform(Image.Image): +class ImageTransform(Image.Image): """ Image transforms for AYAB GUI called by `AyabImage.apply_transform()` @@ -96,14 +96,14 @@ def reflect( h1 = 1 + h0 + h_ if w1 > 1: im = image - image = Transform.hflip(image) - image = Transform.repeat(image, (1, w1)) + image = ImageTransform.hflip(image) + image = ImageTransform.repeat(image, (1, w1)) for i in range(w0, w1, 2): image.paste(im, (i * w, 0)) if h1 > 1: im = image - image = Transform.vflip(image) - image = Transform.repeat(image, (h1, 1)) + image = ImageTransform.vflip(image) + image = ImageTransform.repeat(image, (h1, 1)) for i in range(h0, h1, 2): image.paste(im, (0, i * h)) return image diff --git a/src/main/resources/base/patterns b/src/main/resources/base/patterns index 55d2f190..adc7f9a3 160000 --- a/src/main/resources/base/patterns +++ b/src/main/resources/base/patterns @@ -1 +1 @@ -Subproject commit 55d2f190a4b95b4d3d93491e1e9b06194424bbfb +Subproject commit adc7f9a3468915efff03053bd88f636cdb7de295 From 12b877cd27ab91ee9c9b7c620fc4cf6135239369 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Mon, 31 Mar 2025 19:26:58 -0400 Subject: [PATCH 19/25] Apply image transforms to Memo data --- src/main/python/main/ayab/image.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/python/main/ayab/image.py b/src/main/python/main/ayab/image.py index 60cdf757..a60916c4 100644 --- a/src/main/python/main/ayab/image.py +++ b/src/main/python/main/ayab/image.py @@ -135,6 +135,7 @@ def __open(self, filename: str) -> None: def invert(self) -> None: self.apply_transform(ImageTransform.invert) + # memos unchanged def repeat(self) -> None: machine_width = Machine(self.__parent.prefs.value("machine")).width @@ -150,6 +151,7 @@ def repeat(self) -> None: maxValue=ceil(machine_width / self.image.width), ) self.apply_transform(ImageTransform.repeat, v[0], h[0]) + self.memos = self.memos * v[0] def stretch(self) -> None: machine_width = Machine(self.__parent.prefs.value("machine")).width @@ -165,23 +167,29 @@ def stretch(self) -> None: maxValue=ceil(machine_width / self.image.width), ) self.apply_transform(ImageTransform.stretch, v[0], h[0]) + self.memos = [] def reflect(self) -> None: m = Mirrors() if m.result == QDialog.DialogCode.Accepted: self.apply_transform(ImageTransform.reflect, m.mirrors) + self.memos = [] def hflip(self) -> None: self.apply_transform(ImageTransform.hflip) + # memos unchanged def vflip(self) -> None: self.apply_transform(ImageTransform.vflip) + self.memos = [] def rotate_left(self) -> None: self.apply_transform(ImageTransform.rotate_left) + self.memos = [] def rotate_right(self) -> None: self.apply_transform(ImageTransform.rotate_right) + self.memos = [] def zoom_in(self) -> None: self.__parent.scene.set_zoom(+1) @@ -196,10 +204,6 @@ def apply_transform( ) -> None: """Executes an image transform specified by function and args.""" self.image = transform(self.image, args) - try: - pass # self.image = transform(self.image, args) - except Exception as e: - logging.error("Error while executing image transform: " + repr(e)) # Update the view self.emit_image_resizer() From 14a22462989238e9dd3c5d83b480a0f2fe81163e Mon Sep 17 00:00:00 2001 From: Tom Price Date: Sun, 20 Apr 2025 07:39:54 -0400 Subject: [PATCH 20/25] Fix exception handling in memo extraction --- src/main/python/main/ayab/engine/control.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/python/main/ayab/engine/control.py b/src/main/python/main/ayab/engine/control.py index 57551621..f994e2f9 100644 --- a/src/main/python/main/ayab/engine/control.py +++ b/src/main/python/main/ayab/engine/control.py @@ -236,8 +236,10 @@ def cnf_line_API6(self, line_number: int) -> bool: ) try: msg = msg + " memo: " + str(self.memos[self.pat_row]) - except: + except IndexError: pass + except Exception as e: + self.logger.debug(f"Error logging memo: {str(e)}") if blank_line: msg += " BLANK LINE" else: @@ -273,7 +275,10 @@ def __update_status(self, line_number: int, color: int, bits: bitarray) -> None: self.status.line_number = line_number try: self.status.memo = self.memos[self.pat_row] - except: + except IndexError: + self.status.memo = "0" + except Exception as e: + self.logger.warning(f"Error setting memo value: {str(e)}") self.status.memo = "0" if self.inf_repeat: self.status.repeats = self.pattern_repeats From 12e40c34ed4a0f2a7541cc2b57f6ad90dc15a850 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Sun, 20 Apr 2025 07:57:33 -0400 Subject: [PATCH 21/25] Remove redundant try/excecpt clause --- src/main/python/main/ayab/image.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/python/main/ayab/image.py b/src/main/python/main/ayab/image.py index a60916c4..85186d7b 100644 --- a/src/main/python/main/ayab/image.py +++ b/src/main/python/main/ayab/image.py @@ -122,10 +122,7 @@ def __open(self, filename: str) -> None: 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') + self.memos.append(comment[i + 5]) # report metadata logging.info("File metadata Comment tag: " + comment) logging.info("File memo information: " + str(self.memos)) From 03b64043d2329549255dec8fd80bb074a7be18d3 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Sun, 20 Apr 2025 07:59:37 -0400 Subject: [PATCH 22/25] Remove TODO --- src/main/python/main/ayab/pattern_import.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/python/main/ayab/pattern_import.py b/src/main/python/main/ayab/pattern_import.py index 772d4b9f..59d8c313 100644 --- a/src/main/python/main/ayab/pattern_import.py +++ b/src/main/python/main/ayab/pattern_import.py @@ -5,8 +5,6 @@ # .cut and .pal file formats # documented at https://www.fileformat.info/format/drhalo/egff.htm -# TODO (TP): extract memo information - from __future__ import annotations import sys from typing import Optional, cast From 593551c1c26b443b3603f9519ef5f6dade4bd3bb Mon Sep 17 00:00:00 2001 From: Tom Price Date: Sun, 20 Apr 2025 08:01:11 -0400 Subject: [PATCH 23/25] Remove TODO --- src/main/python/main/ayab/knitprogress.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/python/main/ayab/knitprogress.py b/src/main/python/main/ayab/knitprogress.py index 471102eb..fb16b957 100644 --- a/src/main/python/main/ayab/knitprogress.py +++ b/src/main/python/main/ayab/knitprogress.py @@ -36,8 +36,6 @@ from .engine.status import Status -# TODO (TP): display memo information - class KnitProgress(QTableWidget): """ Class for the knit progress window, implemented as a subclass of `QScrollArea`. From 6f192ab5f211cbca48d8bc1d3b4081633b918117 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Sun, 20 Apr 2025 08:07:20 -0400 Subject: [PATCH 24/25] Add error handling to image transform --- src/main/python/main/ayab/image.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/python/main/ayab/image.py b/src/main/python/main/ayab/image.py index 85186d7b..4978a942 100644 --- a/src/main/python/main/ayab/image.py +++ b/src/main/python/main/ayab/image.py @@ -200,7 +200,15 @@ def apply_transform( *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 + # Update the view self.emit_image_resizer() From 29581268b32adeee46e9ffdab59e3de41bf6e35e Mon Sep 17 00:00:00 2001 From: Tom Price Date: Sun, 20 Apr 2025 08:10:29 -0400 Subject: [PATCH 25/25] Fix error handling in image transform --- src/main/python/main/ayab/image.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/python/main/ayab/image.py b/src/main/python/main/ayab/image.py index 4978a942..c4958f0b 100644 --- a/src/main/python/main/ayab/image.py +++ b/src/main/python/main/ayab/image.py @@ -207,8 +207,7 @@ def apply_transform( QCoreApplication.translate("Image", "Error applying transform"), "error" ) logging.error(f"Error in transform {transform.__name__}: {str(e)}") - return - + return # Update the view self.emit_image_resizer()