Skip to content

Commit

Permalink
before merge j-dev 20250116
Browse files Browse the repository at this point in the history
  • Loading branch information
cudmore committed Jan 16, 2025
1 parent 60facf1 commit 4a49711
Show file tree
Hide file tree
Showing 11 changed files with 306 additions and 162 deletions.
13 changes: 13 additions & 0 deletions src/pymapmanager/interface2/core/search_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,19 @@ def data(self, index, role) -> str:
Returns the data stored under the given role for the item referred to by the index. (in str form)
"""

# abb adding segment color
if role == Qt.BackgroundRole:
row = index.row()
col = index.column()
# Get the corresponding row within the actual dataframe
# They could be different values due to deleting
rowLabel = self._data.index.tolist()[row]
# logger.warning(f'row:{row} rowLabel:{rowLabel}')
if col==0 and rowLabel == 3:
_color = QtGui.QColor('#38ff2a')
return QtGui.QBrush(_color)

# print("data", self.rowCount(None))
# print("role: ", type(role))
if role == Qt.DisplayRole:
Expand Down
44 changes: 29 additions & 15 deletions src/pymapmanager/interface2/mainMenus.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
from qtpy import QtWidgets

from pymapmanager._logger import logger, setLogLevel
from pymapmanager.interface2.stackWidgets import stackWidget2
from pymapmanager.interface2.mapWidgets import mapWidget
# from pymapmanager.interface2.stackWidgets import stackWidget2
from pymapmanager.interface2.stackWidgets.base.mmWidget2 import mmWidget2
from pymapmanager.interface2.mapWidgets.mapWidget import mapWidget

class PyMapManagerMenus:
"""Main app menus including loaded map and stack widgets.
Expand Down Expand Up @@ -304,7 +305,7 @@ def _refreshEditMenu(self):

# from pymapmanager.interface2.stackWidgets import stackWidget2
frontWindow = self.getApp().getFrontWindow()
if isinstance(frontWindow, (stackWidget2, mapWidget)):
if isinstance(frontWindow, (mmWidget2, mapWidget)):
nextUndo = frontWindow.getUndoRedo().nextUndoStr()
nextRedo = frontWindow.getUndoRedo().nextRedoStr()
enableUndo = frontWindow.getUndoRedo().numUndo() > 0
Expand Down Expand Up @@ -418,32 +419,33 @@ def _refreshFileMenu(self):
# self.fileMenu.addAction(loadFolderAction)
# self.fileMenu.addSeparator()

# abj
enableUndo = False
enableRedo = False
isDirty = False
# enableUndo = False
# enableRedo = False
enableSave = False

frontWindow = self.getApp().getFrontWindow()

if isinstance(frontWindow, stackWidget2):
enableUndo = frontWindow.getUndoRedo().numUndo() > 0
enableRedo = frontWindow.getUndoRedo().numRedo() > 0
isDirty = frontWindow.getDirty()
logger.info(f"isDirty: {isDirty}")
if isinstance(frontWindow, (mmWidget2, mapWidget)):
if frontWindow.getPath().endswith('.mmap.zip'):
enableSave = False
else:
# enableUndo = frontWindow.getUndoRedo().numUndo() > 0
# enableRedo = frontWindow.getUndoRedo().numRedo() > 0
enableSave = frontWindow.getDirty()

# save
saveFileAction = QtWidgets.QAction("Save", self.getApp())
saveFileAction.setCheckable(False) # setChecked is True by default?
saveFileAction.setShortcut("Ctrl+S")
# saveFileAction.setEnabled(enableUndo and isDirty)
saveFileAction.setEnabled(isDirty)
saveFileAction.setEnabled(enableSave)
saveFileAction.triggered.connect(self.getApp().saveFile)
self.fileMenu.addAction(saveFileAction)

# save as
saveAsFileAction = QtWidgets.QAction("Save As", self.getApp())
saveAsFileAction.setCheckable(False) # setChecked is True by default?
saveAsFileAction.triggered.connect(self.getApp().saveAsFile)
saveAsFileAction.triggered.connect(self.getApp().saveAs)
self.fileMenu.addAction(saveAsFileAction)

self.fileMenu.addSeparator()
Expand All @@ -463,16 +465,28 @@ def _refreshFileMenu(self):
self.settingsMenu.aboutToShow.connect(self._refreshSettingsMenu)
self.fileMenu.addSeparator()

#abj
analysisParametersAction = QtWidgets.QAction('App Analysis Parameters', self.getApp())
analysisParametersAction.triggered.connect(self.getApp()._showAnalysisParameters)
self.fileMenu.addAction(analysisParametersAction)

self.fileMenu.addSeparator()
importNewTIFAction = QtWidgets.QAction('Import new TIF (channel)', self.getApp())
importNewTIFAction.setEnabled(isinstance(frontWindow, mmWidget2))
importNewTIFAction.triggered.connect(self.getApp().importNewTIF)
self.fileMenu.addAction(importNewTIFAction)

# open some mapmanagercore sample data (download and store locally with pooch)
self.fileMenu.addSeparator()
self.sampleDataMenu = QtWidgets.QMenu("Sample Data ...")
importList = ['Tiff File Ch1', 'Tiff File Ch2', 'mmap with spines and segments']
for importType in importList:
loadSampleAction = QtWidgets.QAction(importType, self.getApp())
loadSampleAction.triggered.connect(
partial(self.getApp().loadSampleData, importType)
)
self.sampleDataMenu.addAction(loadSampleAction)
self.fileMenu.addMenu(self.sampleDataMenu)

def _refreshOpenRecent(self):
"""Dynamically generate the open recent stack/map menu.
Expand Down
43 changes: 40 additions & 3 deletions src/pymapmanager/interface2/mapWidgets/mapTableWidget.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import os
import sys
from typing import List, Union # , Callable, Iterator, Optional

from qtpy import QtGui, QtCore, QtWidgets

from mapmanagercore import IMPORT_FILE_EXTENSIONS
from pymapmanager.interface2.core.search_widget import myQTableView

from pymapmanager.timeseriesCore import TimeSeriesCore

from pymapmanager._logger import logger

class mapTableWidget(QtWidgets.QWidget):
Expand All @@ -29,7 +29,44 @@ def __init__(self, timeSeriesCore : TimeSeriesCore):

self.slot_switchMap(timeSeriesCore)

# self._setModel()
self.setAcceptDrops(True)

def dragEnterEvent(self, event):
"""Accept drag/drop of tiff file and append to _timeSeriesCore.
"""
if event.mimeData().hasUrls():
urlList = event.mimeData().urls()
url = urlList[0]
file_path = url.toLocalFile()
_path, _ext = os.path.splitext(file_path)
if _ext in IMPORT_FILE_EXTENSIONS:
event.acceptProposedAction()
else:
logger.warning(f'did not understand ext "{_ext}" path "{_path}"')

def dropEvent(self, event):
"""When user drops a tiff file, append it to the timeseries.
"""
urlList = event.mimeData().urls()
url = urlList[0]
file_path = url.toLocalFile()
# self.label.setText(f"Dropped file: {file_path}")
# self._timeSeriesCore.loadInNewChannel(file_path) # todo specify append (default to tp 0)
# append the tiff file to the _timeSeriesCore
logger.info(f'file_path:{file_path}')
timePoint = self._timeSeriesCore.numSessions
channel = 0
name = 'xxx imported channel'

logger.info('adding new timepoint from tif, timePoint:{timePoint}')

from mapmanagercore.lazy_geo_pd_images.loader.imageio import MultiImageLoader
_loader = MultiImageLoader()
_loader.read(file_path, time=timePoint, channel=channel, name=name)
self._timeSeriesCore._fullMap.loader.merge(_loader)

# update gui
self._setModel()

def slot_switchMap(self, timeSeriesCore : TimeSeriesCore):
self._timeSeriesCore = timeSeriesCore
Expand Down
15 changes: 12 additions & 3 deletions src/pymapmanager/interface2/mapWidgets/mapWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ class mapWidget(MainWindow):

_widgetName = 'Map Widget'

# def __init__(self, mmMap : pmm.mmMap):
def __init__(self, timeseriescore : TimeSeriesCore):
super().__init__(mapWidget=self, iAmMapWidget=True)

Expand All @@ -77,8 +76,10 @@ def __init__(self, timeseriescore : TimeSeriesCore):

self.setWindowTitle(os.path.split(self._map.path)[1])

# def getMapSelection(self) -> MapSelection:
# return self._mapSelection
# over-ride from mmWIdget2, which returns stack
# using this to share some api like save and save as
def getStack(self) -> TimeSeriesCore:
return self._map

def zoomToPointAnnotation(self,
idx : int,
Expand All @@ -102,6 +103,10 @@ def getMap(self):
def getPath(self):
return self._map.path

# over-ride mmWidget2
def getDirty(self):
return self._map.getDirty()

def emitUndoEvent(self):
"""
"""
Expand Down Expand Up @@ -186,6 +191,10 @@ def openStackRun(self,
if spineID and _multipleTp:
self.linkOpenPlots(link=True)

@property
def numSessions(self):
return self.getMap().numSessions

def getNumSessions(self):
return self.getMap().numSessions

Expand Down
68 changes: 52 additions & 16 deletions src/pymapmanager/interface2/pyMapManagerApp2.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,14 @@ def _importPlugins(pluginType : str, verbose = False):
elif pluginType == 'mapWidgets':
_modulePath = 'pymapmanager.interface2.mapWidgets'
# _folderPath = 'mapWidgets'

else:
logger.error(f'did not understand pluginType:"{pluginType}" expecting one of (stackWidgets, mapWidgets)')
return

numAdded = 0

# logger.warning(f'verbose:{verbose}')

# CRITICAL: abb this list is not complete in pyinstaller
invalidate_caches() # ???
m1 = import_module(_modulePath)
Expand Down Expand Up @@ -149,7 +154,7 @@ def loadPlugins(pluginType : str, verbose = False) -> dict:

pluginDict = {}

_importPlugins(pluginType, pluginType == 'stackWidgets')
_importPlugins(pluginType)

if pluginType == 'stackWidgets':
members = inspect.getmembers(pymapmanager.interface2.stackWidgets)
Expand Down Expand Up @@ -336,17 +341,23 @@ def updateMapPathDict(self, aWidget):
# self.pathDict["lastSaveTime"] = self._timeSeriesCore.getLastSaveTime()

path = aWidget.getPath()
refreshTimeSeriesCore = TimeSeriesCore(path)
# lastSaveTime = aWidget.getLastSaveTime() # still showing old one, need to create new time series core to refresh?
lastSaveTime = refreshTimeSeriesCore.getLastSaveTime()
numTimepoints = refreshTimeSeriesCore.numSessions

# abb why are we making TimeSeriesCore
logger.error('do not create TimeSeriesCore')

lastSaveTime = aWidget.getLastSaveTime() # still showing old one, need to create new time series core to refresh?
numTimepoints = aWidget.numSessions

# refreshTimeSeriesCore = TimeSeriesCore(path)
# lastSaveTime = refreshTimeSeriesCore.getLastSaveTime()
# numTimepoints = refreshTimeSeriesCore.numSessions
pathDict = {"Path": path,
"Last Save Time": str(lastSaveTime), # needs to be updated
"Timepoints": str(numTimepoints)}
self._app.getConfigDict().addMapPathDict(pathDict)

def save(self, aWidget):
# logger.info(f'TODO: save widget: {aWidget}')
# abb only stackwidget2 has save(), e.g. map widgets do not
logger.info(f'save widget: {aWidget}')
aWidget.save()

Expand All @@ -356,10 +367,11 @@ def save(self, aWidget):
# self.pathDict["lastSaveTime"] = lastSaveTime

def saveAs(self, aWidget):
# logger.info(f'TODO: save as widget: {aWidget}')
# abb only stackwidget2 has fileSaveAs(), e.g. map widgets do not
logger.info(f'save as widget: {aWidget}')
aWidget.fileSaveAs()
self.updateMapPathDict(aWidget) # abj
_saved = aWidget.saveAs()
if _saved:
self.updateMapPathDict(aWidget) # abj

def _checkWidgetExists(self, path):
""" Check if a widget exists in the widget dict list
Expand Down Expand Up @@ -587,7 +599,7 @@ def saveFile(self):
_frontWidget = self.getFrontWindow()
self._openWidgetList.save(_frontWidget)

def saveAsFile(self):
def saveAs(self):
""" Save as a new file
"""
_frontWidget = self.getFrontWindow()
Expand Down Expand Up @@ -666,6 +678,28 @@ def getScreenGrid(self, numItems : int, itemsPerRow : int) -> List[List[int]]:
def getOpenWidgetDict(self):
return self._openWidgetList.getDict()

def loadSampleData(self, sampleName):
if sampleName == 'Tiff File Ch1':
import mapmanagercore.data
from mapmanagercore.data import getTiffChannel_1
tiffPath = getTiffChannel_1()
if os.path.isfile(tiffPath):
self.loadStackWidget(tiffPath)
elif sampleName == 'Tiff File Ch2':
import mapmanagercore.data
from mapmanagercore.data import getTiffChannel_2
tiffPath = getTiffChannel_2()
if os.path.isfile(tiffPath):
self.loadStackWidget(tiffPath)
elif sampleName == 'mmap with spines and segments':
import mapmanagercore.data
from mapmanagercore.data import getSingleTimepointMap
tiffPath = getSingleTimepointMap()
if os.path.isfile(tiffPath):
self.loadStackWidget(tiffPath)
else:
logger.warning(f'did not understand "{sampleName}"')

def loadStackWidget(self, path : str = None) -> Union[stackWidget2, mapWidget]:
"""Load a stack from a path and open a stackWidget2 or mapWidget
Expand All @@ -690,7 +724,7 @@ def loadStackWidget(self, path : str = None) -> Union[stackWidget2, mapWidget]:

dialog = QtWidgets.QFileDialog(None)
# dialog.setFileMode(QtWidgets.QFileDialog.Directory)
dialog.setNameFilter("zarr directory (*.mmap)")
dialog.setNameFilter("MapManager Files (*.mmap, *.zip)")
# openFilePath = dialog.getExistingDirectory(None)
# dialog.setOptions(options)
openFilePath = dialog.getExistingDirectory()
Expand All @@ -701,11 +735,11 @@ def loadStackWidget(self, path : str = None) -> Union[stackWidget2, mapWidget]:
_ext = os.path.splitext(openFilePath)[1]
window = self.activeWindow()
if openFilePath == "":
logger.warning("openFilePath is Empty")
# logger.warning("openFilePath is Empty")
# QtWidgets.QMessageBox.critical(window, "Error", "File Path is Empty")
return
elif _ext != '.mmap': # could make this into a for loop until user inputs .mmap
logger.warning(f"incorrect directory type, must be of extension: (.mmap)")
elif _ext not in ['.mmap', '.zip']: # could make this into a for loop until user inputs .mmap
logger.warning(f"incorrect directory type, must be of extension: .mmap or .zip")
QtWidgets.QMessageBox.critical(window, "Error", "Incorrect directory type, must be of extension: (.mmap)")
return

Expand Down Expand Up @@ -804,9 +838,11 @@ def clearRecentFiles(self):
self._openFirstWindow.refreshUI()

def importNewTIF(self):
pass

frontStackWindow = self.getFrontWindow()
if not isinstance(frontStackWindow, stackWidget2):
return

# bring up directory

# get newTifPath
Expand Down
4 changes: 2 additions & 2 deletions src/pymapmanager/interface2/runInterfaceBob.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def run():
app = PyMapManagerApp(sys.argv)

# open a single timepoint map with segments and spines
path = mapmanagercore.data.getSingleTimepointMap()
# path = mapmanagercore.data.getSingleTimepointMap()

# a single timepoint tif file (import)
# path = mapmanagercore.data.getTiffChannel_1()
Expand All @@ -33,7 +33,7 @@ def run():

# a mmap with multiple timepoints, connects segments and spines
# path = '/Users/cudmore/Desktop/multi_timepoint_map_seg_spine_connected.mmap'
# path = mapmanagercore.data.getMultiTimepointMap()
path = mapmanagercore.data.getMultiTimepointMap()

# path = '/Users/cudmore/Desktop/yyy4.mmap'
# path = '/Users/cudmore/Desktop/single_timepoint.mmap'
Expand Down
Loading

0 comments on commit 4a49711

Please sign in to comment.