0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-14 11:41:49 +02:00

fix(cli): find_widget_by_id for BECImageShow changed to be compatible with RPC logic

This commit is contained in:
wyzula-jan
2024-03-12 22:21:21 +01:00
parent 4865b10ced
commit 4ef6ae90f2
5 changed files with 157 additions and 38 deletions

View File

@ -555,7 +555,7 @@ class BECImageShow(RPCBase):
Args: Args:
vmin(float): Minimum value of the color bar. vmin(float): Minimum value of the color bar.
vmax(float): Maximum value of the color bar. vmax(float): Maximum value of the color bar.
name(str): The name of the image. name(str): The name of the image. If None, apply to all images.
""" """
@rpc_call @rpc_call
@ -565,7 +565,7 @@ class BECImageShow(RPCBase):
If name is not specified, then set color map for all images. If name is not specified, then set color map for all images.
Args: Args:
cmap(str): The color map of the image. cmap(str): The color map of the image.
name(str): The name of the image. name(str): The name of the image. If None, apply to all images.
""" """
@ -585,7 +585,67 @@ class BECImageItem(RPCBase):
@rpc_call @rpc_call
def set(self, **kwargs): def set(self, **kwargs):
""" """
None Set the properties of the image.
Args:
**kwargs: Keyword arguments for the properties to be set.
Possible properties:
- downsample
- color_map
- monitor
- opacity
- vrange
- fft
- log
- rot
- transpose
"""
@rpc_call
def set_fft(self, enable: "bool" = False):
"""
Set the FFT of the image.
Args:
enable(bool): Whether to perform FFT on the monitor data.
"""
@rpc_call
def set_log(self, enable: "bool" = False):
"""
Set the log of the image.
Args:
enable(bool): Whether to perform log on the monitor data.
"""
@rpc_call
def set_rotation(self, deg_90: "int" = 0):
"""
Set the rotation of the image.
Args:
deg_90(int): The rotation angle of the monitor data before displaying.
"""
@rpc_call
def set_transpose(self, enable: "bool" = False):
"""
Set the transpose of the image.
Args:
enable(bool): Whether to transpose the image.
"""
@rpc_call
def set_opacity(self, opacity: "float" = 1.0):
"""
Set the opacity of the image.
Args:
opacity(float): The opacity of the image.
"""
@rpc_call
def set_autorange(self, autorange: "bool" = True):
"""
Set the autorange of the color bar.
Args:
autorange(bool): Whether to autorange the color bar.
""" """
@rpc_call @rpc_call

View File

@ -132,7 +132,6 @@ class RPCBase:
print(f"RPCBase: {rpc_msg}") print(f"RPCBase: {rpc_msg}")
# pylint: disable=protected-access # pylint: disable=protected-access
receiver = self._root._gui_id receiver = self._root._gui_id
# self._client.connector.send(MessageEndpoints.gui_instructions(receiver), rpc_msg)
self._client.connector.set_and_publish(MessageEndpoints.gui_instructions(receiver), rpc_msg) self._client.connector.set_and_publish(MessageEndpoints.gui_instructions(receiver), rpc_msg)
if not wait_for_rpc_response: if not wait_for_rpc_response:

View File

@ -62,9 +62,9 @@ class BECWidgetsCLIServer:
item = widget.find_widget_by_id(gui_id) item = widget.find_widget_by_id(gui_id)
if item: if item:
return item return item
raise NotImplementedError( # raise NotImplementedError(
f"gui_id lookup for widget of type {widget.__class__.__name__} not implemented" # f"gui_id lookup for widget of type {widget.__class__.__name__} not implemented"
) # )
raise ValueError(f"Object with gui_id {gui_id} not found") raise ValueError(f"Object with gui_id {gui_id} not found")
@ -94,7 +94,7 @@ class BECWidgetsCLIServer:
"config": obj.config.model_dump(), "config": obj.config.model_dump(),
"__rpc__": True, "__rpc__": True,
} }
return obj return obpyj
if __name__ == "__main__": # pragma: no cover if __name__ == "__main__": # pragma: no cover

View File

@ -607,6 +607,7 @@ class BECFigureMainWindow(QMainWindow):
def closeEvent(self, event): def closeEvent(self, event):
self.figure.cleanup() self.figure.cleanup()
self.figure.client.shutdown()
if self.safe_close == True: if self.safe_close == True:
print("Safe close") print("Safe close")
event.accept() event.accept()

View File

@ -31,6 +31,7 @@ class ProcessingConfig(BaseModel):
class ImageItemConfig(ConnectionConfig): class ImageItemConfig(ConnectionConfig):
parent_id: Optional[str] = Field(None, description="The parent plot of the image.")
monitor: Optional[str] = Field(None, description="The name of the monitor.") monitor: Optional[str] = Field(None, description="The name of the monitor.")
source: Optional[str] = Field(None, description="The source of the curve.") source: Optional[str] = Field(None, description="The source of the curve.")
color_map: Optional[str] = Field("magma", description="The color map of the image.") color_map: Optional[str] = Field("magma", description="The color map of the image.")
@ -42,6 +43,7 @@ class ImageItemConfig(ConnectionConfig):
color_bar: Optional[Literal["simple", "full"]] = Field( color_bar: Optional[Literal["simple", "full"]] = Field(
"simple", description="The type of the color bar." "simple", description="The type of the color bar."
) )
autorange: Optional[bool] = Field(True, description="Whether to autorange the color bar.")
processing: ProcessingConfig = Field( processing: ProcessingConfig = Field(
default_factory=ProcessingConfig, description="The post processing of the image." default_factory=ProcessingConfig, description="The post processing of the image."
) )
@ -57,6 +59,12 @@ class ImageConfig(WidgetConfig):
class BECImageItem(BECConnector, pg.ImageItem): class BECImageItem(BECConnector, pg.ImageItem):
USER_ACCESS = [ USER_ACCESS = [
"set", "set",
"set_fft",
"set_log",
"set_rotation",
"set_transpose",
"set_opacity",
"set_autorange",
"set_color_map", "set_color_map",
"set_auto_downsample", "set_auto_downsample",
"set_monitor", "set_monitor",
@ -89,13 +97,30 @@ class BECImageItem(BECConnector, pg.ImageItem):
self.set(**kwargs) self.set(**kwargs)
def apply_config(self): def apply_config(self):
"""
Apply current configuration.
"""
self.set_color_map(self.config.color_map) self.set_color_map(self.config.color_map)
self.set_auto_downsample(self.config.downsample) self.set_auto_downsample(self.config.downsample)
if self.config.vrange is not None: if self.config.vrange is not None:
self.set_vrange(vrange=self.config.vrange) self.set_vrange(vrange=self.config.vrange)
# self.set_color_bar(self.config.color_bar)
def set(self, **kwargs): def set(self, **kwargs):
"""
Set the properties of the image.
Args:
**kwargs: Keyword arguments for the properties to be set.
Possible properties:
- downsample
- color_map
- monitor
- opacity
- vrange
- fft
- log
- rot
- transpose
"""
method_map = { method_map = {
"downsample": self.set_auto_downsample, "downsample": self.set_auto_downsample,
"color_map": self.set_color_map, "color_map": self.set_color_map,
@ -114,23 +139,58 @@ class BECImageItem(BECConnector, pg.ImageItem):
print(f"Warning: '{key}' is not a recognized property.") print(f"Warning: '{key}' is not a recognized property.")
def set_fft(self, enable: bool = False): def set_fft(self, enable: bool = False):
"""
Set the FFT of the image.
Args:
enable(bool): Whether to perform FFT on the monitor data.
"""
self.config.processing.fft = enable self.config.processing.fft = enable
def set_log(self, enable: bool = False): def set_log(self, enable: bool = False):
"""
Set the log of the image.
Args:
enable(bool): Whether to perform log on the monitor data.
"""
self.config.processing.log = enable self.config.processing.log = enable
if enable and self.color_bar and self.config.color_bar == "full": if enable and self.color_bar and self.config.color_bar == "full":
self.color_bar.autoHistogramRange() self.color_bar.autoHistogramRange()
def set_rotation(self, deg_90: int = 0): def set_rotation(self, deg_90: int = 0):
"""
Set the rotation of the image.
Args:
deg_90(int): The rotation angle of the monitor data before displaying.
"""
self.config.processing.rotation = deg_90 self.config.processing.rotation = deg_90
def set_transpose(self, enable: bool = False): def set_transpose(self, enable: bool = False):
"""
Set the transpose of the image.
Args:
enable(bool): Whether to transpose the image.
"""
self.config.processing.transpose = enable self.config.processing.transpose = enable
def set_opacity(self, opacity: float = 1.0): def set_opacity(self, opacity: float = 1.0):
"""
Set the opacity of the image.
Args:
opacity(float): The opacity of the image.
"""
self.setOpacity(opacity) self.setOpacity(opacity)
self.config.opacity = opacity self.config.opacity = opacity
def set_autorange(self, autorange: bool = True):
"""
Set the autorange of the color bar.
Args:
autorange(bool): Whether to autorange the color bar.
"""
self.config.autorange = autorange
if self.color_bar is not None:
self.color_bar.autoHistogramRange()
def set_color_map(self, cmap: str = "magma"): def set_color_map(self, cmap: str = "magma"):
""" """
Set the color map of the image. Set the color map of the image.
@ -173,6 +233,7 @@ class BECImageItem(BECConnector, pg.ImageItem):
vmin, vmax = vrange vmin, vmax = vrange
self.setLevels([vmin, vmax]) self.setLevels([vmin, vmax])
self.config.vrange = (vmin, vmax) self.config.vrange = (vmin, vmax)
self.config.autorange = False
if self.color_bar is not None: if self.color_bar is not None:
if self.config.color_bar == "simple": if self.config.color_bar == "simple":
self.color_bar.setLevels(low=vmin, high=vmax) self.color_bar.setLevels(low=vmin, high=vmax)
@ -215,24 +276,6 @@ class BECImageItem(BECConnector, pg.ImageItem):
else: else:
raise ValueError("style should be 'simple' or 'full'") raise ValueError("style should be 'simple' or 'full'")
def _update_color_bar_for_log(self):
"""
Update the color bar to reflect a logarithmic scale.
"""
...
# if self.config.vrange:
# vmin, vmax = self.config.vrange
# offset = 1e-6
# vmin_log, vmax_log = np.log10(vmin + offset), np.log10(vmax + offset)
# if self.config.color_bar == "simple":
# self.color_bar.setLevels(low=vmin_log, high=vmax_log)
# elif self.config.color_bar == "full":
# self.color_bar.setImageItem(self)
# self.color_bar.setLevels(min=vmin_log, max=vmax_log)
# self.color_bar.setHistogramRange(
# vmin_log - 0.1 * vmin_log, vmax_log + 0.1 * vmax_log
# )
class BECImageShow(BECPlotBase): class BECImageShow(BECPlotBase):
USER_ACCESS = [ USER_ACCESS = [
@ -286,6 +329,20 @@ class BECImageShow(BECPlotBase):
Args: Args:
item_id(str): The gui_id of the widget. item_id(str): The gui_id of the widget.
Returns:
BECImageItem: The widget with the given gui_id.
"""
for source, images in self._images.items():
for monitor, image_item in images.items():
if image_item.gui_id == item_id:
return image_item
def find_image_by_monitor(self, item_id: str) -> BECImageItem:
"""
Find the widget by its gui_id.
Args:
item_id(str): The gui_id of the widget.
Returns: Returns:
BECImageItem: The widget with the given gui_id. BECImageItem: The widget with the given gui_id.
""" """
@ -294,7 +351,7 @@ class BECImageShow(BECPlotBase):
if key == item_id and isinstance(value, BECImageItem): if key == item_id and isinstance(value, BECImageItem):
return value return value
elif isinstance(value, dict): elif isinstance(value, dict):
result = self.find_widget_by_id(item_id) result = self.find_image_by_monitor(item_id)
if result is not None: if result is not None:
return result return result
@ -344,6 +401,7 @@ class BECImageShow(BECPlotBase):
""" """
if isinstance(config, dict): if isinstance(config, dict):
config = ImageItemConfig(**config) config = ImageItemConfig(**config)
config.parent_id = self.gui_id
name = config.monitor if config.monitor is not None else config.gui_id name = config.monitor if config.monitor is not None else config.gui_id
image = self._add_image_object(source=config.source, name=name, config=config) image = self._add_image_object(source=config.source, name=name, config=config)
return image return image
@ -468,7 +526,7 @@ class BECImageShow(BECPlotBase):
image_id (str, optional): The ID of the specific image to apply the setting to. If None, applies to all images. image_id (str, optional): The ID of the specific image to apply the setting to. If None, applies to all images.
""" """
if image_id: if image_id:
image = self.find_widget_by_id(image_id) image = self.find_image_by_monitor(image_id)
if image: if image:
getattr(image, setting_method_name)(*args, **kwargs) getattr(image, setting_method_name)(*args, **kwargs)
else: else:
@ -610,7 +668,7 @@ class BECImageShow(BECPlotBase):
data(np.ndarray): The data to be updated. data(np.ndarray): The data to be updated.
""" """
image_to_update = self._images["device_monitor"][device] image_to_update = self._images["device_monitor"][device]
image_to_update.updateImage(data) image_to_update.updateImage(data, autoLevels=image_to_update.config.autorange)
def _connect_device_monitor(self, monitor: str): def _connect_device_monitor(self, monitor: str):
""" """
@ -618,7 +676,7 @@ class BECImageShow(BECPlotBase):
Args: Args:
monitor(str): The name of the monitor. monitor(str): The name of the monitor.
""" """
image_item = self.find_widget_by_id(monitor) image_item = self.find_image_by_monitor(monitor)
try: try:
previous_monitor = image_item.config.monitor previous_monitor = image_item.config.monitor
except AttributeError: except AttributeError:
@ -637,6 +695,7 @@ class BECImageShow(BECPlotBase):
def _add_image_object( def _add_image_object(
self, source: str, name: str, config: ImageItemConfig, data=None self, source: str, name: str, config: ImageItemConfig, data=None
) -> BECImageItem: # TODO fix types ) -> BECImageItem: # TODO fix types
config.parent_id = self.gui_id
image = BECImageItem(config=config, parent_image=self) image = BECImageItem(config=config, parent_image=self)
self.plot_item.addItem(image) self.plot_item.addItem(image)
self._images[source][name] = image self._images[source][name] = image
@ -668,13 +727,13 @@ class BECImageShow(BECPlotBase):
Clean up the widget. Clean up the widget.
""" """
print(f"Cleaning up {self.gui_id}") print(f"Cleaning up {self.gui_id}")
for monitor in self._images["device_monitor"]: # for monitor in self._images["device_monitor"]:
self.bec_dispatcher.disconnect_slot( # self.bec_dispatcher.disconnect_slot(
self.on_image_update, MessageEndpoints.device_monitor(monitor) # self.on_image_update, MessageEndpoints.device_monitor(monitor)
) # )
if self.thread.isRunning(): # if self.thread is not None and self.thread.isRunning():
self.thread.quit() # self.thread.quit()
self.thread.wait() # self.thread.wait()
class ImageProcessor: class ImageProcessor: