From cd15f2ae765f5c958f78810a8bf98574bbd302d6 Mon Sep 17 00:00:00 2001 From: wygud Date: Thu, 2 Oct 2025 14:25:44 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=9F=A2=20.gitignore=20=F0=9F=9B=A0?= =?UTF-8?q?=EF=B8=8F=20piker/ui/=5Faxes.py=20->=20Enhance=20axis=20font=20?= =?UTF-8?q?and=20size=20handling=20=F0=9F=9B=A0=EF=B8=8F=20piker/ui/=5Fwin?= =?UTF-8?q?dow.py=20->=20Improve=20zoom=20key=20detection=20and=20event=20?= =?UTF-8?q?handling=20=F0=9F=9B=A0=EF=B8=8F=20piker/ui/=5Fwindow.py=20->?= =?UTF-8?q?=20Update=20axes=20fonts=20and=20layout=20after=20zoom=20events?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + piker/ui/_axes.py | 56 ++++++++++++++++++++++++++++++++-- piker/ui/_window.py | 74 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 115 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 70826d07..bef34b85 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,4 @@ ENV/ # mypy .mypy_cache/ .vscode/settings.json +**/.DS_Store diff --git a/piker/ui/_axes.py b/piker/ui/_axes.py index 5eab5afe..a4ae601b 100644 --- a/piker/ui/_axes.py +++ b/piker/ui/_axes.py @@ -75,6 +75,9 @@ class Axis(pg.AxisItem): self.pi = plotitem self._dpi_font = _font + # store for later recalculation on zoom + self._typical_max_str = typical_max_str + self.setTickFont(_font.font) font_size = self._dpi_font.font.pixelSize() @@ -156,6 +159,41 @@ class Axis(pg.AxisItem): def size_to_values(self) -> None: pass + def update_fonts(self, font: DpiAwareFont) -> None: + '''Update font and recalculate axis sizing after zoom change.''' + # IMPORTANT: tell Qt we're about to change geometry + self.prepareGeometryChange() + + self._dpi_font = font + self.setTickFont(font.font) + font_size = font.font.pixelSize() + + # recalculate text offset based on new font size + text_offset = None + if self.orientation in ('bottom',): + text_offset = floor(0.25 * font_size) + elif self.orientation in ('left', 'right'): + text_offset = floor(font_size / 2) + + if text_offset: + self.setStyle(tickTextOffset=text_offset) + + # recalculate bounding rect with new font + # Note: typical_max_str should be stored from init + if not hasattr(self, '_typical_max_str'): + self._typical_max_str = '100 000.000 ' # fallback default + self.typical_br = font._qfm.boundingRect(self._typical_max_str) + + # Update PyQtGraph's internal text size tracking + # This is critical - PyQtGraph uses these internally for auto-expand + if self.orientation in ['left', 'right']: + self.textWidth = self.typical_br.width() + else: + self.textHeight = self.typical_br.height() + + # resize axis to fit new font - this triggers PyQtGraph's auto-expand + self.size_to_values() + def txt_offsets(self) -> tuple[int, int]: return tuple(self.style['tickTextOffset']) @@ -256,7 +294,14 @@ class PriceAxis(Axis): self._min_tick = size def size_to_values(self) -> None: - self.setWidth(self.typical_br.width()) + # Call PyQtGraph's internal width update mechanism + # This respects autoExpandTextSpace and updates min/max constraints + self._updateWidth() + # tell Qt our preferred size changed so layout recalculates + self.updateGeometry() + # force parent plot item to recalculate its layout + if self.pi and hasattr(self.pi, 'updateGeometry'): + self.pi.updateGeometry() # XXX: drop for now since it just eats up h space @@ -300,7 +345,14 @@ class DynamicDateAxis(Axis): } def size_to_values(self) -> None: - self.setHeight(self.typical_br.height() + 1) + # Call PyQtGraph's internal height update mechanism + # This respects autoExpandTextSpace and updates min/max constraints + self._updateHeight() + # tell Qt our preferred size changed so layout recalculates + self.updateGeometry() + # force parent plot item to recalculate its layout + if self.pi and hasattr(self.pi, 'updateGeometry'): + self.pi.updateGeometry() def _indexes_to_timestrs( self, diff --git a/piker/ui/_window.py b/piker/ui/_window.py index 27db80b3..47777c30 100644 --- a/piker/ui/_window.py +++ b/piker/ui/_window.py @@ -77,14 +77,19 @@ class GlobalZoomEventFilter(QObject): key = event.key() mods = event.modifiers() - # Check for Ctrl+Shift modifier combination - has_ctrl_shift = ( - (mods & Qt.KeyboardModifier.ControlModifier) and - (mods & Qt.KeyboardModifier.ShiftModifier) - ) + # Mask out the KeypadModifier which Qt sometimes adds + mods = mods & ~Qt.KeyboardModifier.KeypadModifier - if has_ctrl_shift: - # Zoom in: Ctrl+Shift+Plus or Ctrl+Shift+Equal + # Check if we have Ctrl+Shift (both required) + has_ctrl = bool(mods & Qt.KeyboardModifier.ControlModifier) + has_shift = bool(mods & Qt.KeyboardModifier.ShiftModifier) + + # Only handle UI zoom if BOTH Ctrl and Shift are pressed + # For Plus key: user presses Cmd+Shift+Equal (which makes Plus) + # For Minus key: user presses Cmd+Shift+Minus + if has_ctrl and has_shift: + # Zoom in: Ctrl+Shift+Plus + # Note: Plus key usually comes as Key_Equal with Shift modifier if key in (Qt.Key.Key_Plus, Qt.Key.Key_Equal): self.main_window.zoom_in() return True # consume event @@ -99,7 +104,10 @@ class GlobalZoomEventFilter(QObject): self.main_window.reset_zoom() return True # consume event - # Pass through all other events + # Pass through if only Ctrl (no Shift) - this goes to chart zoom + # Pass through all other events too + return False + return False @@ -481,16 +489,25 @@ class MainWindow(QMainWindow): # get main chart chart = getattr(splits, 'chart', None) - if chart and hasattr(chart, 'view'): - view = chart.view - if hasattr(view, 'order_mode') and view.order_mode: - order_mode = view.order_mode - if hasattr(order_mode, 'pane') and order_mode.pane: - order_mode.pane.update_fonts() + if chart: + # update axes + self._update_chart_axes(chart) + + # update order pane + if hasattr(chart, 'view'): + view = chart.view + if hasattr(view, 'order_mode') and view.order_mode: + order_mode = view.order_mode + if hasattr(order_mode, 'pane') and order_mode.pane: + order_mode.pane.update_fonts() # also check subplots subplots = getattr(splits, 'subplots', {}) for name, subplot_chart in subplots.items(): + # update subplot axes + self._update_chart_axes(subplot_chart) + + # update subplot order pane if hasattr(subplot_chart, 'view'): subplot_view = subplot_chart.view if hasattr(subplot_view, 'order_mode') and subplot_view.order_mode: @@ -498,6 +515,35 @@ class MainWindow(QMainWindow): if hasattr(subplot_order_mode, 'pane') and subplot_order_mode.pane: subplot_order_mode.pane.update_fonts() + # resize all sidepanes to match main chart's sidepane width + # this ensures volume/subplot sidepanes match the main chart + if splits and hasattr(splits, 'resize_sidepanes'): + splits.resize_sidepanes() + + def _update_chart_axes(self, chart) -> None: + '''Update axis fonts and sizing for a chart.''' + from . import _style + + # update price axis (right side) + if hasattr(chart, 'pi') and chart.pi: + plot_item = chart.pi + # get all axes from plot item + for axis_name in ['left', 'right', 'bottom', 'top']: + axis = plot_item.getAxis(axis_name) + if axis and hasattr(axis, 'update_fonts'): + axis.update_fonts(_style._font) + + # force plot item to recalculate its entire layout + plot_item.updateGeometry() + + # force chart widget to update + if hasattr(chart, 'updateGeometry'): + chart.updateGeometry() + + # trigger a full scene update + if hasattr(chart, 'update'): + chart.update() + def _refresh_widget_fonts(self, widget: QWidget) -> None: ''' Recursively update font sizes in all child widgets.