From d94184329029001ec9e2037b7967a70b221134a4 Mon Sep 17 00:00:00 2001 From: Steven Zeltmann Date: Thu, 21 Nov 2024 10:28:42 -0500 Subject: [PATCH 1/5] change how detectors are drawn to make customizing simpler --- pyproject.toml | 2 +- src/py4D_browser/main_window.py | 24 +++++++------ src/py4D_browser/update_views.py | 61 ++++++++++++++++++++++++-------- src/py4D_browser/utils.py | 4 +-- 4 files changed, 64 insertions(+), 27 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2eb1b60..ef30fc1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "py4D_browser" -version = "1.2.0" +version = "1.2.1" authors = [ { name="Steven Zeltmann", email="steven.zeltmann@lbl.gov" }, ] diff --git a/src/py4D_browser/main_window.py b/src/py4D_browser/main_window.py index 2bbd098..df13f24 100644 --- a/src/py4D_browser/main_window.py +++ b/src/py4D_browser/main_window.py @@ -397,6 +397,18 @@ def setup_menus(self): detector_mode_group.addAction(detector_CoM) self.detector_menu.addAction(detector_CoM) + detector_CoMx = QAction("CoM &X", self) + detector_CoMx.setCheckable(True) + detector_CoMx.triggered.connect(partial(self.update_real_space_view, True)) + detector_mode_group.addAction(detector_CoMx) + self.detector_menu.addAction(detector_CoMx) + + detector_CoMy = QAction("CoM &Y", self) + detector_CoMy.setCheckable(True) + detector_CoMy.triggered.connect(partial(self.update_real_space_view, True)) + detector_mode_group.addAction(detector_CoMy) + self.detector_menu.addAction(detector_CoMy) + detector_iCoM = QAction("i&CoM", self) detector_iCoM.setCheckable(True) detector_iCoM.triggered.connect(partial(self.update_real_space_view, True)) @@ -552,12 +564,7 @@ def setup_views(self): self.diffraction_space_widget.setMouseTracking(True) # Create virtual detector ROI selector - self.virtual_detector_point = pg_point_roi( - self.diffraction_space_widget.getView() - ) - self.virtual_detector_point.sigRegionChanged.connect( - partial(self.update_real_space_view, False) - ) + self.update_diffraction_detector() # Scalebar self.diffraction_scale_bar = ScaleBar(pixel_size=1, units="px", width=10) @@ -574,10 +581,7 @@ def setup_views(self): self.real_space_widget.setImage(np.zeros((512, 512))) # Add point selector connected to displayed diffraction pattern - self.real_space_point_selector = pg_point_roi(self.real_space_widget.getView()) - self.real_space_point_selector.sigRegionChanged.connect( - partial(self.update_diffraction_space_view, False) - ) + self.update_realspace_detector() # Scalebar, None by default self.real_space_scale_bar = ScaleBar(pixel_size=1, units="px", width=10) diff --git a/src/py4D_browser/update_views.py b/src/py4D_browser/update_views.py index b494434..d5ae51a 100644 --- a/src/py4D_browser/update_views.py +++ b/src/py4D_browser/update_views.py @@ -29,6 +29,8 @@ def update_real_space_view(self, reset=False): "Integrating", "Maximum", "CoM", + "CoM X", + "CoM Y", "iCoM", ], detector_mode @@ -156,6 +158,10 @@ def update_real_space_view(self, reset=False): if detector_mode == "CoM": vimg = CoMx + 1.0j * CoMy + elif detector_mode == "CoM X": + vimg = CoMx + elif detector_mode == "CoM Y": + vimg = CoMy elif detector_mode == "iCoM": dpc = py4DSTEM.process.phase.DPC(verbose=False) dpc.preprocess( @@ -402,11 +408,12 @@ def update_realspace_detector(self): assert detector_shape in ["Point", "Rectangular"], detector_shape if self.datacube is None: - return - - x, y = self.datacube.data.shape[:2] - x0, y0 = x // 2, y // 2 - xr, yr = x / 10, y / 10 + x0, y0 = 0, 0 + xr, yr = 4, 4 + else: + x, y = self.datacube.data.shape[2:] + x0, y0 = x // 2, y // 2 + xr, yr = x / 10, y / 10 # Remove existing detector if hasattr(self, "real_space_point_selector"): @@ -447,12 +454,18 @@ def update_diffraction_detector(self): detector_shape = self.detector_shape_group.checkedAction().text().strip("&") assert detector_shape in ["Point", "Rectangular", "Circle", "Annulus"] - if self.datacube is None: - return + main_pen = {"color": "g", "width": 6} + handle_pen = {"color": "r", "width": 9} + hover_pen = {"color": "c", "width": 6} + hover_handle = {"color": "c", "width": 9} - x, y = self.datacube.data.shape[2:] - x0, y0 = x // 2, y // 2 - xr, yr = x / 10, y / 10 + if self.datacube is None: + x0, y0 = 0, 0 + xr, yr = 4, 4 + else: + x, y = self.datacube.data.shape[2:] + x0, y0 = x // 2, y // 2 + xr, yr = x / 10, y / 10 # Remove existing detector if hasattr(self, "virtual_detector_point"): @@ -479,6 +492,8 @@ def update_diffraction_detector(self): self.virtual_detector_point = pg_point_roi( self.diffraction_space_widget.getView(), center=(x0 - 0.5, y0 - 0.5), + pen=main_pen, + hoverPen=hover_pen, ) self.virtual_detector_point.sigRegionChanged.connect( partial(self.update_real_space_view, False) @@ -487,7 +502,12 @@ def update_diffraction_detector(self): # Rectangular detector elif detector_shape == "Rectangular": self.virtual_detector_roi = pg.RectROI( - [int(x0 - xr / 2), int(y0 - yr / 2)], [int(xr), int(yr)], pen=(3, 9) + [int(x0 - xr / 2), int(y0 - yr / 2)], + [int(xr), int(yr)], + pen=main_pen, + handlePen=handle_pen, + hoverPen=hover_pen, + handleHoverPen=hover_handle, ) self.diffraction_space_widget.getView().addItem(self.virtual_detector_roi) self.virtual_detector_roi.sigRegionChangeFinished.connect( @@ -497,7 +517,12 @@ def update_diffraction_detector(self): # Circular detector elif detector_shape == "Circle": self.virtual_detector_roi = pg.CircleROI( - [int(x0 - xr / 2), int(y0 - yr / 2)], [int(xr), int(yr)], pen=(3, 9) + [int(x0 - xr / 2), int(y0 - yr / 2)], + [int(xr), int(yr)], + pen=main_pen, + handlePen=handle_pen, + hoverPen=hover_pen, + handleHoverPen=hover_handle, ) self.diffraction_space_widget.getView().addItem(self.virtual_detector_roi) self.virtual_detector_roi.sigRegionChangeFinished.connect( @@ -508,7 +533,12 @@ def update_diffraction_detector(self): elif detector_shape == "Annulus": # Make outer detector self.virtual_detector_roi_outer = pg.CircleROI( - [int(x0 - xr), int(y0 - yr)], [int(2 * xr), int(2 * yr)], pen=(3, 9) + [int(x0 - xr), int(y0 - yr)], + [int(2 * xr), int(2 * yr)], + pen=main_pen, + handlePen=handle_pen, + hoverPen=hover_pen, + handleHoverPen=hover_handle, ) self.diffraction_space_widget.getView().addItem(self.virtual_detector_roi_outer) @@ -516,7 +546,10 @@ def update_diffraction_detector(self): self.virtual_detector_roi_inner = pg.CircleROI( [int(x0 - xr / 2), int(y0 - yr / 2)], [int(xr), int(yr)], - pen=(4, 9), + pen=main_pen, + hoverPen=hover_pen, + handlePen=handle_pen, + handleHoverPen=hover_handle, movable=False, ) self.diffraction_space_widget.getView().addItem(self.virtual_detector_roi_inner) diff --git a/src/py4D_browser/utils.py b/src/py4D_browser/utils.py index 5ac1058..b27ad7f 100644 --- a/src/py4D_browser/utils.py +++ b/src/py4D_browser/utils.py @@ -66,12 +66,12 @@ def on_click(self, *args): self.status_bar.showMessage("Shift+click to keep on", 5_000) -def pg_point_roi(view_box, center=(-0.5, -0.5)): +def pg_point_roi(view_box, center=(-0.5, -0.5), pen=(0, 9), hoverPen=None): """ Point selection. Based in pyqtgraph, and returns a pyqtgraph CircleROI object. This object has a sigRegionChanged.connect() signal method to connect to other functions. """ - circ_roi = pg.CircleROI(center, (2, 2), movable=True, pen=(0, 9)) + circ_roi = pg.CircleROI(center, (2, 2), movable=True, pen=pen, hoverPen=hoverPen) h = circ_roi.addTranslateHandle((0.5, 0.5)) h.pen = pg.mkPen("r") h.update() From fcd1c826366d11eb81d8f96c164297e914517703 Mon Sep 17 00:00:00 2001 From: Steven Zeltmann Date: Mon, 2 Dec 2024 13:31:27 -0500 Subject: [PATCH 2/5] fix HDF5 export --- src/py4D_browser/menu_actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/py4D_browser/menu_actions.py b/src/py4D_browser/menu_actions.py index d7e4eaa..74a6ce4 100644 --- a/src/py4D_browser/menu_actions.py +++ b/src/py4D_browser/menu_actions.py @@ -196,7 +196,7 @@ def export_datacube(self, save_format: str): py4DSTEM.save(filename, self.datacube, mode="o") elif save_format == "Plain HDF5": - with h5py.File(filename, "o") as f: + with h5py.File(filename, "w") as f: f["array"] = self.datacube.data From bfeccaa3d1f8d2a54ccdf0be79991b8ffd5e88af Mon Sep 17 00:00:00 2001 From: Steven Zeltmann Date: Mon, 2 Dec 2024 13:48:07 -0500 Subject: [PATCH 3/5] fix incorrect slice calculation for rectangular realspace detector in non-square scan --- src/py4D_browser/update_views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/py4D_browser/update_views.py b/src/py4D_browser/update_views.py index d5ae51a..551142e 100644 --- a/src/py4D_browser/update_views.py +++ b/src/py4D_browser/update_views.py @@ -319,7 +319,7 @@ def update_diffraction_space_view(self, reset=False): elif detector_shape == "Rectangular": # Get slices corresponding to ROI slices, _ = self.real_space_rect_selector.getArraySlice( - np.zeros((self.datacube.Rshape)), self.real_space_widget.getImageItem() + np.zeros((self.datacube.Rshape)).T, self.real_space_widget.getImageItem() ) slice_y, slice_x = slices @@ -656,8 +656,8 @@ def update_tooltip(self): if scene.getView().rect().contains(pos_in_scene): pos_in_data = scene.view.mapSceneToView(pos_in_scene) - y = int(np.clip(np.floor(pos_in_data.x()), 0, data.shape[0] - 1)) - x = int(np.clip(np.floor(pos_in_data.y()), 0, data.shape[1] - 1)) + y = int(np.clip(np.floor(pos_in_data.x()), 0, data.shape[1] - 1)) + x = int(np.clip(np.floor(pos_in_data.y()), 0, data.shape[0] - 1)) if np.isrealobj(data): display_text = f"[{x},{y}]: {data[x,y]:.5g}" From cc582a40da94312850dcaa2cb223068d508f471e Mon Sep 17 00:00:00 2001 From: Steven Zeltmann Date: Mon, 2 Dec 2024 13:52:39 -0500 Subject: [PATCH 4/5] fix slice calculation for rectangular diffraction pattern --- src/py4D_browser/update_views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/py4D_browser/update_views.py b/src/py4D_browser/update_views.py index 551142e..6cc662e 100644 --- a/src/py4D_browser/update_views.py +++ b/src/py4D_browser/update_views.py @@ -52,7 +52,8 @@ def update_real_space_view(self, reset=False): if detector_shape == "Rectangular": # Get slices corresponding to ROI slices, transforms = self.virtual_detector_roi.getArraySlice( - self.datacube.data[0, 0, :, :], self.diffraction_space_widget.getImageItem() + self.datacube.data[0, 0, :, :].T, + self.diffraction_space_widget.getImageItem(), ) slice_y, slice_x = slices From 1d8795529e282beae48da24a8b229a7d7c86058f Mon Sep 17 00:00:00 2001 From: Steven Zeltmann Date: Mon, 2 Dec 2024 14:15:31 -0500 Subject: [PATCH 5/5] position and styling of all detectors implemented --- src/py4D_browser/update_views.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/py4D_browser/update_views.py b/src/py4D_browser/update_views.py index 6cc662e..0dc256a 100644 --- a/src/py4D_browser/update_views.py +++ b/src/py4D_browser/update_views.py @@ -408,13 +408,18 @@ def update_realspace_detector(self): ) assert detector_shape in ["Point", "Rectangular"], detector_shape + main_pen = {"color": "g", "width": 6} + handle_pen = {"color": "r", "width": 9} + hover_pen = {"color": "c", "width": 6} + hover_handle = {"color": "c", "width": 9} + if self.datacube is None: x0, y0 = 0, 0 xr, yr = 4, 4 else: x, y = self.datacube.data.shape[2:] - x0, y0 = x // 2, y // 2 - xr, yr = x / 10, y / 10 + y0, x0 = x // 2, y // 2 + xr, yr = (np.minimum(x, y) / 10,) * 2 # Remove existing detector if hasattr(self, "real_space_point_selector"): @@ -424,19 +429,27 @@ def update_realspace_detector(self): self.real_space_widget.view.scene().removeItem(self.real_space_rect_selector) self.real_space_rect_selector = None - # Rectangular detector + # Point detector if detector_shape == "Point": self.real_space_point_selector = pg_point_roi( self.real_space_widget.getView(), center=(x0 - 0.5, y0 - 0.5), + pen=main_pen, + hoverPen=hover_pen, ) self.real_space_point_selector.sigRegionChanged.connect( partial(self.update_diffraction_space_view, False) ) + # Rectangular detector elif detector_shape == "Rectangular": self.real_space_rect_selector = pg.RectROI( - [int(x0 - xr / 2), int(y0 - yr / 2)], [int(xr), int(yr)], pen=(3, 9) + [int(x0 - xr / 2), int(y0 - yr / 2)], + [int(xr), int(yr)], + pen=main_pen, + handlePen=handle_pen, + hoverPen=hover_pen, + handleHoverPen=hover_handle, ) self.real_space_widget.getView().addItem(self.real_space_rect_selector) self.real_space_rect_selector.sigRegionChangeFinished.connect( @@ -465,8 +478,8 @@ def update_diffraction_detector(self): xr, yr = 4, 4 else: x, y = self.datacube.data.shape[2:] - x0, y0 = x // 2, y // 2 - xr, yr = x / 10, y / 10 + y0, x0 = x // 2, y // 2 + xr, yr = (np.minimum(x, y) / 10,) * 2 # Remove existing detector if hasattr(self, "virtual_detector_point"):