Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bottom display when pattern is near the edge #737

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/main/python/main/ayab/engine/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
13 changes: 4 additions & 9 deletions src/main/python/main/ayab/engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
14 changes: 10 additions & 4 deletions src/main/python/main/ayab/engine/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -133,14 +135,16 @@ 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
self.color_symbol = ""
self.current_row = -1
self.firmware_state = -1
self.line_number = -1
self.mirror = False
self.repeats = -1
self.total_rows = -1
# carriage info
Expand All @@ -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):
Expand Down
143 changes: 88 additions & 55 deletions src/main/python/main/ayab/knitprogress.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand All @@ -112,45 +114,44 @@ 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):
self.setColumnCount(len(columns))
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)
Expand All @@ -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)
Expand All @@ -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}")
Expand All @@ -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)
2 changes: 1 addition & 1 deletion src/main/python/main/ayab/signal_receiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 2 additions & 6 deletions src/main/python/main/ayab/signal_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down