Skip to content

Commit 6724d50

Browse files
authored
Merge branch 'dev' into refactor/typing-py311
Signed-off-by: Guilherme Costa <guilherme.costa@blockstec.com>
2 parents 8e64037 + bd1e46f commit 6724d50

21 files changed

Lines changed: 1797 additions & 669 deletions

‎BlocksScreen/configfile.py‎

Lines changed: 234 additions & 321 deletions
Large diffs are not rendered by default.

‎BlocksScreen/devices/amu/models.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class MMUState:
6767
filament: str # "Loaded" | "Unloaded" | "Unknown"
6868
filament_pos: FilamentPos
6969
action: str
70+
bowden_progress: float
7071
print_state: str
7172
reason_for_pause: str
7273
has_bypass: bool
@@ -142,6 +143,7 @@ def from_status(cls, data: dict) -> "MMUState":
142143
filament=data.get("filament", "Unknown"),
143144
filament_pos=FilamentPos(data.get("filament_pos", FilamentPos.UNKNOWN)),
144145
action=data.get("action", ""),
146+
bowden_progress=data.get("bowden_progress", -1),
145147
print_state=data.get("print_state", ""),
146148
reason_for_pause=data.get("reason_for_pause", ""),
147149
has_bypass=data.get("has_bypass", False),

‎BlocksScreen/lib/panels/filamentTab.py‎

Lines changed: 126 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from lib.utils.blocks_linedit import BlocksCustomLinEdit
2020
from lib.utils.icon_button import IconButton
2121
from lib.utils.list_model import EntryDelegate, EntryListModel, ListItem
22+
from lib.utils.toolmap import MmuToolmapWidget
2223
from PyQt6 import QtCore, QtGui, QtWidgets
2324

2425
logger = logging.getLogger(__name__)
@@ -58,8 +59,13 @@ def __init__(
5859
self._material_filter: str | None = None
5960
self.moonraker_run = True
6061

61-
self.amu_manager.mmu_state_changed.connect(self.on_mmu_state_changed)
6262
self._setup_pre_gate_popup()
63+
self._setup_load_popup()
64+
self.amu_manager.mmu_state_changed.connect(self.on_mmu_state_changed)
65+
66+
self._extruder_current_temp = 0.0
67+
self._extruder_target_temp = 0.0
68+
self.printer.extruder_update.connect(self.on_extruder_update)
6369

6470
self.spoolmanPanel = SpoolmanPage(self)
6571
self.addWidget(self.spoolmanPanel)
@@ -96,8 +102,15 @@ def __init__(
96102
body, callback=self.spoolmanPanel.on_add_filament_result
97103
)
98104
)
105+
self.spoolmanPanel.request_add_manufacturer.connect(
106+
lambda body: self.ws.api.add_manufacturer(
107+
body, callback=self.spoolmanPanel.on_add_manufacturer_result
108+
)
109+
)
99110

100-
self._basic_panel = BasicFilamentPanel(self.printer, self.cfg, parent=self)
111+
self._basic_panel = BasicFilamentPanel(
112+
self.printer, self.cfg, parent=self, load_popup=self.load_popup
113+
)
101114
self._basic_panel.run_gcode.connect(self.run_gcode)
102115
self._basic_panel.call_load_panel.connect(self.call_load_panel)
103116
self._basic_panel.request_back.connect(self.request_back)
@@ -115,7 +128,17 @@ def __init__(
115128

116129
self.run_gcode.connect(self.ws.api.run_gcode)
117130

131+
def in_case_error(self):
132+
"""Reset the popup and spool info in case of an error."""
133+
self._reset_popup()
134+
self.reset_spool_info()
135+
self._add_spool_page.setFilter(None)
136+
self._add_filament_page.setData("---", 0)
137+
self.load_state = False
138+
self.load_popup.hide()
139+
118140
def handle_moonraker_components(self):
141+
"""Build the pre-gate popup pages once, choosing spoolman vs. manual-entry order."""
119142
if self.moonraker_run:
120143
components = self.ws._moonRest.get_server_info()
121144
if "spoolman" not in components["result"].get("components", []):
@@ -137,6 +160,43 @@ def change_page(self, index: int) -> None:
137160
"""
138161
self.request_change_page.emit(1, index)
139162

163+
def _setup_load_popup(self) -> None:
164+
self.load_popup = BasePopup(self, floating=False, dialog=False)
165+
load_container = QtWidgets.QWidget(self.load_popup)
166+
load_layout = QtWidgets.QVBoxLayout(load_container)
167+
168+
self.load_status_label = QtWidgets.QLabel(load_container)
169+
self.load_status_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
170+
label_font = QtGui.QFont()
171+
label_font.setPointSize(20)
172+
self.load_status_label.setFont(label_font)
173+
self.load_status_label.setStyleSheet("color: #ffffff; background: transparent;")
174+
self.load_status_label.setMaximumHeight(100)
175+
load_layout.addWidget(self.load_status_label)
176+
177+
self.load_status_widget = MmuToolmapWidget(load_container)
178+
self.load_status_widget.set_left_text("Auxiliary Extruder")
179+
load_layout.addWidget(self.load_status_widget)
180+
181+
self.load_popup.add_widget(load_container)
182+
183+
@QtCore.pyqtSlot(str, str, float, name="on-extruder-update")
184+
def on_extruder_update(
185+
self, extruder_name: str, field: str, new_value: float
186+
) -> None:
187+
"""Track the extruder's current/target temp and feed it to the load status widget."""
188+
if extruder_name != "extruder":
189+
return
190+
if field == "temperature":
191+
self._extruder_current_temp = new_value
192+
elif field == "target":
193+
self._extruder_target_temp = new_value
194+
else:
195+
return
196+
self.load_status_widget.set_temps(
197+
self._extruder_current_temp, self._extruder_target_temp
198+
)
199+
140200
def _setup_pre_gate_popup(self) -> None:
141201
self._numpad = CustomNumpad(self)
142202
self._numpad.hide()
@@ -382,6 +442,16 @@ def _build_spool_page(self) -> QtWidgets.QWidget:
382442
frame_lay.addWidget(self._spool_load_widget, 1)
383443
self._spool_list_view.hide()
384444

445+
self._no_spools_label = QtWidgets.QLabel("", frame)
446+
self._no_spools_label.setWordWrap(True)
447+
self._no_spools_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
448+
self._no_spools_label.setStyleSheet("color: gray;")
449+
no_spools_font = QtGui.QFont()
450+
no_spools_font.setPointSize(13)
451+
self._no_spools_label.setFont(no_spools_font)
452+
self._no_spools_label.hide()
453+
frame_lay.addWidget(self._no_spools_label)
454+
385455
self._add_spool_page = AddSpoolPage(self)
386456
self._add_filament_page = AddFilamentPage(self)
387457

@@ -511,6 +581,7 @@ def _add_row(title_text: str) -> QtWidgets.QLabel:
511581
return page
512582

513583
def handle_skip_button(self):
584+
"""Map the pending pre-gate gate to an empty spool and dismiss the popup."""
514585
gate = self.pre_gate_idx.get("gate", 0)
515586
self._reset_popup()
516587
self.run_gcode.emit(
@@ -519,7 +590,7 @@ def handle_skip_button(self):
519590
if self._popup_callback is not None:
520591
try:
521592
self._popup_callback()
522-
except Exception as e:
593+
except Exception as e: # noqa: BLE001 - arbitrary caller-supplied callback
523594
logger.error(f"Error executing pre-gate accept callback: {e}")
524595
finally:
525596
self._popup_callback = None
@@ -562,6 +633,7 @@ def handle_popup(self, force=False):
562633

563634
@QtCore.pyqtSlot(int, str, str, "PyQt_PyObject", name="open-pregate-popup")
564635
def open_pregate_popup(self, temp, material, name, callback=None):
636+
"""Open the pre-gate popup pre-filled with a detected filament's info and *callback*."""
565637
self._popup_name.setText(name)
566638
self._popup_material.setText(material)
567639
self._popup_temp.setText(str(temp))
@@ -600,7 +672,7 @@ def on_popup_accept(self):
600672
if self._popup_callback is not None:
601673
try:
602674
self._popup_callback()
603-
except Exception as e:
675+
except Exception as e: # noqa: BLE001 - arbitrary caller-supplied callback
604676
logger.error(f"Error executing pre-gate accept callback: {e}")
605677
finally:
606678
self._popup_callback = None
@@ -620,13 +692,12 @@ def _reset_popup(self):
620692

621693
@QtCore.pyqtSlot(dict, name="on-spools-received")
622694
def on_spools_received(self, result: dict) -> None:
623-
"""Handles the result from the API call to get spools for the spoolman page."""
624695
self._spool_load_widget.hide()
625696
self._spool_list_view.show()
626-
627697
self.reset_spool_info()
628-
629698
if result.get("error") is not None:
699+
self._no_spools_label.setText("Could not reach Spoolman")
700+
self._no_spools_label.show()
630701
return
631702
spools = result.get("response")
632703
if not isinstance(spools, list):
@@ -666,6 +737,16 @@ def on_spools_received(self, result: dict) -> None:
666737
self._spool_id_map[name] = spool
667738
self.update()
668739

740+
if self._spool_id_map:
741+
self._no_spools_label.hide()
742+
else:
743+
self._no_spools_label.setText(
744+
f'No spools found for "{self._material_filter}"'
745+
if self._material_filter
746+
else "No spools found"
747+
)
748+
self._no_spools_label.show()
749+
669750
def _on_spool_selected(self) -> None:
670751
item = self._spool_model.get_selected_item()
671752
if item is None:
@@ -715,14 +796,15 @@ def _on_spool_selected(self) -> None:
715796
if self._popup_callback is not None:
716797
try:
717798
self._popup_callback()
718-
except Exception as e:
799+
except Exception as e: # noqa: BLE001 - arbitrary caller-supplied callback
719800
logger.error(f"Error executing pre-gate accept callback: {e}")
720801
finally:
721802
self._popup_callback = None
722803

723804
self.handle_popup()
724805

725806
def reset_spool_info(self):
807+
"""Clear the selected-spool detail labels back to their placeholder state."""
726808
self.filament_name_label.setText("N/A")
727809
self.material_label.setText("N/A")
728810
self.weight_label.setText("N/A")
@@ -854,7 +936,10 @@ def on_mmu_state_changed(self, mmu_state):
854936

855937
if not self.amu_configured:
856938
if len(mmu_state.gates) > 1:
857-
self.amupage = AMUpage(self.amu_manager, parent=self)
939+
self.load_status_widget.set_left_text("AMU")
940+
self.amupage = AMUpage(
941+
self.amu_manager, parent=self, load_popup=self.load_popup
942+
)
858943
self.addWidget(self.amupage)
859944
try:
860945
self.removeWidget(self._basic_panel)
@@ -880,26 +965,50 @@ def on_mmu_state_changed(self, mmu_state):
880965
self.printer.print_stats_update[str, float].connect(
881966
self.amupage.on_print_stats_update
882967
)
883-
self.amupage.call_load_panel.connect(self.call_load_panel)
884968
self.amupage.request_keyboard.connect(self._on_show_keyboard)
885969
self.amupage.request_color_wheel.connect(self._open_color_wheel)
886-
970+
else:
971+
self.load_status_widget.set_left_text("Auxiliary Extruder")
887972
self.amu_configured = True
888973

889974
if self.load_state:
890975
if mmu_state.action == "Idle":
891976
self.load_state = False
892-
self.call_load_panel.emit(False, "", True)
977+
self.load_popup.hide()
893978
if not len(mmu_state.gates) > 1:
894979
self._basic_panel.change_page(0)
895-
return
896-
self.call_load_panel.emit(True, mmu_state.action, True)
897-
898-
if mmu_state.action == "Loading" or mmu_state.action == "Unloading":
980+
elif mmu_state.action in ("Loading", "Unloading"):
899981
self.load_state = True
900-
self.call_load_panel.emit(True, mmu_state.action, True)
982+
self.load_popup.show()
983+
984+
if not self.load_popup.isVisible():
985+
return
986+
self.load_status_widget.set_filament_pos(
987+
mmu_state.filament_pos, mmu_state.bowden_progress
988+
)
989+
990+
for sensor_name in ("mmu_pre_gate", "mmu_gate", "toolhead"):
991+
self.load_status_widget.set_sensor(
992+
sensor_name, bool(mmu_state.sensors.get(sensor_name))
993+
)
994+
self.load_status_widget.set_action(mmu_state.action)
995+
996+
gate_info = mmu_state.current_gate_info
997+
raw_color = (
998+
str(gate_info.color).lstrip("#")[:6]
999+
if gate_info and gate_info.color
1000+
else ""
1001+
)
1002+
parsed_color = QtGui.QColor("#" + raw_color) if raw_color else QtGui.QColor()
1003+
if parsed_color.isValid():
1004+
self.load_status_widget.set_gate_color(parsed_color)
1005+
else:
1006+
self.load_status_widget.set_gate_color(QtGui.QColor("#ffffff"))
1007+
1008+
self.load_status_label.setText(mmu_state.action)
9011009

9021010
def setupUi(self):
1011+
"""Build the tab's landing page (title + Filament Control / Spoolman buttons)."""
9031012
self.resize(710, 410)
9041013
self.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight)
9051014
widget = QtWidgets.QWidget()

‎BlocksScreen/lib/panels/mainWindow.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ def __init__(self):
195195
self.printPanel.request_back.connect(slot=self.global_back)
196196
self.printPanel.on_cancel_print.connect(slot=self.on_cancel_print)
197197
self.in_case_error.connect(self.printPanel.in_case_error)
198+
self.in_case_error.connect(self.filamentPanel.in_case_error)
198199

199200
self.show_notifications.connect(self.notiPage.new_notication)
200201

0 commit comments

Comments
 (0)