Skip to content

Commit 492ca25

Browse files
authored
Merge branch 'dev' into ref/files-page-window
2 parents 57ab1c6 + 70e2c43 commit 492ca25

File tree

7 files changed

+661
-142
lines changed

7 files changed

+661
-142
lines changed

BlocksScreen/lib/panels/controlTab.py

Lines changed: 143 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
from PyQt6 import QtCore, QtGui, QtWidgets
1414

1515
from lib.panels.widgets.popupDialogWidget import Popup
16+
from lib.utils.display_button import DisplayButton
17+
from lib.panels.widgets.slider_selector_page import SliderPage
18+
19+
from lib.panels.widgets.optionCardWidget import OptionCard
20+
from helper_methods import normalize
1621

1722

1823
class ControlTab(QtWidgets.QStackedWidget):
@@ -42,6 +47,8 @@ class ControlTab(QtWidgets.QStackedWidget):
4247
request_file_info: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal(
4348
str, name="request-file-info"
4449
)
50+
tune_display_buttons: dict = {}
51+
card_options: dict = {}
4552

4653
def __init__(
4754
self,
@@ -74,6 +81,11 @@ def __init__(
7481
self.addWidget(self.printcores_page)
7582
self.loadpage = LoadScreen(self, LoadScreen.AnimationGIF.DEFAULT)
7683
self.addWidget(self.loadpage)
84+
85+
self.sliderPage = SliderPage(self)
86+
self.addWidget(self.sliderPage)
87+
self.sliderPage.request_back.connect(self.back_button)
88+
7789
self.probe_helper_page.request_page_view.connect(
7890
partial(self.change_page, self.indexOf(self.probe_helper_page))
7991
)
@@ -108,6 +120,10 @@ def __init__(
108120
self.panel.cp_temperature_btn.clicked.connect(
109121
partial(self.change_page, self.indexOf(self.panel.temperature_page))
110122
)
123+
self.panel.cp_fans_btn.clicked.connect(
124+
partial(self.change_page, self.indexOf(self.panel.fans_page))
125+
)
126+
self.panel.fans_back_btn.clicked.connect(self.back_button)
111127
self.panel.cp_switch_print_core_btn.clicked.connect(self.show_swapcore)
112128
self.panel.cp_nozzles_calibration_btn.clicked.connect(
113129
partial(self.change_page, self.indexOf(self.probe_helper_page))
@@ -267,6 +283,133 @@ def __init__(
267283
self.panel.cooldown_btn.hide()
268284
self.panel.cp_switch_print_core_btn.hide()
269285

286+
self.printer.fan_update[str, str, float].connect(self.on_fan_object_update)
287+
self.printer.fan_update[str, str, int].connect(self.on_fan_object_update)
288+
289+
@QtCore.pyqtSlot(str, str, float, name="on_fan_update")
290+
@QtCore.pyqtSlot(str, str, int, name="on_fan_update")
291+
def on_fan_object_update(
292+
self, name: str, field: str, new_value: int | float
293+
) -> None:
294+
"""Slot that receives updates from fan objects.
295+
296+
Args:
297+
name (str): Fan object name
298+
field (str): Field name
299+
new_value (int | float): New value for the field
300+
"""
301+
if "speed" not in field:
302+
return
303+
304+
if name == "fan_generic Auxiliary_Cooling_Fans":
305+
name = "Auxiliary\ncooling fans"
306+
elif name == "fan_generic CHAMBER_EXHAUST":
307+
name = "Exhaust Fan"
308+
elif name == "fan_generic Part_Cooling_Fan":
309+
name = "Cooling fan"
310+
else:
311+
name = name.removeprefix("fan_generic")
312+
fan_card = self.tune_display_buttons.get(name)
313+
314+
if fan_card is None:
315+
icon_path = (
316+
":/temperature_related/media/btn_icons/blower.svg"
317+
if "blower" in name.lower()
318+
else ":/temperature_related/media/btn_icons/fan.svg"
319+
)
320+
icon = QtGui.QPixmap(icon_path)
321+
322+
card = OptionCard(self, name, str(name), icon) # type: ignore
323+
card.setObjectName(str(name))
324+
325+
# Add card to layout and record reference
326+
self.card_options[name] = card
327+
self.panel.fans_content_layout.addWidget(card)
328+
329+
# If the card doesn't have expected UI properties, discard it
330+
if not hasattr(card, "continue_clicked"):
331+
del card
332+
self.card_options.pop(name, None)
333+
return
334+
335+
card.setMode(True)
336+
card.secondtext.setText(f"{new_value}%")
337+
card.continue_clicked.connect(
338+
lambda: self.on_slidePage_request(
339+
str(name),
340+
card.secondtext.text().replace("%", ""),
341+
self.on_slider_change,
342+
0,
343+
100,
344+
)
345+
)
346+
347+
self.tune_display_buttons[name] = card
348+
self.update()
349+
fan_card = card
350+
351+
if fan_card:
352+
value_percent = new_value * 100 if new_value <= 1 else new_value
353+
fan_card.secondtext.setText(f"{value_percent:.0f}%")
354+
355+
@QtCore.pyqtSlot(str, int, "PyQt_PyObject", name="on_slidePage_request")
356+
@QtCore.pyqtSlot(str, int, "PyQt_PyObject", int, int, name="on_slidePage_request")
357+
def on_slidePage_request(
358+
self,
359+
name: str,
360+
current_value: int,
361+
callback,
362+
min_value: int = 0,
363+
max_value: int = 100,
364+
) -> None:
365+
self.sliderPage.value_selected.connect(callback)
366+
self.sliderPage.set_name(name)
367+
self.sliderPage.set_slider_position(int(current_value))
368+
self.sliderPage.set_slider_minimum(min_value)
369+
self.sliderPage.set_slider_maximum(max_value)
370+
self.change_page(self.indexOf(self.sliderPage))
371+
372+
@QtCore.pyqtSlot(str, int, name="on_slider_change")
373+
def on_slider_change(self, name: str, new_value: int) -> None:
374+
if "speed" in name.lower():
375+
self.speed_factor_override = new_value / 100
376+
self.run_gcode_signal.emit(f"M220 S{new_value}")
377+
378+
if name == "Auxiliary\ncooling fans":
379+
name = "Auxiliary_Cooling_Fans"
380+
elif name == "Exhaust Fan":
381+
name = "CHAMBER_EXHAUST"
382+
elif name == "Cooling fan":
383+
name = "Part_Cooling_Fan"
384+
else:
385+
...
386+
if name.lower() == "fan":
387+
self.run_gcode_signal.emit(
388+
f"M106 S{int(round((normalize(float(new_value / 100), 0.0, 1.0, 0, 255))))}"
389+
) # [0, 255] Range
390+
else:
391+
self.run_gcode_signal.emit(
392+
f'SET_FAN_SPEED FAN="{name}" SPEED={float(new_value / 100.00)}'
393+
) # [0.0, 1.0] Range
394+
395+
def create_display_button(self, name: str) -> DisplayButton:
396+
"""Create and return a DisplayButton
397+
398+
Args:
399+
name (str): Name for the display button
400+
401+
Returns:
402+
DisplayButton: The created DisplayButton object
403+
"""
404+
display_button = DisplayButton()
405+
display_button.setObjectName(str(name + "_display"))
406+
display_button.setMinimumSize(QtCore.QSize(150, 50))
407+
display_button.setMaximumSize(QtCore.QSize(150, 80))
408+
font = QtGui.QFont()
409+
font.setPointSize(16)
410+
display_button.setFont(font)
411+
return display_button
412+
270413
def handle_printcoreupdate(self, value: dict):
271414
if value["swapping"] == "idle":
272415
return
@@ -297,9 +440,7 @@ def _handle_gcode_response(self, messages: list):
297440
and "range:" in msg_list
298441
and "tolerance:" in msg_list
299442
):
300-
print("Match candidate:", msg_list)
301443
match = re.search(pattern, msg_list)
302-
print("Regex match:", match)
303444

304445
if match:
305446
retries_done = int(match.group(1))

BlocksScreen/lib/panels/widgets/optionCardWidget.py

Lines changed: 91 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import typing
22

33
from PyQt6 import QtCore, QtGui, QtWidgets
4-
from lib.utils.blocks_label import BlocksLabel
54
from lib.utils.icon_button import IconButton
65

76

8-
class OptionCard(QtWidgets.QFrame):
9-
continue_clicked: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal(
7+
class OptionCard(QtWidgets.QAbstractButton):
8+
clicked: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal(
109
"PyQt_PyObject", name="continue_clicked"
1110
)
1211

@@ -25,8 +24,22 @@ def __init__(
2524
self.icon_background_color = QtGui.QColor(150, 150, 130, 80)
2625
self.name = name
2726
self.card_text = text
27+
self.doubleT: bool = False
2828
self._setupUi(self)
29-
self.continue_button.clicked.connect(lambda: self.continue_clicked.emit(self))
29+
self.option_icon.setAttribute(
30+
QtCore.Qt.WidgetAttribute.WA_TransparentForMouseEvents
31+
)
32+
self.option_text.setAttribute(
33+
QtCore.Qt.WidgetAttribute.WA_TransparentForMouseEvents
34+
)
35+
self.secondtext.setAttribute(
36+
QtCore.Qt.WidgetAttribute.WA_TransparentForMouseEvents
37+
)
38+
self.line_separator.setAttribute(
39+
QtCore.Qt.WidgetAttribute.WA_TransparentForMouseEvents
40+
)
41+
42+
self.setMode(False)
3043
self.set_card_icon(icon)
3144
self.set_card_text(text)
3245

@@ -42,7 +55,13 @@ def enable_button(self) -> None:
4255

4356
def set_card_icon(self, pixmap: QtGui.QPixmap) -> None:
4457
"""Set widget icon"""
45-
self.option_icon.setPixmap(pixmap)
58+
scaled = pixmap.scaled(
59+
300,
60+
300,
61+
QtCore.Qt.AspectRatioMode.IgnoreAspectRatio,
62+
QtCore.Qt.TransformationMode.SmoothTransformation,
63+
)
64+
self.option_icon.setPixmap(scaled)
4665
self.repaint()
4766

4867
def set_card_text(self, text: str) -> None:
@@ -79,14 +98,54 @@ def leaveEvent(self, a0: QtCore.QEvent) -> None:
7998

8099
def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
81100
"""Re-implemented method, handle mouse press event"""
101+
self.clicked.emit(self)
82102
self.update()
83103
return super().mousePressEvent(a0)
84104

105+
def setMode(self, double_mode: bool = False):
106+
"""Set the mode of the layout: single or double text."""
107+
self.doubleT = double_mode
108+
109+
# Clear existing widgets from layout before adding new ones
110+
while self.verticalLayout.count():
111+
item = self.verticalLayout.takeAt(0)
112+
widget = item.widget()
113+
if widget is not None:
114+
widget.setParent(None)
115+
116+
if self.doubleT:
117+
self.verticalLayout.addWidget(
118+
self.option_icon,
119+
0,
120+
QtCore.Qt.AlignmentFlag.AlignHCenter
121+
| QtCore.Qt.AlignmentFlag.AlignBottom,
122+
)
123+
self.verticalLayout.addWidget(
124+
self.secondtext, 0, QtCore.Qt.AlignmentFlag.AlignHCenter
125+
)
126+
self.verticalLayout.addWidget(
127+
self.line_separator, 0, QtCore.Qt.AlignmentFlag.AlignCenter
128+
)
129+
self.verticalLayout.addWidget(self.option_text)
130+
self.verticalLayout.addItem(self.spacer)
131+
self.secondtext.show()
132+
else:
133+
self.verticalLayout.addWidget(
134+
self.option_icon, 0, QtCore.Qt.AlignmentFlag.AlignCenter
135+
)
136+
self.verticalLayout.addWidget(
137+
self.line_separator, 0, QtCore.Qt.AlignmentFlag.AlignCenter
138+
)
139+
self.verticalLayout.addWidget(self.option_text)
140+
self.verticalLayout.addWidget(self.continue_button)
141+
142+
self.update()
143+
85144
def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
86145
"""Re-implemented method, paint widget"""
87146
# Rounded background edges
88-
self.background_path = QtGui.QPainterPath()
89-
self.background_path.addRoundedRect(
147+
background_path = QtGui.QPainterPath()
148+
background_path.addRoundedRect(
90149
self.rect().toRectF(), 20.0, 20.0, QtCore.Qt.SizeMode.AbsoluteSize
91150
)
92151

@@ -108,7 +167,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
108167
painter.setRenderHint(painter.RenderHint.Antialiasing)
109168
painter.setRenderHint(painter.RenderHint.SmoothPixmapTransform)
110169
painter.setRenderHint(painter.RenderHint.LosslessImageRendering)
111-
painter.fillPath(self.background_path, bg_color)
170+
painter.fillPath(background_path, bg_color)
112171
if self.underMouse():
113172
_pen = QtGui.QPen()
114173
_pen.setStyle(QtCore.Qt.PenStyle.SolidLine)
@@ -134,7 +193,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
134193
_gradient.setColorAt(1, _color3)
135194
painter.setBrush(_gradient)
136195
painter.setPen(QtCore.Qt.PenStyle.NoPen)
137-
painter.fillPath(self.background_path, painter.brush())
196+
painter.fillPath(background_path, painter.brush())
138197

139198
painter.end()
140199

@@ -154,12 +213,21 @@ def _setupUi(self, option_card):
154213
self.verticalLayout = QtWidgets.QVBoxLayout(option_card)
155214
self.verticalLayout.setContentsMargins(0, 0, -1, -1)
156215
self.verticalLayout.setObjectName("verticalLayout")
157-
self.option_icon = BlocksLabel(parent=option_card)
216+
self.option_icon = IconButton(parent=option_card)
158217
self.option_icon.setMinimumSize(QtCore.QSize(200, 150))
159218
self.option_icon.setObjectName("option_icon")
160-
self.verticalLayout.addWidget(
161-
self.option_icon, 0, QtCore.Qt.AlignmentFlag.AlignHCenter
162-
)
219+
self.option_icon.setScaledContents(True)
220+
_button_font = QtGui.QFont()
221+
_button_font.setBold(True)
222+
_button_font.setPointSize(20)
223+
self.secondtext = QtWidgets.QLabel(parent=option_card)
224+
self.secondtext.setText("%")
225+
self.secondtext.setStyleSheet("color:white")
226+
self.secondtext.setFont(_button_font)
227+
self.secondtext.setObjectName("option_text")
228+
self.secondtext.setWordWrap(True)
229+
self.secondtext.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
230+
self.secondtext.hide()
163231
self.line_separator = QtWidgets.QFrame(parent=option_card)
164232
self.line_separator.setFrameShape(QtWidgets.QFrame.Shape.HLine)
165233
self.line_separator.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
@@ -174,21 +242,15 @@ def _setupUi(self, option_card):
174242
self.option_text = QtWidgets.QLabel(parent=option_card)
175243
self.option_text.setMinimumSize(QtCore.QSize(200, 50))
176244
self.option_text.setObjectName("option_text")
177-
self.verticalLayout.addWidget(
178-
self.option_text,
179-
)
180-
self.continue_button = IconButton(parent=option_card)
181-
self.option_text.setAlignment(
182-
QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter
183-
)
184245
self.option_text.setWordWrap(True)
185-
_button_font = QtGui.QFont()
186-
_button_font.setBold(True)
246+
self.option_text.setStyleSheet("color:white")
247+
self.option_text.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
187248
_palette = self.option_text.palette()
188249
_palette.setColor(QtGui.QPalette.ColorRole.WindowText, self.text_color)
189250
self.option_text.setPalette(_palette)
190-
_button_font.setPointSize(15)
251+
191252
self.option_text.setFont(_button_font)
253+
self.continue_button = IconButton(parent=option_card)
192254
sizePolicy = QtWidgets.QSizePolicy(
193255
QtWidgets.QSizePolicy.Policy.MinimumExpanding,
194256
QtWidgets.QSizePolicy.Policy.MinimumExpanding,
@@ -207,8 +269,13 @@ def _setupUi(self, option_card):
207269
QtGui.QPixmap(":/arrow_icons/media/btn_icons/right_arrow.svg"),
208270
)
209271
self.continue_button.setObjectName("continue_button")
210-
self.verticalLayout.addWidget(self.continue_button)
211272

273+
self.spacer = QtWidgets.QSpacerItem(
274+
20,
275+
40,
276+
QtWidgets.QSizePolicy.Policy.Minimum,
277+
QtWidgets.QSizePolicy.Policy.Expanding,
278+
)
212279
self._retranslateUi(option_card)
213280
QtCore.QMetaObject.connectSlotsByName(option_card)
214281

0 commit comments

Comments
 (0)