Skip to content
Open
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
27 changes: 18 additions & 9 deletions BlocksScreen/lib/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ class Files(QtCore.QObject):
- full_refresh_needed: Root changed
"""

_EVT_WS_OPEN: typing.ClassVar[QtCore.QEvent.Type] = events.WebSocketOpen.type()
_EVT_KLIPPER_DISC: typing.ClassVar[QtCore.QEvent.Type] = (
events.KlippyDisconnected.type()
)
_EVT_FILE_DATA: typing.ClassVar[QtCore.QEvent.Type] = ReceivedFileData.type()

# Signals for API requests
request_file_list = QtCore.pyqtSignal([], [str], name="api_get_files_list")
request_dir_info = QtCore.pyqtSignal(
Expand Down Expand Up @@ -225,10 +231,14 @@ def _connect_signals(self) -> None:
self.request_file_metadata.connect(self.ws.api.get_gcode_metadata)

def _install_event_filter(self) -> None:
"""Install event filter on application instance."""
app = QtWidgets.QApplication.instance()
if app:
app.installEventFilter(self)
"""Install event filter on parent to limit scope to mainWindow events only."""
parent = self.parent()
if parent is not None:
parent.installEventFilter(self)
else:
app = QtWidgets.QApplication.instance()
if app:
app.installEventFilter(self)

@property
def file_list(self) -> list[dict]:
Expand Down Expand Up @@ -657,19 +667,18 @@ def get_dir_information(

def eventFilter(self, obj: QtCore.QObject, event: QtCore.QEvent) -> bool:
"""Handle application-level events."""
if event.type() == events.WebSocketOpen.type():
etype = event.type()
if etype == self._EVT_WS_OPEN:
self.initial_load()
return False

if event.type() == events.KlippyDisconnected.type():
if etype == self._EVT_KLIPPER_DISC:
self._clear_all_data()
return False

return super().eventFilter(obj, event)

def event(self, event: QtCore.QEvent) -> bool:
"""Handle object-level events."""
if event.type() == ReceivedFileData.type():
if event.type() == self._EVT_FILE_DATA:
if isinstance(event, ReceivedFileData):
self.handle_message_received(event.method, event.data, event.params)
return True
Expand Down
27 changes: 20 additions & 7 deletions BlocksScreen/lib/panels/mainWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ class MainWindow(QtWidgets.QMainWindow):

call_load_panel = QtCore.pyqtSignal(bool, str, name="call-load-panel")

_EVT_WS_MSG: typing.ClassVar[QtCore.QEvent.Type] = (
events.WebSocketMessageReceived.type()
)
_EVT_PRINT_START: typing.ClassVar[QtCore.QEvent.Type] = events.PrintStart.type()
_EVT_PRINT_ERROR: typing.ClassVar[QtCore.QEvent.Type] = events.PrintError.type()
_EVT_PRINT_COMPLETE: typing.ClassVar[QtCore.QEvent.Type] = (
events.PrintComplete.type()
)
_EVT_PRINT_CANCELLED: typing.ClassVar[QtCore.QEvent.Type] = (
events.PrintCancelled.type()
)

def __init__(self):
"""Set up UI, instantiate subsystems, and wire all inter-component signals."""
super(MainWindow, self).__init__()
Expand Down Expand Up @@ -1131,12 +1143,13 @@ def closeEvent(self, a0: QtGui.QCloseEvent | None) -> None:

def event(self, event: QtCore.QEvent) -> bool:
"""Receives PyQt Events, reimplemented method from the QEvent class"""
if event.type() == events.WebSocketMessageReceived.type():
etype = event.type()
if etype == self._EVT_WS_MSG:
if isinstance(event, events.WebSocketMessageReceived):
self.messageReceivedEvent(event)
return True
return False
if event.type() == events.PrintStart.type():
if etype == self._EVT_PRINT_START:
self.print_status = "printing"
self.disable_tab_bar()
self.ui.extruder_temp_display.clicked.disconnect()
Expand All @@ -1155,13 +1168,13 @@ def event(self, event: QtCore.QEvent) -> bool:
)
return False

if event.type() in (
events.PrintError.type(),
events.PrintComplete.type(),
events.PrintCancelled.type(),
if etype in (
self._EVT_PRINT_ERROR,
self._EVT_PRINT_COMPLETE,
self._EVT_PRINT_CANCELLED,
):
self.print_status = "idle"
if event.type() == events.PrintCancelled.type():
if etype == self._EVT_PRINT_CANCELLED:
self.handle_cancel_print()
self.enable_tab_bar()
self.ui.extruder_temp_display.clicked.disconnect()
Expand Down
14 changes: 6 additions & 8 deletions BlocksScreen/lib/panels/widgets/filesPage.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget] = None) -> None:

self._file_list: list[dict] = []
self._files_data: dict[str, dict] = {} # filename -> metadata dict
self._display_name_to_key: dict[str, str] = {} # display_name -> filename key
self._directories: list[dict] = []
self._curr_dir: str = ""
self._pending_action: bool = False
Expand Down Expand Up @@ -86,6 +87,7 @@ def reload_gcodes_folder(self) -> None:
def clear_files_data(self) -> None:
"""Clear all cached file data."""
self._files_data.clear()
self._display_name_to_key.clear()
self._pending_metadata_requests.clear()
self._metadata_retry_count.clear()

Expand Down Expand Up @@ -155,6 +157,7 @@ def on_fileinfo(self, filedata: dict) -> None:

# Cache the file data
self._files_data[filename] = filedata
self._display_name_to_key[self._get_display_name(filename)] = filename

# Remove from pending requests and reset retry count (success)
self._pending_metadata_requests.discard(filename)
Expand Down Expand Up @@ -237,10 +240,7 @@ def _find_file_insert_position(self, modified_time: float) -> int:
"""
insert_pos = 0

for i in range(self._model.rowCount()):
index = self._model.index(i)
item = self._model.data(index, QtCore.Qt.ItemDataRole.UserRole)

for i, item in enumerate(self._model.entries):
if not item:
continue

Expand All @@ -265,10 +265,7 @@ def _find_file_insert_position(self, modified_time: float) -> int:

def _find_file_key_by_display_name(self, display_name: str) -> typing.Optional[str]:
"""Find the file key in _files_data by its display name."""
for key in self._files_data:
if self._get_display_name(key) == display_name:
return key
return None
return self._display_name_to_key.get(display_name)

@QtCore.pyqtSlot(dict, name="on_file_added")
def on_file_added(self, file_data: dict) -> None:
Expand Down Expand Up @@ -344,6 +341,7 @@ def on_file_removed(self, filepath: str) -> None:
current = self._curr_dir.removeprefix("/")

# Always clean up cache
self._display_name_to_key.pop(self._get_display_name(filepath), None)
self._files_data.pop(filepath, None)
self._pending_metadata_requests.discard(filepath)
self._metadata_retry_count.pop(filepath, None)
Expand Down
14 changes: 10 additions & 4 deletions BlocksScreen/lib/panels/widgets/loadWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,16 @@ def resizeEvent(self, a0: QtGui.QResizeEvent | None) -> None:

self.gifshow.setGeometry(gifshow_x, gifshow_y, size, size)

def show(self) -> None:
"""Re-implemented method, show widget"""
self.repaint()
return super().show()
def setVisible(self, visible: bool) -> None:
"""Re-implemented method, pause animation timer when hidden"""
if self.anim_type == LoadingOverlayWidget.AnimationGIF.DEFAULT:
if visible:
self.timer.start(16)
else:
self.timer.stop()
if visible:
self.repaint()
super().setVisible(visible)

def _setupUI(self) -> None:
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground, True)
Expand Down
67 changes: 30 additions & 37 deletions BlocksScreen/lib/utils/blocks_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ def __init__(
self.text_color: QtGui.QColor = QtGui.QColor(*ButtonColors.TEXT_COLOR.value)
self._show_notification: bool = False
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_AcceptTouchEvents, True)
self._icon_cache: QtGui.QPixmap = QtGui.QPixmap()
self._icon_cache_size: QtCore.QSize = QtCore.QSize()
self._cached_path_size: QtCore.QSize = QtCore.QSize()

def setShowNotification(self, show: bool) -> None:
"""Set notification on button"""
Expand Down Expand Up @@ -62,6 +65,8 @@ def setText(self, text: str) -> None:
def setPixmap(self, pixmap: QtGui.QPixmap) -> None:
"""Set button pixmap"""
self.icon_pixmap = pixmap
self._icon_cache = QtGui.QPixmap()
self._icon_cache_size = QtCore.QSize()
self.update()

def mousePressEvent(self, e: QtGui.QMouseEvent) -> None:
Expand Down Expand Up @@ -118,19 +123,14 @@ def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]):
return
# Flat button control
opt = QtWidgets.QStyleOptionButton()
draw_frame = (
if (
not self._is_flat
or self.underMouse()
or opt.state & QtWidgets.QStyle.StateFlag.State_Sunken
)
if draw_frame:
):
_style.drawControl(
QtWidgets.QStyle.ControlElement.CE_PushButtonLabel, opt, painter, self
)
_style.drawControl(
QtWidgets.QStyle.ControlElement.CE_PushButtonLabel, opt, painter, self
)
self.setStyle(_style)
# Determine background and text colors based on state
if not self.isEnabled():
bg_color_tuple = ButtonColors.DISABLED_BG.value
Expand All @@ -144,30 +144,21 @@ def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]):

bg_color = QtGui.QColor(*bg_color_tuple)

path = QtGui.QPainterPath()
xRadius = self.rect().toRectF().normalized().height() / 2.0
yRadius = self.rect().toRectF().normalized().height() / 2.0
painter.setBackgroundMode(QtCore.Qt.BGMode.TransparentMode)
path.addRoundedRect(
0,
0,
self.rect().toRectF().normalized().width(),
self.rect().toRectF().normalized().height(),
xRadius,
yRadius,
QtCore.Qt.SizeMode.AbsoluteSize,
)
icon_path = QtGui.QPainterPath()
self.button_ellipse = QtCore.QRectF(
self.rect().toRectF().normalized().left()
+ self.rect().toRectF().normalized().height() * 0.05,
self.rect().toRectF().normalized().top()
+ self.rect().toRectF().normalized().height() * 0.05,
(self.rect().toRectF().normalized().height() * 0.90),
(self.rect().toRectF().normalized().height() * 0.90),
)
icon_path.addEllipse(self.button_ellipse)
self.button_background = path.subtracted(icon_path)
current_size = _rect.size()
if current_size != self._cached_path_size:
h = float(_rect.height())
w = float(_rect.width())
radius = h / 2.0
path = QtGui.QPainterPath()
path.addRoundedRect(
0, 0, w, h, radius, radius, QtCore.Qt.SizeMode.AbsoluteSize
)
self.button_ellipse = QtCore.QRectF(h * 0.05, h * 0.05, h * 0.90, h * 0.90)
icon_path = QtGui.QPainterPath()
icon_path.addEllipse(self.button_ellipse)
self.button_background = path.subtracted(icon_path)
self._cached_path_size = current_size
painter.setPen(QtCore.Qt.PenStyle.NoPen)
painter.setBrush(bg_color)
painter.fillPath(self.button_background, bg_color)
Expand All @@ -179,11 +170,15 @@ def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]):
_parent_rect.height() * 0.80,
)
if not self.icon_pixmap.isNull():
_icon_scaled = self.icon_pixmap.scaled(
_icon_rect.size().toSize(),
QtCore.Qt.AspectRatioMode.KeepAspectRatio,
QtCore.Qt.TransformationMode.SmoothTransformation,
)
target_size = _icon_rect.size().toSize()
if target_size != self._icon_cache_size:
self._icon_cache = self.icon_pixmap.scaled(
target_size,
QtCore.Qt.AspectRatioMode.KeepAspectRatio,
QtCore.Qt.TransformationMode.SmoothTransformation,
)
self._icon_cache_size = target_size
_icon_scaled = self._icon_cache
scaled_width = _icon_scaled.width()
scaled_height = _icon_scaled.height()
adjusted_x = (_icon_rect.width() - scaled_width) / 2.0
Expand All @@ -194,8 +189,6 @@ def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]):
scaled_width,
scaled_height,
)
tinted_icon_pixmap = QtGui.QPixmap(_icon_scaled.size())
tinted_icon_pixmap.fill(QtCore.Qt.GlobalColor.transparent)
if not self.isEnabled():
tinted_icon_pixmap = QtGui.QPixmap(_icon_scaled.size())
tinted_icon_pixmap.fill(QtCore.Qt.GlobalColor.transparent)
Expand Down
Loading
Loading