🟢 .gitignore
🛠️ piker/ui/_axes.py -> Enhance axis font and size handling 🛠️ piker/ui/_window.py -> Improve zoom key detection and event handling 🛠️ piker/ui/_window.py -> Update axes fonts and layout after zoom eventsmacos_fixes_2025
parent
ed3a8d81b1
commit
cd15f2ae76
|
@ -103,3 +103,4 @@ ENV/
|
|||
# mypy
|
||||
.mypy_cache/
|
||||
.vscode/settings.json
|
||||
**/.DS_Store
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,7 +489,12 @@ class MainWindow(QMainWindow):
|
|||
|
||||
# get main chart
|
||||
chart = getattr(splits, 'chart', None)
|
||||
if chart and hasattr(chart, 'view'):
|
||||
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
|
||||
|
@ -491,6 +504,10 @@ class MainWindow(QMainWindow):
|
|||
# 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.
|
||||
|
|
Loading…
Reference in New Issue