Skip to content

Commit

Permalink
Refactor Settings (#98)
Browse files Browse the repository at this point in the history
- Add BDP hooks for ptycho-enhanced XRF
- Implement numpy 2.0 fix
- Workflow to autoload product files as they appear
- Extract reconstructor API
- Clean up timers
- Fix crash when selecting zero-sized region with rectangle tool
- Refactor settings classes to use parametric classes
- Add tooltip support to parametric view builder
- Refactor Tike GUI to use parametric view controllers
- Reformat repository using ruff format
- Enable syncing product/scan/probe/object parameters to settings
- Make default product name configurable and validate renaming
  • Loading branch information
stevehenke authored Oct 8, 2024
1 parent d3ce8c2 commit bf5ab77
Show file tree
Hide file tree
Showing 250 changed files with 6,226 additions and 5,107 deletions.
31 changes: 30 additions & 1 deletion ptychodus/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ def main() -> int:
action='store_true',
help=argparse.SUPPRESS,
)
parser.add_argument(
'--fluorescence-input',
metavar='FLUORESCENCE_INPUT_FILE',
type=argparse.FileType('r'),
help=argparse.SUPPRESS,
)
parser.add_argument(
'--fluorescence-output',
metavar='FLUORESCENCE_OUTPUT_FILE',
type=argparse.FileType('w'),
help=argparse.SUPPRESS,
)
parser.add_argument(
'-i',
'--input',
Expand Down Expand Up @@ -88,7 +100,22 @@ def main() -> int:
action = parsedArgs.batch
inputFilePath = Path(parsedArgs.input.name)
outputFilePath = Path(parsedArgs.output.name)
return model.batchModeExecute(action, inputFilePath, outputFilePath)
fluorescenceInputFilePath: Path | None = None
fluorescenceOutputFilePath: Path | None = None

if parsedArgs.flourescence_input is not None:
fluorescenceInputFilePath = Path(parsedArgs.fluorescence_input.name)

if parsedArgs.flourescence_output is not None:
fluorescenceOutputFilePath = Path(parsedArgs.fluorescence_output.name)

return model.batchModeExecute(
action,
inputFilePath,
outputFilePath,
fluorescenceInputFilePath=fluorescenceInputFilePath,
fluorescenceOutputFilePath=fluorescenceOutputFilePath,
)

try:
from PyQt5.QtWidgets import QApplication
Expand All @@ -100,9 +127,11 @@ def main() -> int:
verifyAllArgumentsParsed(parser, app.arguments()[1:])

from ptychodus.view import ViewCore

view = ViewCore.createInstance(parsedArgs.dev)

from ptychodus.controller import ControllerCore

controller = ControllerCore(model, view)
controller.showMainWindow(versionString())

Expand Down
16 changes: 7 additions & 9 deletions ptychodus/api/fluorescence.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,35 @@ class FluorescenceDataset:

# TODO need to communicate association between element map pixels and scan order.
# integer-valued, same shape as counts_per_second
#scan_indexes: IntegerArray
# scan_indexes: IntegerArray


class FluorescenceFileReader(ABC):

@abstractmethod
def read(self, filePath: Path) -> FluorescenceDataset:
'''reads a fluorescence dataset from file'''
"""reads a fluorescence dataset from file"""
pass


class FluorescenceFileWriter(ABC):

@abstractmethod
def write(self, filePath: Path, dataset: FluorescenceDataset) -> None:
'''writes a fluorescence dataset to file'''
"""writes a fluorescence dataset to file"""
pass


class UpscalingStrategy(ABC):
'''Uses ptychography-corrected scan positions to remap element
concentrations from the regular scan grid to the upscaled grid'''
"""Uses ptychography-corrected scan positions to remap element
concentrations from the regular scan grid to the upscaled grid"""

@abstractmethod
def __call__(self, emap: ElementMap, product: Product) -> ElementMap:
pass


class DeconvolutionStrategy(ABC):
'''Deconvolves the kernel from the accumulated array to obtain the
resolution-enhanced element map'''
"""Deconvolves the kernel from the accumulated array to obtain the
resolution-enhanced element map"""

@abstractmethod
def __call__(self, emap: ElementMap, product: Product) -> ElementMap:
Expand Down
9 changes: 4 additions & 5 deletions ptychodus/api/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ class ImageExtent:

@property
def size(self) -> int:
'''returns the number of pixels in the image'''
"""returns the number of pixels in the image"""
return self.widthInPixels * self.heightInPixels

@property
def shape(self) -> tuple[int, int]:
'''returns the image shape (heightInPixels, widthInPixels) tuple'''
"""returns the image shape (heightInPixels, widthInPixels) tuple"""
return self.heightInPixels, self.widthInPixels

def __eq__(self, other: object) -> bool:
if isinstance(other, ImageExtent):
return (self.shape == other.shape)
return self.shape == other.shape

return False

Expand Down Expand Up @@ -92,7 +92,6 @@ def __repr__(self) -> str:


class Interval(Generic[T]):

def __init__(self, lower: T, upper: T) -> None:
self.lower: T = lower
self.upper: T = upper
Expand Down Expand Up @@ -130,7 +129,7 @@ def copy(self) -> Interval[T]:
return Interval[T](self.lower, self.upper)

def __contains__(self, item: T) -> bool:
return (self.lower <= item and item < self.upper)
return self.lower <= item and item < self.upper

def __repr__(self) -> str:
return f'{type(self).__name__}({self.lower}, {self.upper})'
38 changes: 17 additions & 21 deletions ptychodus/api/object.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ def heightInMeters(self) -> float:

@property
def minimumXInMeters(self) -> float:
return self.centerXInMeters - self.widthInMeters / 2.
return self.centerXInMeters - self.widthInMeters / 2.0

@property
def minimumYInMeters(self) -> float:
return self.centerYInMeters - self.heightInMeters / 2.
return self.centerYInMeters - self.heightInMeters / 2.0

def getPixelGeometry(self) -> PixelGeometry:
return PixelGeometry(
Expand Down Expand Up @@ -79,26 +79,26 @@ def contains(self, geometry: ObjectGeometry) -> bool:
dy = self.centerYInMeters - geometry.centerYInMeters
dw = self.widthInMeters - geometry.widthInMeters
dh = self.heightInMeters - geometry.heightInMeters
return (abs(dx) <= dw and abs(dy) <= dh)
return abs(dx) <= dw and abs(dy) <= dh


class ObjectGeometryProvider(ABC):

@abstractmethod
def getObjectGeometry(self) -> ObjectGeometry:
pass


class Object:

def __init__(self,
array: ObjectArrayType | None = None,
layerDistanceInMeters: Sequence[float] | None = None,
*,
pixelWidthInMeters: float = 0.,
pixelHeightInMeters: float = 0.,
centerXInMeters: float = 0.,
centerYInMeters: float = 0.) -> None:
def __init__(
self,
array: ObjectArrayType | None = None,
layerDistanceInMeters: Sequence[float] | None = None,
*,
pixelWidthInMeters: float = 0.0,
pixelHeightInMeters: float = 0.0,
centerXInMeters: float = 0.0,
centerYInMeters: float = 0.0,
) -> None:
if array is None:
self._array = numpy.zeros((1, 0, 0), dtype=complex)
else:
Expand Down Expand Up @@ -212,32 +212,28 @@ def getTotalLayerDistanceInMeters(self) -> float:


class ObjectInterpolator(ABC):

@abstractmethod
def getPatch(self, patchCenter: ScanPoint, patchExtent: ImageExtent) -> Object:
'''returns an interpolated patch from the object array'''
"""returns an interpolated patch from the object array"""
pass


class ObjectPhaseCenteringStrategy(ABC):

@abstractmethod
def __call__(self, array: ObjectArrayType) -> ObjectArrayType:
'''returns the phase-centered array'''
"""returns the phase-centered array"""
pass


class ObjectFileReader(ABC):

@abstractmethod
def read(self, filePath: Path) -> Object:
'''reads an object from file'''
"""reads an object from file"""
pass


class ObjectFileWriter(ABC):

@abstractmethod
def write(self, filePath: Path, object_: Object) -> None:
'''writes an object to file'''
"""writes an object to file"""
pass
4 changes: 0 additions & 4 deletions ptychodus/api/observer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@


class Observer(ABC):

@abstractmethod
def update(self, observable: Observable) -> None:
pass


class Observable:

def __init__(self) -> None:
self._observerList: list[Observer] = list()

Expand All @@ -41,7 +39,6 @@ def notifyObservers(self) -> None:


class SequenceObserver(Generic[T], ABC):

@abstractmethod
def handleItemInserted(self, index: int, item: T) -> None:
pass
Expand All @@ -56,7 +53,6 @@ def handleItemRemoved(self, index: int, item: T) -> None:


class ObservableSequence(Sequence[T]):

def __init__(self) -> None:
self._observerList: list[SequenceObserver[T]] = list()

Expand Down
Loading

0 comments on commit bf5ab77

Please sign in to comment.