From 3c44c566d9832799b45b5022546ed221d2920127 Mon Sep 17 00:00:00 2001 From: Stephen Aylward Date: Sun, 5 May 2024 16:35:33 -0400 Subject: [PATCH] ENH: Updated Settings to use local files instead of registry --- README.md | 13 +++++++++++++ README_Dev.md | 6 ++++++ pyproject.toml | 8 +++----- src/minder3d/__main__.py | 12 ++++++------ src/minder3d/lib/sovImageTablePanelUtils.py | 2 +- src/minder3d/lib/sovImageTablePanelWidget.py | 4 ++-- src/minder3d/lib/sovUtils.py | 19 +++++++++++++++---- src/minder3d/minder3DWindow.py | 20 +++++++++++++++----- src/minder3d/{config.py => parse_args.py} | 2 +- 9 files changed, 62 insertions(+), 24 deletions(-) rename src/minder3d/{config.py => parse_args.py} (93%) diff --git a/README.md b/README.md index eba94e1..1e97bd0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,16 @@ # Minder3D Using AI to foster innovation in the exploration of radiological images. + +## Installing in a end-user system (without a python already installed) + +Download the installation package for current release from + [work-in-progress] + +## Installing and Running in a Python Environment + + pip install minder3d + +### Running + + python -m minder3d diff --git a/README_Dev.md b/README_Dev.md index 69ca581..e270844 100644 --- a/README_Dev.md +++ b/README_Dev.md @@ -1,6 +1,12 @@ +Setup for editable installation. Changes to code in src are +immediatelly reflected in the installed version. Also installs +all dependencies needed for development and testing: + pip install -e .[dev] + Prior to making a commit, please verify your code is compliant: pre-commit run --all-files +HINT: QT Designer was used to define the layout. On Windows, to launch designer, use this command: qt6-tools.exe designer pytubeview.ui diff --git a/pyproject.toml b/pyproject.toml index 39f2d47..1bd6c48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,15 +28,13 @@ dependencies = [ [project.scripts] minder3d = "minder3d.__main__:main" -[project.gui-scripts] -minder3d = "minder3d.__main__:main" - [project.urls] Homepage = "https://github.com/aylward/Minder3D" Aylward = "https://www.aylward.org" [project.optional-dependencies] dev = [ + 'qt6-tools', 'black==24.4.2', 'pylint', 'flake8', @@ -61,8 +59,8 @@ tag = true push = false [tool.bumpver.file_patterns] -"pyproject.toml" = ['current_version = "{version}"', 'version = "{version}"'] -"src/minder3d/__init__.py" = ["{version}"] +"pyproject.toml" = ['current_version = "{version}"'] +"src/minder3d/__init__.py" = ["__version__ = '{version}'"] [tool.black] line-length = 80 diff --git a/src/minder3d/__main__.py b/src/minder3d/__main__.py index 31c5af6..bf914fa 100644 --- a/src/minder3d/__main__.py +++ b/src/minder3d/__main__.py @@ -9,7 +9,7 @@ from PySide6.QtGui import QColor, QPalette from PySide6.QtWidgets import QApplication -from .config import parse_config +from .parse_args import parse_args from .minder3DWindow import Minder3DWindow @@ -19,7 +19,7 @@ def main(): Parse arguments and pass them to the application """ - config = parse_config() + cli_args = parse_args() app = QApplication() @@ -46,11 +46,11 @@ def main(): app.setPalette(palette) app.setStyle('Fusion') - if config.load_image is not None: - minder3D.load_image(config.load_image) + if cli_args.load_image is not None: + minder3D.load_image(cli_args.load_image) - if config.load_scene is not None: - minder3D.load_scene(config.load_scene) + if cli_args.load_scene is not None: + minder3D.load_scene(cli_args.load_scene) minder3D.show() diff --git a/src/minder3d/lib/sovImageTablePanelUtils.py b/src/minder3d/lib/sovImageTablePanelUtils.py index 0a67aba..7b39db7 100644 --- a/src/minder3d/lib/sovImageTablePanelUtils.py +++ b/src/minder3d/lib/sovImageTablePanelUtils.py @@ -3,7 +3,7 @@ def get_qthumbnail_from_array(thumb_array): - auto_range = np.quantile(thumb_array, [0.1, 0.9]) + auto_range = np.quantile(thumb_array, [0.05, 0.95]) thumb_array = np.clip(thumb_array, auto_range[0], auto_range[1]) thumb_array = ( (thumb_array - auto_range[0]) / (auto_range[1] - auto_range[0]) * 255 diff --git a/src/minder3d/lib/sovImageTablePanelWidget.py b/src/minder3d/lib/sovImageTablePanelWidget.py index b33398a..e96dbca 100644 --- a/src/minder3d/lib/sovImageTablePanelWidget.py +++ b/src/minder3d/lib/sovImageTablePanelWidget.py @@ -136,13 +136,13 @@ def fill_table(self): self.imageTableWidget.setItem( img_num, 2, QTableWidgetItem(file.filename) ) - if isinstance(file.file_size, []) and len(file.file_size) > 0: + if isinstance(file.file_size, list) and len(file.file_size) > 0: size_str = [str(i) for i in file.file_size] self.imageTableWidget.setItem( img_num, 3, QTableWidgetItem('x'.join(size_str)) ) if ( - isinstance(file.file_spacing, []) + isinstance(file.file_spacing, list) and len(file.file_spacing) > 0 ): spacing_str = [f'{i:.4f}' for i in file.file_spacing] diff --git a/src/minder3d/lib/sovUtils.py b/src/minder3d/lib/sovUtils.py index 7cd109a..cf68715 100644 --- a/src/minder3d/lib/sovUtils.py +++ b/src/minder3d/lib/sovUtils.py @@ -18,7 +18,7 @@ import itk import numpy as np -from PySide6.QtCore import QSettings, QStandardPaths +from PySide6.QtCore import QSettings, QStandardPaths, QCoreApplication from PySide6.QtWidgets import QMainWindow, QTextEdit from .sovColorMapUtils import short_colormap, short_colormap_scale_factor @@ -170,13 +170,23 @@ def wrapper(*args, **kwargs): def get_settings(): """Get the application settings. - This function retrieves the application settings using QSettings from 'itkSpatialObjectsViewer' and 'QuantAIV'. + This function retrieves the application settings via QSettings. + + The main application should set the CoreApplication variables, as shown + in the example below: + from PySide6.QtCore import QCoreApplication + + QCoreApplication.setOrganizationName("aylward") + QCoreApplication.setApplicationName("Minder3D") Returns: QSettings: The application settings. """ + settings_file = os.path.join(QStandardPaths.writableLocation( + QStandardPaths.AppDataLocation), "settings.ini") + os.makedirs(os.path.dirname(settings_file), exist_ok=True) + settings = QSettings(settings_file, QSettings.IniFormat) - settings = QSettings('itkSpatialObjectsViewer', 'QuantAIV') return settings @@ -236,7 +246,8 @@ def get_file_reccords_from_settings(): def add_file_to_settings(obj, filename, file_type, qthumbnail=None): """Add a file to the settings. - This function adds a file to the settings, including its filename, type, spacing, size, and thumbnail. + This function adds a file to the settings, including its filename, type, + spacing, size, and thumbnail. Args: obj: The object representing the file. diff --git a/src/minder3d/minder3DWindow.py b/src/minder3d/minder3DWindow.py index 2586d5c..aabf58c 100644 --- a/src/minder3d/minder3DWindow.py +++ b/src/minder3d/minder3DWindow.py @@ -3,6 +3,7 @@ import itk import numpy as np import vtk +from PySide6.QtCore import QCoreApplication from PySide6.QtWidgets import QFileDialog, QInputDialog, QMainWindow, QTabBar from .lib.sovColorMapUtils import get_nearest_color_index_and_name @@ -38,6 +39,9 @@ def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) + QCoreApplication.setOrganizationName("Aylward") + QCoreApplication.setApplicationName("Minder3D") + self.state = Minder3DState() # File Menu @@ -180,8 +184,9 @@ def log(self, message, level='info'): def load_image(self, filename=None): """Load an image from a file. - If filename is not provided, it opens a file dialog to select an image file. - It then creates a new image from the selected file and updates the image and overlay. + If filename is not provided, it opens a file dialog to select an image + file. It then creates a new image from the selected file and updates + the image and overlay. Args: filename (str?): The path of the image file to be loaded. @@ -201,7 +206,11 @@ def load_image(self, filename=None): self.update_image() self.update_overlay() - qthumb = get_qthumbnail_from_array(self.state.image_array[-1]) + qthumb = get_qthumbnail_from_array( + self.state.image_array[-1][ + self.state.image_array[-1].shape[0] // 2, ::-1, : + ] + ) add_file_to_settings( self.state.image[-1], filename, 'image', qthumb ) @@ -210,8 +219,9 @@ def load_image(self, filename=None): def load_scene(self, filename=None): """Load a scene from a file and update the application state. - If filename is not provided, a file dialog is opened to select the file. - If a valid filename is provided, the scene is loaded from the file and added to the application state. + If filename is not provided, a file dialog is opened to select the + file. If a valid filename is provided, the scene is loaded from the + file and added to the application state. Args: filename (str?): The name of the file to load the scene from. diff --git a/src/minder3d/config.py b/src/minder3d/parse_args.py similarity index 93% rename from src/minder3d/config.py rename to src/minder3d/parse_args.py index 040a2f6..dbe95cf 100644 --- a/src/minder3d/config.py +++ b/src/minder3d/parse_args.py @@ -7,7 +7,7 @@ import argparse -def parse_config() -> argparse.Namespace: +def parse_args() -> argparse.Namespace: """ Parse command line arguments to configure the run. """