From 8090e272fbaab1accc20fa384646507417e4566f Mon Sep 17 00:00:00 2001 From: Pat Gunn Date: Wed, 22 Jan 2025 12:03:55 -0500 Subject: [PATCH 1/6] Addresses #1454 using pyside6 --- caiman/source_extraction/volpy/volpy_gui.py | 44 ++++++++++----------- environment.yml | 1 + 2 files changed, 22 insertions(+), 23 deletions(-) mode change 100644 => 100755 caiman/source_extraction/volpy/volpy_gui.py diff --git a/caiman/source_extraction/volpy/volpy_gui.py b/caiman/source_extraction/volpy/volpy_gui.py old mode 100644 new mode 100755 index ae333dfc1..d70c8ace8 --- a/caiman/source_extraction/volpy/volpy_gui.py +++ b/caiman/source_extraction/volpy/volpy_gui.py @@ -15,18 +15,19 @@ from pyqtgraph import FileDialog from pyqtgraph.Qt import QtGui from pyqtgraph.parametertree import Parameter, ParameterTree -import PyQt5 -from PyQt5 import QtWidgets -from PyQt5.QtWidgets import QApplication, QShortcut +import PySide6 +from PySide6 import QtWidgets +from PySide6.QtWidgets import QApplication +from PySide6.QtGui import QShortcut import random from skimage.draw import polygon import sys import caiman as cm from caiman.external.cell_magic_wand import cell_magic_wand_single_point - + os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = os.fspath( - Path(PyQt5.__file__).resolve().parent / "Qt5" / "plugins") + Path(PySide6.__file__).resolve().parent / "Qt5" / "plugins") def mouseClickEvent(event): @@ -70,7 +71,7 @@ def mouseClickEvent(event): roi = roi * 1. except: pass - + elif mode == 'CHOOSE NEURONS': pos = img.mapFromScene(event.pos()) p1.clear() @@ -104,7 +105,7 @@ def add(): roi = np.zeros((dims[1], dims[0])) ff = np.array(polygon(np.array(pts)[:,0], np.array(pts)[:,1])) roi[ff[1],[ff[0]]] = 1 - + if len(pts) > 2 : flag = True while flag: @@ -187,7 +188,7 @@ def load_rois(): if (l_ROIs.shape[2], l_ROIs.shape[1]) != dims: print(dims);print(l_ROIs.shape[1:]) raise ValueError('Dimentions of movie and rois do not accord') - + for roi in l_ROIs: flag = True while flag: @@ -213,7 +214,7 @@ def save(): print(ffll[0]) save_ROIs = np.array(list(all_ROIs.values())).copy() save_ROIs = np.flip(save_ROIs, axis=1) - + if os.path.splitext(ffll[0])[1] == '.hdf5': cm.movie(save_ROIs).save(ffll[0]) summary_images = summary_images.transpose([0, 2, 1]) @@ -294,10 +295,10 @@ def overlay(all_ROIs): if __name__ == "__main__": ## Always start by initializing Qt (only once per application) app = QApplication(sys.argv) - + ## Define a top-level widget to hold everything w = QtWidgets.QWidget() - + ## Create some widgets to be placed inside hist = pg.HistogramLUTItem() # Contrast/color control win = pg.GraphicsLayoutWidget() @@ -307,11 +308,11 @@ def overlay(all_ROIs): p1 = pg.PlotWidget() neuron_action = ParameterTree() neuron_list = QtWidgets.QListWidget() - + ## Create a grid layout to manage the widgets size and position layout = QtWidgets.QGridLayout() w.setLayout(layout) - + ## Add widgets to the layout in their proper positions layout.addWidget(win, 0, 1) layout.addWidget(p1, 0, 2) @@ -320,7 +321,7 @@ def overlay(all_ROIs): img = pg.ImageItem() p1.addItem(img) hist.setImageItem(img) - + # Add actions params_action = [{'name': 'LOAD DATA', 'type':'action'}, {'name': 'LOAD ROIS', 'type':'action'}, @@ -335,12 +336,12 @@ def overlay(all_ROIs): {'name': 'MAGIC WAND PARAMS', 'type': 'group', 'children': [{'name': 'MIN RADIUS', 'type': 'int', 'value': 4}, {'name': 'MAX RADIUS', 'type': 'int', 'value': 10}, {'name': 'ROUGHNESS', 'type': 'int', 'value': 1}]}] - + pars_action = Parameter.create(name='params_action', type='group', children=params_action) neuron_action.setParameters(pars_action, showTop=False) neuron_action.setWindowTitle('Parameter Action') mode = pars_action.getValues()['MODE'][0] - + # Add event p1.mousePressEvent = mouseClickEvent p1.mouseReleaseEvent = release @@ -358,7 +359,7 @@ def overlay(all_ROIs): shortcut_up = QShortcut(QtGui.QKeySequence("up"), w) shortcut_up.activated.connect(up) neuron_list.itemClicked.connect(show_neuron) - + # Create dictionary for saving F = FileDialog() all_pts = {} @@ -366,12 +367,9 @@ def overlay(all_ROIs): all_ROIs = {} pts = [] pen = pg.mkPen(color=(255, 255, 0), width=4)#, style=Qt.DashDotLine) - + ## Display the widget as a new window w.show() - + ## Start the Qt event loop - app.exec_() - - - + app.exec() diff --git a/environment.yml b/environment.yml index 014c3532c..eaa692baf 100644 --- a/environment.yml +++ b/environment.yml @@ -27,6 +27,7 @@ dependencies: - psutil - pynwb - pyqtgraph +- pyside6 - scikit-image >=0.19.0 - scikit-learn >=1.2 - scipy >= 1.10.1 From db6ddac55b736f5f9dda8db976857a95ba84afcb Mon Sep 17 00:00:00 2001 From: Pat Gunn Date: Wed, 22 Jan 2025 15:13:38 -0500 Subject: [PATCH 2/6] volpy_gui: change import errors based on a stackoverflow suggestion --- caiman/source_extraction/volpy/volpy_gui.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/caiman/source_extraction/volpy/volpy_gui.py b/caiman/source_extraction/volpy/volpy_gui.py index d70c8ace8..8ba5e4545 100755 --- a/caiman/source_extraction/volpy/volpy_gui.py +++ b/caiman/source_extraction/volpy/volpy_gui.py @@ -6,6 +6,13 @@ and spike extraction step of VolPy. @author: @caichangjia """ + +# These imports apparently must come before importing pyqtgraph on some platforms +import PySide6 +from PySide6 import QtWidgets +from PySide6.QtWidgets import QApplication +from PySide6.QtGui import QShortcut + import cv2 import h5py import numpy as np @@ -15,10 +22,6 @@ from pyqtgraph import FileDialog from pyqtgraph.Qt import QtGui from pyqtgraph.parametertree import Parameter, ParameterTree -import PySide6 -from PySide6 import QtWidgets -from PySide6.QtWidgets import QApplication -from PySide6.QtGui import QShortcut import random from skimage.draw import polygon import sys From 96d8a063b2050b1bfe436ffe50c254b54bb7480f Mon Sep 17 00:00:00 2001 From: Pat Gunn Date: Fri, 24 Jan 2025 11:28:42 -0500 Subject: [PATCH 3/6] Add pyside6 to pyproject - so many places to update things! --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 96941901d..a92252752 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ dependencies = [ "pims", "psutil", "pynwb", + "pyside6", "scikit-image", "scikit-learn", "scipy", From d1236ec54b0e3df6f26b57597a73776b818d031c Mon Sep 17 00:00:00 2001 From: Pat Gunn Date: Mon, 27 Jan 2025 13:58:56 -0500 Subject: [PATCH 4/6] Add workaround to fix pyqtgraph issue 2380 --- caiman/source_extraction/volpy/volpy_gui.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/caiman/source_extraction/volpy/volpy_gui.py b/caiman/source_extraction/volpy/volpy_gui.py index 8ba5e4545..ddfeb82a7 100755 --- a/caiman/source_extraction/volpy/volpy_gui.py +++ b/caiman/source_extraction/volpy/volpy_gui.py @@ -297,6 +297,8 @@ def overlay(all_ROIs): if __name__ == "__main__": ## Always start by initializing Qt (only once per application) + if sys.platform == 'darwin': + PySide6.QtWidgets.QApplication.setStyle("fusion") app = QApplication(sys.argv) ## Define a top-level widget to hold everything From 66120c771c2653220b57b649c03a4d24033fc34f Mon Sep 17 00:00:00 2001 From: Pat Gunn Date: Mon, 27 Jan 2025 15:45:14 -0500 Subject: [PATCH 5/6] volpy_gui: use limits keyword, which makes the menu actually work --- caiman/source_extraction/volpy/volpy_gui.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/caiman/source_extraction/volpy/volpy_gui.py b/caiman/source_extraction/volpy/volpy_gui.py index ddfeb82a7..ec41f4163 100755 --- a/caiman/source_extraction/volpy/volpy_gui.py +++ b/caiman/source_extraction/volpy/volpy_gui.py @@ -328,16 +328,16 @@ def overlay(all_ROIs): hist.setImageItem(img) # Add actions - params_action = [{'name': 'LOAD DATA', 'type':'action'}, - {'name': 'LOAD ROIS', 'type':'action'}, - {'name': 'SAVE', 'type':'action'}, - {'name': 'ADD', 'type': 'action'}, - {'name': 'REMOVE', 'type': 'action'}, - {'name': 'SHOW ALL', 'type': 'action'}, - {'name': 'CLEAR', 'type': 'action'}, - {'name': 'IMAGES', 'type': 'list', 'values': ['MEAN','CORR']}, - {'name': 'DISPLAY', 'type': 'list', 'values': ['CONTOUR','SPATIAL FOOTPRINTS']}, - {'name': 'MODE', 'type': 'list', 'values': ['POLYGON','CELL MAGIC WAND', 'CHOOSE NEURONS']}, + params_action = [{'name': 'LOAD DATA', 'type': 'action'}, + {'name': 'LOAD ROIS', 'type': 'action'}, + {'name': 'SAVE', 'type': 'action'}, + {'name': 'ADD', 'type': 'action'}, + {'name': 'REMOVE', 'type': 'action'}, + {'name': 'SHOW ALL', 'type': 'action'}, + {'name': 'CLEAR', 'type': 'action'}, + {'name': 'IMAGES', 'type': 'list', 'limits': ['MEAN','CORR']}, + {'name': 'DISPLAY', 'type': 'list', 'limits': ['CONTOUR', 'SPATIAL FOOTPRINTS']}, + {'name': 'MODE', 'type': 'list', 'limits': ['POLYGON', 'CELL MAGIC WAND', 'CHOOSE NEURONS']}, {'name': 'MAGIC WAND PARAMS', 'type': 'group', 'children': [{'name': 'MIN RADIUS', 'type': 'int', 'value': 4}, {'name': 'MAX RADIUS', 'type': 'int', 'value': 10}, {'name': 'ROUGHNESS', 'type': 'int', 'value': 1}]}] From 6ef1051f27647afa90bdc29c47b64446b6c788b7 Mon Sep 17 00:00:00 2001 From: Pat Gunn Date: Mon, 27 Jan 2025 16:16:26 -0500 Subject: [PATCH 6/6] If we're going qt6, let's go qt6. Fix plugin path --- caiman/source_extraction/volpy/volpy_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/caiman/source_extraction/volpy/volpy_gui.py b/caiman/source_extraction/volpy/volpy_gui.py index ec41f4163..d8710de38 100755 --- a/caiman/source_extraction/volpy/volpy_gui.py +++ b/caiman/source_extraction/volpy/volpy_gui.py @@ -30,7 +30,7 @@ from caiman.external.cell_magic_wand import cell_magic_wand_single_point os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = os.fspath( - Path(PySide6.__file__).resolve().parent / "Qt5" / "plugins") + Path(PySide6.__file__).resolve().parent / "Qt6" / "plugins") def mouseClickEvent(event):