mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-13 19:21:50 +02:00
fix: add is_log checks and functionality to plot_indicator_items
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
"""Module to create an arrow item for a pyqtgraph plot"""
|
||||
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
from qtpy.QtCore import QObject, Qt, Signal, Slot
|
||||
|
||||
@ -12,6 +13,9 @@ class BECIndicatorItem(QObject):
|
||||
super().__init__(parent=parent)
|
||||
self.accent_colors = get_accent_colors()
|
||||
self.plot_item = plot_item
|
||||
self._pos = None
|
||||
self.is_log_x = False
|
||||
self.is_log_y = False
|
||||
|
||||
def add_to_plot(self) -> None:
|
||||
"""Add the item to the plot"""
|
||||
@ -25,6 +29,12 @@ class BECIndicatorItem(QObject):
|
||||
"""Set the position of the item"""
|
||||
raise NotImplementedError("Method set_position not implemented")
|
||||
|
||||
def check_log(self):
|
||||
"""Checks if the x or y axis is in log scale and updates the internal state accordingly."""
|
||||
self.is_log_x = self.plot_item.ctrl.logXCheck.isChecked()
|
||||
self.is_log_y = self.plot_item.ctrl.logYCheck.isChecked()
|
||||
self.set_position(self._pos)
|
||||
|
||||
def cleanup(self) -> None:
|
||||
"""Cleanup the item"""
|
||||
self.remove_from_plot()
|
||||
@ -43,7 +53,8 @@ class BECTickItem(BECIndicatorItem):
|
||||
super().__init__(plot_item=plot_item, parent=parent)
|
||||
self.tick_item = pg.TickSliderItem(allowAdd=False, allowRemove=False)
|
||||
self.tick = None
|
||||
self._tick_pos = 0
|
||||
# Set _pos to float
|
||||
self._pos = 0
|
||||
self._range = [0, 1]
|
||||
|
||||
@Slot(float)
|
||||
@ -53,8 +64,15 @@ class BECTickItem(BECIndicatorItem):
|
||||
Args:
|
||||
pos (float): The position of the tick item.
|
||||
"""
|
||||
self._tick_pos = pos
|
||||
self.update_range(self.plot_item.vb, self._range)
|
||||
if self.is_log_x is True:
|
||||
pos = pos if pos > 0 else 1e-10
|
||||
pos = np.log10(pos)
|
||||
self._pos = pos
|
||||
view_box = self.plot_item.getViewBox() # Ensure you're accessing the correct view box
|
||||
view_range = view_box.viewRange()[0]
|
||||
self.update_range(self.plot_item.vb, view_range)
|
||||
self.position_changed.emit(pos)
|
||||
self.position_changed_str.emit(str(pos))
|
||||
|
||||
def update_range(self, vb, viewRange) -> None:
|
||||
"""Update the range of the tick item
|
||||
@ -69,12 +87,9 @@ class BECTickItem(BECIndicatorItem):
|
||||
lengthIncludingPadding = length + self.tick_item.tickSize + 2
|
||||
|
||||
self._range = viewRange
|
||||
|
||||
tickValueIncludingPadding = (self._tick_pos - viewRange[0]) / (viewRange[1] - viewRange[0])
|
||||
tickValueIncludingPadding = (self._pos - viewRange[0]) / (viewRange[1] - viewRange[0])
|
||||
tickValue = (tickValueIncludingPadding * lengthIncludingPadding - origin) / length
|
||||
self.tick_item.setTickValue(self.tick, tickValue)
|
||||
self.position_changed.emit(self._tick_pos)
|
||||
self.position_changed_str.emit(str(self._tick_pos))
|
||||
|
||||
def add_to_plot(self):
|
||||
"""Add the tick item to the view box or plot item."""
|
||||
@ -82,6 +97,8 @@ class BECTickItem(BECIndicatorItem):
|
||||
self.plot_item.layout.addItem(self.tick_item, 4, 1)
|
||||
self.tick = self.tick_item.addTick(0, movable=False, color=self.accent_colors.highlight)
|
||||
self.plot_item.vb.sigXRangeChanged.connect(self.update_range)
|
||||
self.plot_item.ctrl.logXCheck.checkStateChanged.connect(self.check_log)
|
||||
self.plot_item.ctrl.logYCheck.checkStateChanged.connect(self.check_log)
|
||||
|
||||
def remove_from_plot(self):
|
||||
"""Remove the tick item from the view box or plot item."""
|
||||
@ -110,6 +127,7 @@ class BECArrowItem(BECIndicatorItem):
|
||||
def __init__(self, plot_item: pg.PlotItem = None, parent=None):
|
||||
super().__init__(plot_item=plot_item, parent=parent)
|
||||
self.arrow_item = pg.ArrowItem()
|
||||
self._pos = (0, 0)
|
||||
|
||||
@Slot(dict)
|
||||
def set_style(self, style: dict) -> None:
|
||||
@ -128,11 +146,23 @@ class BECArrowItem(BECIndicatorItem):
|
||||
Args:
|
||||
pos (tuple): The position of the arrow item as a tuple (x, y).
|
||||
"""
|
||||
self._pos = pos
|
||||
pos_x = pos[0]
|
||||
pos_y = pos[1]
|
||||
if self.is_log_x is True:
|
||||
pos_x = np.log10(pos_x) if pos_x > 0 else 1e-10
|
||||
view_box = self.plot_item.getViewBox() # Ensure you're accessing the correct view box
|
||||
view_range = view_box.viewRange()[0]
|
||||
# Avoid values outside the view range in the negative direction. Otherwise, there is
|
||||
# a buggy behaviour of the arrow item and it appears at the wrong position.
|
||||
if pos_x < view_range[0]:
|
||||
pos_x = view_range[0]
|
||||
if self.is_log_y is True:
|
||||
pos_y = np.log10(pos_y) if pos_y > 0 else 1e-10
|
||||
|
||||
self.arrow_item.setPos(pos_x, pos_y)
|
||||
self.position_changed.emit((pos_x, pos_y))
|
||||
self.position_changed_str.emit((str(pos_x), str(pos_y)))
|
||||
self.position_changed.emit(self._pos)
|
||||
self.position_changed_str.emit((str(self._pos[0]), str(self._pos[1])))
|
||||
|
||||
def add_to_plot(self):
|
||||
"""Add the arrow item to the view box or plot item."""
|
||||
@ -144,8 +174,16 @@ class BECArrowItem(BECIndicatorItem):
|
||||
)
|
||||
if self.plot_item is not None:
|
||||
self.plot_item.addItem(self.arrow_item)
|
||||
self.plot_item.ctrl.logXCheck.checkStateChanged.connect(self.check_log)
|
||||
self.plot_item.ctrl.logYCheck.checkStateChanged.connect(self.check_log)
|
||||
|
||||
def remove_from_plot(self):
|
||||
"""Remove the arrow item from the view box or plot item."""
|
||||
if self.plot_item is not None:
|
||||
self.plot_item.removeItem(self.arrow_item)
|
||||
|
||||
def check_log(self):
|
||||
"""Checks if the x or y axis is in log scale and updates the internal state accordingly."""
|
||||
self.is_log_x = self.plot_item.ctrl.logXCheck.isChecked()
|
||||
self.is_log_y = self.plot_item.ctrl.logYCheck.isChecked()
|
||||
self.set_position(self._pos)
|
||||
|
@ -1,43 +1,55 @@
|
||||
import pyqtgraph as pg
|
||||
import pytest
|
||||
from bec_qthemes._main import AccentColors
|
||||
|
||||
from bec_widgets.utils.plot_indicator_items import BECArrowItem, BECTickItem
|
||||
from bec_widgets.widgets.waveform.waveform_widget import BECWaveformWidget
|
||||
|
||||
from .client_mocks import mocked_client
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def arrow_item():
|
||||
"""Fixture for the BECArrowItem class"""
|
||||
item = BECArrowItem(plot_item=pg.PlotItem())
|
||||
yield item
|
||||
@pytest.fixture
|
||||
def plot_widget_with_arrow_item(qtbot, mocked_client):
|
||||
widget = BECWaveformWidget(client=mocked_client())
|
||||
qtbot.addWidget(widget)
|
||||
qtbot.waitExposed(widget)
|
||||
|
||||
yield widget.waveform.arrow_item, widget.waveform.plot_item
|
||||
|
||||
|
||||
def test_arrow_item_add_to_plot(arrow_item):
|
||||
@pytest.fixture
|
||||
def plot_widget_with_tick_item(qtbot, mocked_client):
|
||||
widget = BECWaveformWidget(client=mocked_client())
|
||||
qtbot.addWidget(widget)
|
||||
qtbot.waitExposed(widget)
|
||||
|
||||
yield widget.waveform.motor_pos_tick, widget.waveform.plot_item
|
||||
|
||||
|
||||
def test_arrow_item_add_to_plot(plot_widget_with_arrow_item):
|
||||
"""Test the add_to_plot method"""
|
||||
arrow_item, plot_item = plot_widget_with_arrow_item
|
||||
assert arrow_item.plot_item is not None
|
||||
assert arrow_item.plot_item.items == []
|
||||
arrow_item.accent_colors = AccentColors(theme="dark")
|
||||
arrow_item.add_to_plot()
|
||||
assert arrow_item.plot_item.items == [arrow_item.arrow_item]
|
||||
|
||||
|
||||
def test_arrow_item_remove_to_plot(arrow_item):
|
||||
def test_arrow_item_remove_to_plot(plot_widget_with_arrow_item):
|
||||
"""Test the remove_from_plot method"""
|
||||
arrow_item.accent_colors = AccentColors(theme="dark")
|
||||
arrow_item, plot_item = plot_widget_with_arrow_item
|
||||
arrow_item.add_to_plot()
|
||||
assert arrow_item.plot_item.items == [arrow_item.arrow_item]
|
||||
arrow_item.remove_from_plot()
|
||||
assert arrow_item.plot_item.items == []
|
||||
|
||||
|
||||
def test_arrow_item_set_position(arrow_item):
|
||||
def test_arrow_item_set_position(plot_widget_with_arrow_item):
|
||||
"""Test the set_position method"""
|
||||
arrow_item, plot_item = plot_widget_with_arrow_item
|
||||
container = []
|
||||
|
||||
def signal_callback(tup: tuple):
|
||||
container.append(tup)
|
||||
|
||||
arrow_item.accent_colors = AccentColors(theme="dark")
|
||||
arrow_item.add_to_plot()
|
||||
arrow_item.position_changed.connect(signal_callback)
|
||||
arrow_item.set_position(pos=(1, 1))
|
||||
@ -47,44 +59,37 @@ def test_arrow_item_set_position(arrow_item):
|
||||
assert container == [(1, 1), (2, 2)]
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def tick_item():
|
||||
"""Fixture for the BECArrowItem class"""
|
||||
item = BECTickItem(plot_item=pg.PlotItem())
|
||||
yield item
|
||||
|
||||
|
||||
def test_tick_item_add_to_plot(tick_item):
|
||||
def test_tick_item_add_to_plot(plot_widget_with_tick_item):
|
||||
"""Test the add_to_plot method"""
|
||||
tick_item, plot_item = plot_widget_with_tick_item
|
||||
assert tick_item.plot_item is not None
|
||||
assert tick_item.plot_item.items == []
|
||||
tick_item.accent_colors = AccentColors(theme="dark")
|
||||
tick_item.add_to_plot()
|
||||
assert tick_item.plot_item.layout.itemAt(4, 1) == tick_item.tick_item
|
||||
|
||||
|
||||
def test_tick_item_remove_to_plot(tick_item):
|
||||
def test_tick_item_remove_to_plot(plot_widget_with_tick_item):
|
||||
"""Test the remove_from_plot method"""
|
||||
tick_item.accent_colors = AccentColors(theme="dark")
|
||||
tick_item, plot_item = plot_widget_with_tick_item
|
||||
tick_item.add_to_plot()
|
||||
assert tick_item.plot_item.layout.itemAt(4, 1) == tick_item.tick_item
|
||||
tick_item.remove_from_plot()
|
||||
assert tick_item.plot_item.layout.itemAt(4, 1) is None
|
||||
|
||||
|
||||
def test_tick_item_set_position(tick_item):
|
||||
def test_tick_item_set_position(plot_widget_with_tick_item):
|
||||
"""Test the set_position method"""
|
||||
tick_item, plot_item = plot_widget_with_tick_item
|
||||
container = []
|
||||
|
||||
def signal_callback(val: float):
|
||||
container.append(val)
|
||||
|
||||
tick_item.accent_colors = AccentColors(theme="dark")
|
||||
tick_item.add_to_plot()
|
||||
tick_item.position_changed.connect(signal_callback)
|
||||
|
||||
tick_item.set_position(pos=1)
|
||||
assert tick_item._tick_pos == 1
|
||||
assert tick_item._pos == 1
|
||||
tick_item.set_position(pos=2)
|
||||
assert tick_item._tick_pos == 2
|
||||
assert tick_item._pos == 2
|
||||
assert container == [1.0, 2.0]
|
||||
|
Reference in New Issue
Block a user