diff --git a/src/main/python/main/ayab/engine/control.py b/src/main/python/main/ayab/engine/control.py index c474adc4..82d33760 100644 --- a/src/main/python/main/ayab/engine/control.py +++ b/src/main/python/main/ayab/engine/control.py @@ -61,7 +61,6 @@ class Control(SignalSender): initial_position: int len_pat_expanded: int line_block: int - midline: int mode: Mode mode_func: ModeFuncType num_colors: int @@ -110,10 +109,6 @@ def start( ) self.start_pixel = self.start_needle - self.pattern.pat_start_needle self.end_pixel = self.end_needle - self.pattern.pat_start_needle - if self.FLANKING_NEEDLES and self.mode != Mode.SINGLEBED: - self.midline = self.pattern.knit_end_needle - self.machine.width // 2 - else: - self.midline = self.end_needle - self.machine.width // 2 self.initial_carriage = Carriage.Unknown self.initial_position = -1 self.initial_direction = Direction.Unknown @@ -155,6 +150,16 @@ def reset_status(self) -> None: else: self.status.alt_color = None + self.status.machine_width = self.machine.width + + if self.FLANKING_NEEDLES and self.mode != Mode.SINGLEBED: + self.status.knit_start_needle = self.pattern.knit_start_needle + else: + # in single-bed mode, only the pattern bits are emitted, no extra needles + self.status.knit_start_needle = self.start_needle + + self.status.passes_per_row = self.passes_per_row + def check_serial_API6(self) -> tuple[Token, int]: msg, token, param = self.com.update_API6() if msg is None: diff --git a/src/main/python/main/ayab/engine/engine.py b/src/main/python/main/ayab/engine/engine.py index 618945c5..34eec425 100644 --- a/src/main/python/main/ayab/engine/engine.py +++ b/src/main/python/main/ayab/engine/engine.py @@ -204,15 +204,10 @@ def __handle_status(self) -> None: # that holds up this thread until the knit progress window has finished # updating, Otherwise if the knit progress window lags the status # will change before the information is written to the UI. - data = Status() - data.copy(self.status) - self.emit_knit_progress_updater( - data, - self.control.passes_per_row, - self.control.midline, - self.config.auto_mirror, - ) - self.emit_progress_bar_updater(data) + status_copy = Status() + status_copy.copy(self.status) + self.emit_knit_progress_updater(status_copy) + self.emit_progress_bar_updater(status_copy) def cancel(self) -> None: self.__canceled = True diff --git a/src/main/python/main/ayab/engine/status.py b/src/main/python/main/ayab/engine/status.py index 511e5812..c9919ab6 100644 --- a/src/main/python/main/ayab/engine/status.py +++ b/src/main/python/main/ayab/engine/status.py @@ -110,13 +110,15 @@ class Status(object): active: bool # data fields alt_color: Optional[int] + machine_width: int + knit_start_needle: int + passes_per_row: int bits: bitarray color: int color_symbol: ColorSymbolType current_row: int firmware_state: int line_number: int - mirror: bool repeats: int total_rows: int # carriage info @@ -133,6 +135,9 @@ def __init__(self) -> None: def reset(self) -> None: self.active = True # data fields + self.machine_width = -1 + self.knit_start_needle = -1 + self.passes_per_row = 1 self.alt_color = None self.bits = bitarray() self.color = -1 @@ -140,7 +145,6 @@ def reset(self) -> None: self.current_row = -1 self.firmware_state = -1 self.line_number = -1 - self.mirror = False self.repeats = -1 self.total_rows = -1 # carriage info @@ -159,14 +163,16 @@ def copy(self, status: Status) -> None: self.color_symbol = status.color_symbol self.color = status.color self.alt_color = status.alt_color + self.total_rows = status.total_rows + self.machine_width = status.machine_width + self.knit_start_needle = status.knit_start_needle + self.passes_per_row = status.passes_per_row self.bits = status.bits self.hall_l = status.hall_l self.hall_r = status.hall_r self.carriage_type = status.carriage_type self.carriage_position = status.carriage_position self.carriage_direction = status.carriage_direction - self.total_rows = status.total_rows - self.mirror = status.mirror def parse_device_state_API6(self, state: Any, msg: bytes) -> None: if not (self.active): diff --git a/src/main/python/main/ayab/knitprogress.py b/src/main/python/main/ayab/knitprogress.py index 92101107..99007009 100644 --- a/src/main/python/main/ayab/knitprogress.py +++ b/src/main/python/main/ayab/knitprogress.py @@ -20,7 +20,12 @@ from __future__ import annotations from PySide6.QtCore import QCoreApplication, QRect, Qt -from PySide6.QtWidgets import QTableWidget, QTableWidgetItem, QHeaderView, QAbstractItemView +from PySide6.QtWidgets import ( + QTableWidget, + QTableWidgetItem, + QHeaderView, + QAbstractItemView, +) from PySide6.QtGui import QBrush, QColor from typing import TYPE_CHECKING, Optional, cast, List from math import floor @@ -50,16 +55,14 @@ def __init__(self, parent: GuiMain): self.__progbar = parent.progbar self.setGeometry(QRect(0, 0, 700, 220)) self.setContentsMargins(1, 1, 1, 1) - self.verticalHeader().setSectionResizeMode( - QHeaderView.ResizeMode.Fixed - ) + self.verticalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Fixed) self.verticalHeader().setSectionsClickable(False) self.horizontalHeader().setMinimumSectionSize(0) - self.horizontalHeader().setDefaultSectionSize(self.__prefs.value("lower_display_stitch_width")) - self.horizontalHeader().setSectionsClickable(False) - self.horizontalHeader().setSectionResizeMode( - QHeaderView.ResizeMode.Fixed + self.horizontalHeader().setDefaultSectionSize( + self.__prefs.value("lower_display_stitch_width") ) + self.horizontalHeader().setSectionsClickable(False) + self.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Fixed) self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectItems) self.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) self.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) @@ -90,16 +93,15 @@ def uiStateChanged(self, status: Status) -> bool: or status.carriage_direction != self.previousStatus.carriage_direction or status.bits != self.previousStatus.bits or status.alt_color != self.previousStatus.alt_color + or status.knit_start_needle != self.previousStatus.knit_start_needle + or status.machine_width != self.previousStatus.machine_width + or status.passes_per_row != self.previousStatus.passes_per_row ): return True return False - def update_progress( - self, status: Status, row_multiplier: int, midline: int, auto_mirror: bool - ) -> None: - # FIXME auto_mirror not used - + def update_progress(self, status: Status) -> None: if not self.uiStateChanged(status): return @@ -112,10 +114,11 @@ def update_progress( else: self.color = True - midline = self.load_columns_from_status(status, midline, columns) + self.load_columns_from_status(status, columns) # For the top row (row idx 0), we show the row header as "To Be Selected", - # When we show a new row, we recover the header info and recombine it with its row (now row idx 2) + # When we show a new row, we recover the header info and recombine it + # with its row (now row idx 2) self.make_row_with_spacer() if self.columnCount() != len(columns): @@ -123,34 +126,32 @@ def update_progress( n_cols = len(columns) if n_cols < 4: self.hideColumn(5) - self.instantiate_row_from_columns(midline, columns) + self.instantiate_row_from_columns(status, columns) self.previousStatus = status - self.previous_row_mulitplier = row_multiplier # update bar in Scene self.scene.row_progress = status.current_row - def load_columns_from_status(self, status: Status, midline: int, columns: List[QTableWidgetItem]) -> int: - midline = len(status.bits) - midline - - for c in range(0, midline): - columns.append(self.__stitch( - status.color, cast(bool, status.bits[c]), status.alt_color, self.__alternate_bg_colors(midline-c, self.orange) - )) - - # if we are only working on the right side, midline is negative. - green_start = midline - if green_start < 0: - green_start = 0 - for c in range(green_start, len(status.bits)): - columns.append(self.__stitch( - status.color, cast(bool, status.bits[c]), status.alt_color, self.__alternate_bg_colors(c-green_start, self.green) - )) - - return midline - - def instantiate_row_from_columns(self, midline: int, columns: List[QTableWidgetItem]) -> None: + def load_columns_from_status( + self, status: Status, columns: List[QTableWidgetItem] + ) -> None: + for c in range(0, len(status.bits)): + needle = status.knit_start_needle + c + needle_number_from_r1 = needle - status.machine_width // 2 + if needle_number_from_r1 < 0: + color = self.__alternate_bg_colors(needle_number_from_r1, self.orange) + else: + color = self.__alternate_bg_colors(needle_number_from_r1, self.green) + columns.append( + self.__stitch( + status.color, cast(bool, status.bits[c]), status.alt_color, color + ) + ) + + def instantiate_row_from_columns( + self, status: Status, columns: List[QTableWidgetItem] + ) -> None: self.setVerticalHeaderItem(0, QTableWidgetItem("To Be Selected")) for i, col in enumerate(columns): self.setItem(0, i, col) @@ -160,14 +161,16 @@ def instantiate_row_from_columns(self, midline: int, columns: List[QTableWidgetI self.horizontalHeader().setVisible(False) else: self.horizontalHeader().setVisible(True) - if i < midline: - header = QTableWidgetItem(f"{(midline)-(i)}") + needle = status.knit_start_needle + i + needle_number_from_r1 = needle - status.machine_width // 2 + if 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"{(i+1)-(midline)}") + 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) @@ -178,49 +181,74 @@ def make_row_with_spacer(self) -> None: self.insertRow(1) self.setVerticalHeaderItem(1, QTableWidgetItem("")) if self.rowCount() > 2: - self.setVerticalHeaderItem(2, self.format_row_header_text(self.previousStatus, self.previous_row_mulitplier)) + self.setVerticalHeaderItem( + 2, + self.format_row_header_text(self.previousStatus), + ) self.verticalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed) self.verticalHeader().setMinimumSectionSize(0) self.verticalHeader().resizeSection(1, 5) - def format_row_header_text(self, status: Optional[Status], row_multiplier: int) -> QTableWidgetItem: + def format_row_header_text(self, status: Optional[Status]) -> QTableWidgetItem: if status is None: return QTableWidgetItem("") tr_ = QCoreApplication.translate info_header = QTableWidgetItem() info_text = "" - row, swipe = divmod(status.line_number, row_multiplier) + swipe = status.line_number % status.passes_per_row # row "Row [1]" - info_text = (tr_("KnitProgress", "Row") + " " + str(status.current_row)) + info_text = tr_("KnitProgress", "Row") + " " + str(status.current_row) # pass, see Mode object. "Pass [1,2,3]" - if row_multiplier == 1: - info_text = info_text+(" "+tr_("KnitProgress", "Pass") + " " + str(swipe + 1)) + if status.passes_per_row > 1: + info_text = info_text + ( + f' {tr_("KnitProgress", "Pass")} {swipe + 1}/{status.passes_per_row}' + ) # color "Color [A,B,C,D]" if self.color is True: - info_text = info_text + " " + tr_("KnitProgress", "Color") + " " + status.color_symbol + info_text = ( + info_text + + " " + + tr_("KnitProgress", "Color") + + " " + + status.color_symbol + ) background_color = QColor(f"#{status.color:06x}") # Ensure text is readable if background_color.lightness() > 128: - background_color.setHsl(background_color.hslHue(), background_color.hslSaturation(), 128) + background_color.setHsl( + background_color.hslHue(), background_color.hslSaturation(), 128 + ) info_header.setForeground(QBrush(background_color)) # Carriage & Direction "[K,L,G] [<-,->]" carriage = status.carriage_type direction = status.carriage_direction - info_text = info_text + (" "+carriage.symbol + " " + direction.symbol) + info_text = info_text + (" " + carriage.symbol + " " + direction.symbol) info_header.setText(info_text) return info_header - def __alternate_bg_colors(self, position: int, color: int, frequency: int = 10) -> QColor: + def __alternate_bg_colors( + self, position: int, color: int, frequency: int = 10 + ) -> QColor: background_color = QColor(f"#{color:06x}") - bg_color_alternate = floor(position/frequency) % 2 + bg_color_alternate = (position // frequency) % 2 if bg_color_alternate > 0: - background_color.setHsl(floor(background_color.hslHue()*.85), floor(background_color.hslSaturation()*.85), background_color.lightness()) + background_color.setHsl( + floor(background_color.hslHue() * 0.85), + floor(background_color.hslSaturation() * 0.85), + background_color.lightness(), + ) return background_color - def __stitch(self, color: int, bit: bool, alt_color: Optional[int] = None, bg_color: Optional[QColor] = None) -> QTableWidgetItem: + def __stitch( + self, + color: int, + bit: bool, + alt_color: Optional[int] = None, + bg_color: Optional[QColor] = None, + ) -> QTableWidgetItem: stitch = QTableWidgetItem() if bit: background_color = QColor(f"#{color:06x}") @@ -236,9 +264,14 @@ def onStitchSelect(self, current: QTableWidgetItem | None) -> None: if current is None: self.__progbar.set_selection_label("") return - if self.horizontalHeaderItem(current.column()).foreground().color().red() == 187: + if ( + self.horizontalHeaderItem(current.column()).foreground().color().red() + == 187 + ): side = "Right" else: side = "Left" - selection_string = f"Selection: {self.verticalHeaderItem(current.row()).text()} , stitch {side}-{self.horizontalHeaderItem(current.column()).text()}" + selection_string = f"""Selection: { + self.verticalHeaderItem(current.row()).text()} stitch {side}-{ + self.horizontalHeaderItem(current.column()).text()}""" self.__progbar.set_selection_label(selection_string) diff --git a/src/main/python/main/ayab/signal_receiver.py b/src/main/python/main/ayab/signal_receiver.py index d256a07f..7a8ba618 100644 --- a/src/main/python/main/ayab/signal_receiver.py +++ b/src/main/python/main/ayab/signal_receiver.py @@ -44,7 +44,7 @@ class SignalReceiver(QObject): # over-ridden by instance attributes with the same name start_row_updater = Signal(int) progress_bar_updater = Signal(Status) - knit_progress_updater = Signal(Status, int, int, bool) + knit_progress_updater = Signal(Status) notifier = Signal(str, bool) # statusbar_updater = Signal('QString', bool) popup_displayer = Signal(str, str) diff --git a/src/main/python/main/ayab/signal_sender.py b/src/main/python/main/ayab/signal_sender.py index 6efcb854..0df8ef0e 100644 --- a/src/main/python/main/ayab/signal_sender.py +++ b/src/main/python/main/ayab/signal_sender.py @@ -52,12 +52,8 @@ def emit_start_row_updater(self, start_row: int) -> None: def emit_progress_bar_updater(self, status: Status) -> None: self.__signal_receiver.progress_bar_updater.emit(status) - def emit_knit_progress_updater( - self, status: Status, row_multiplier: int, midline: int, auto_mirror: bool - ) -> None: - self.__signal_receiver.knit_progress_updater.emit( - status, row_multiplier, midline, auto_mirror - ) + def emit_knit_progress_updater(self, status: Status) -> None: + self.__signal_receiver.knit_progress_updater.emit(status) def emit_notifier(self, text: str, log: bool) -> None: self.__signal_receiver.notifier.emit(text, log)