Loading openflexure_microscope/camera/pi.py +0 −2 Original line number Diff line number Diff line Loading @@ -68,8 +68,6 @@ class PiCameraStreamer(BaseCamera): ] def __init__(self): global USER_CONFIG_DIR # Run BaseCamera init BaseCamera.__init__(self) # Attach to Pi camera Loading openflexure_microscope/config.py +103 −98 Original line number Diff line number Diff line Loading @@ -3,115 +3,24 @@ import os import errno import logging import shutil import copy import numpy as np from fractions import Fraction from .utilities import recursively_apply """ Attributes: USER_CONFIG_DIR (str): Default path of the user-config directory, containing runtime-config and calibration files. Obtained from ``os.path.join(os.path.expanduser("~"), ".openflexure")``. USER_CONFIG_FILE (str): Default path of the user microscope_settings.json runtime-config file. USER_CONFIG_FILE_PATH (str): Default path of the user microscope_settings.json runtime-config file. Obtained from ``os.path.join(USER_CONFIG_DIR, "microscope_settings.json")`` user_settings (OpenflexureSettingsFile): Default settings file object, which handles expansion and contraction of settings dictionaries. """ # HANDLE THE DEFAULT CONFIGURATION FILE HERE = os.path.abspath(os.path.dirname(__file__)) DEFAULT_CONFIG_PATH = os.path.join(HERE, "microscope_settings.default.json") USER_CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".openflexure") USER_CONFIG_FILE = os.path.join(USER_CONFIG_DIR, "microscope_settings.json") USER_CONFIG_FILE_OLD = os.path.join(USER_CONFIG_DIR, "microscoperc.json") # Load the default config with open(DEFAULT_CONFIG_PATH, "r") as default_rc: DEFAULT_CONFIG = default_rc.read() # HANDLE BASIC LOADING AND SAVING OF SETTINGS FILES def load_json_file(config_path) -> dict: """ Open a .json config file Args: config_path (str): Path to the config JSON file. If `None`, defaults to `DEFAULT_CONFIG_PATH` """ config_path = os.path.expanduser(config_path) logging.info("Loading {}...".format(config_path)) with open(config_path) as config_file: try: config_data = json.load(config_file) except json.decoder.JSONDecodeError as e: logging.error(e) config_data = {} # Return loaded config dictionary return config_data def save_json_file(config_path: str, config_dict: dict): """ Save a .json config file Args: config_dict (dict): Dictionary of config data to save. config_path (str): Path to the config JSON file. """ config_path = os.path.expanduser(config_path) logging.info("Saving {}...".format(config_path)) logging.debug(config_dict) with open(config_path, "w") as outfile: json.dump(config_dict, outfile, cls=JSONEncoder, indent=2, sort_keys=True) def create_file(config_path): if not os.path.exists(os.path.dirname(config_path)): try: os.makedirs(os.path.dirname(config_path)) except OSError as exc: # Guard against race condition if exc.errno != errno.EEXIST: raise def initialise_file(config_path, populate: str = "{}\n"): class JSONEncoder(json.JSONEncoder): """ Check if a file exists, and if not, create it and optionally populate it with content Args: config_path (str): Path to the file. populate (str): String to dump to the file, if it is being newly created A custom JSON encoder, with type conversions for PiCamera fractions, Numpy integers, and Numpy arrays """ config_path = os.path.expanduser(config_path) logging.debug("Initialising {}".format(config_path)) logging.debug("Exists: {}".format(os.path.exists(config_path))) if not os.path.exists(config_path): # If user config file doesn't exist logging.warning("No config file found at {}. Creating...".format(config_path)) create_file(config_path) logging.info("Populating {}...".format(config_path)) with open(config_path, "w") as outfile: outfile.write(populate) def settings_file_path(filename:str): """Generate a full file path for a filename to be stored in user settings""" global USER_CONFIG_DIR return os.path.join(USER_CONFIG_DIR, filename) class JSONEncoder(json.JSONEncoder): def default(self, o, markers=None): # PiCamera fractions if isinstance(o, Fraction): Loading @@ -127,6 +36,7 @@ class JSONEncoder(json.JSONEncoder): # raising exceptions for unsupported types return json.JSONEncoder.default(self, o) # MAIN CONFIG CLASS class OpenflexureSettingsFile: Loading @@ -139,7 +49,7 @@ class OpenflexureSettingsFile: """ def __init__(self, config_path: str = None, expand: bool = True): global DEFAULT_CONFIG, USER_CONFIG_FILE global DEFAULT_CONFIG, USER_CONFIG_FILE_PATH self.expandable_keys = { "camera_settings": None, Loading @@ -147,7 +57,7 @@ class OpenflexureSettingsFile: } #: Dictionary of keys that can be passed as a file path string and expanded automatically # Set arguments self.config_path = config_path or USER_CONFIG_FILE self.config_path = config_path or USER_CONFIG_FILE_PATH self.expand = expand # Initialise basic config file with defaults if it doesn't exist Loading Loading @@ -250,3 +160,98 @@ class OpenflexureSettingsFile: return_config[key] = value return return_config # HANDLE BASIC LOADING AND SAVING OF SETTINGS FILES def load_json_file(config_path) -> dict: """ Open a .json config file Args: config_path (str): Path to the config JSON file. If `None`, defaults to `DEFAULT_CONFIG_PATH` """ config_path = os.path.expanduser(config_path) logging.info("Loading {}...".format(config_path)) with open(config_path) as config_file: try: config_data = json.load(config_file) except json.decoder.JSONDecodeError as e: logging.error(e) config_data = {} # Return loaded config dictionary return config_data def save_json_file(config_path: str, config_dict: dict): """ Save a .json config file Args: config_dict (dict): Dictionary of config data to save. config_path (str): Path to the config JSON file. """ config_path = os.path.expanduser(config_path) logging.info("Saving {}...".format(config_path)) logging.debug(config_dict) with open(config_path, "w") as outfile: json.dump(config_dict, outfile, cls=JSONEncoder, indent=2, sort_keys=True) def create_file(config_path): if not os.path.exists(os.path.dirname(config_path)): try: os.makedirs(os.path.dirname(config_path)) except OSError as exc: # Guard against race condition if exc.errno != errno.EEXIST: raise def initialise_file(config_path, populate: str = "{}\n"): """ Check if a file exists, and if not, create it and optionally populate it with content Args: config_path (str): Path to the file. populate (str): String to dump to the file, if it is being newly created """ config_path = os.path.expanduser(config_path) logging.debug("Initialising {}".format(config_path)) logging.debug("Exists: {}".format(os.path.exists(config_path))) if not os.path.exists(config_path): # If user config file doesn't exist logging.warning("No config file found at {}. Creating...".format(config_path)) create_file(config_path) logging.info("Populating {}...".format(config_path)) with open(config_path, "w") as outfile: outfile.write(populate) def settings_file_path(filename: str): """Generate a full file path for a filename to be stored in user settings""" global USER_CONFIG_DIR return os.path.join(USER_CONFIG_DIR, filename) # HANDLE THE DEFAULT CONFIGURATION FILE HERE = os.path.abspath(os.path.dirname(__file__)) DEFAULT_CONFIG_FILE_PATH = os.path.join(HERE, "microscope_settings.default.json") USER_CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".openflexure") USER_CONFIG_FILE_PATH = os.path.join(USER_CONFIG_DIR, "microscope_settings.json") # Load the default config with open(DEFAULT_CONFIG_FILE_PATH, "r") as default_rc: DEFAULT_CONFIG = default_rc.read() # Create the default user settings object user_settings = OpenflexureSettingsFile(config_path=USER_CONFIG_FILE_PATH, expand=True) openflexure_microscope/microscope.py +7 −14 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ from openflexure_microscope.camera.mock import MockStreamer from openflexure_microscope.plugins import PluginMount from openflexure_microscope.task import TaskOrchestrator from openflexure_microscope.common.lock import CompositeLock from openflexure_microscope.config import OpenflexureSettingsFile from openflexure_microscope.config import user_settings class Microscope: Loading @@ -24,8 +24,6 @@ class Microscope: The camera and stage should already be initialised, and passed as arguments. Attributes: settings_file (:py:class:`openflexure_microscope.config.OpenflexureSettingsFile`): Runtime-config object, automatically created if None. lock (:py:class:`openflexure_microscope.common.lock.CompositeLock`): Composite lock controlling thread access to multiple pieces of hardware. camera (:py:class:`openflexure_microscope.camera.base.BaseCamera`): Camera object Loading @@ -50,10 +48,8 @@ class Microscope: # Create a task orchestrator self.task = TaskOrchestrator() # Attach to a settings file self.settings_file = OpenflexureSettingsFile(expand=True) # Apply settings loaded from file self.apply_config(self.settings_file.load()) self.apply_config(user_settings.load()) # Create plugin mount-point and attach plugins from maps self.plugin = PluginMount(self) Loading Loading @@ -223,14 +219,11 @@ class Microscope: settings_current_stage = self.stage.read_config() settings_current["stage_settings"] = settings_current_stage settings_full = self.settings_file.merge(settings_current) #if json_safe: # settings_full = settings_to_json(settings_full) settings_full = user_settings.merge(settings_current) return settings_full def save_config(self, backup: bool = True): def save_config(self): """ Merges the current settings back to disk """ Loading @@ -245,11 +238,11 @@ class Microscope: self.camera.save_config() if self.stage: self.stage.save_config() self.settings_file.save(current_config, backup=True) user_settings.save(current_config, backup=True) @property def config(self) -> dict: logging.warn( logging.warning( "Reading microscope through config property is deprecated.\ Please use read_config method instead." ) Loading @@ -257,7 +250,7 @@ class Microscope: @config.setter def config(self, config: dict) -> None: logging.warn( logging.warning( "Setting microscope through config property is deprecated.\ Please use apply_config method instead." ) Loading Loading
openflexure_microscope/camera/pi.py +0 −2 Original line number Diff line number Diff line Loading @@ -68,8 +68,6 @@ class PiCameraStreamer(BaseCamera): ] def __init__(self): global USER_CONFIG_DIR # Run BaseCamera init BaseCamera.__init__(self) # Attach to Pi camera Loading
openflexure_microscope/config.py +103 −98 Original line number Diff line number Diff line Loading @@ -3,115 +3,24 @@ import os import errno import logging import shutil import copy import numpy as np from fractions import Fraction from .utilities import recursively_apply """ Attributes: USER_CONFIG_DIR (str): Default path of the user-config directory, containing runtime-config and calibration files. Obtained from ``os.path.join(os.path.expanduser("~"), ".openflexure")``. USER_CONFIG_FILE (str): Default path of the user microscope_settings.json runtime-config file. USER_CONFIG_FILE_PATH (str): Default path of the user microscope_settings.json runtime-config file. Obtained from ``os.path.join(USER_CONFIG_DIR, "microscope_settings.json")`` user_settings (OpenflexureSettingsFile): Default settings file object, which handles expansion and contraction of settings dictionaries. """ # HANDLE THE DEFAULT CONFIGURATION FILE HERE = os.path.abspath(os.path.dirname(__file__)) DEFAULT_CONFIG_PATH = os.path.join(HERE, "microscope_settings.default.json") USER_CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".openflexure") USER_CONFIG_FILE = os.path.join(USER_CONFIG_DIR, "microscope_settings.json") USER_CONFIG_FILE_OLD = os.path.join(USER_CONFIG_DIR, "microscoperc.json") # Load the default config with open(DEFAULT_CONFIG_PATH, "r") as default_rc: DEFAULT_CONFIG = default_rc.read() # HANDLE BASIC LOADING AND SAVING OF SETTINGS FILES def load_json_file(config_path) -> dict: """ Open a .json config file Args: config_path (str): Path to the config JSON file. If `None`, defaults to `DEFAULT_CONFIG_PATH` """ config_path = os.path.expanduser(config_path) logging.info("Loading {}...".format(config_path)) with open(config_path) as config_file: try: config_data = json.load(config_file) except json.decoder.JSONDecodeError as e: logging.error(e) config_data = {} # Return loaded config dictionary return config_data def save_json_file(config_path: str, config_dict: dict): """ Save a .json config file Args: config_dict (dict): Dictionary of config data to save. config_path (str): Path to the config JSON file. """ config_path = os.path.expanduser(config_path) logging.info("Saving {}...".format(config_path)) logging.debug(config_dict) with open(config_path, "w") as outfile: json.dump(config_dict, outfile, cls=JSONEncoder, indent=2, sort_keys=True) def create_file(config_path): if not os.path.exists(os.path.dirname(config_path)): try: os.makedirs(os.path.dirname(config_path)) except OSError as exc: # Guard against race condition if exc.errno != errno.EEXIST: raise def initialise_file(config_path, populate: str = "{}\n"): class JSONEncoder(json.JSONEncoder): """ Check if a file exists, and if not, create it and optionally populate it with content Args: config_path (str): Path to the file. populate (str): String to dump to the file, if it is being newly created A custom JSON encoder, with type conversions for PiCamera fractions, Numpy integers, and Numpy arrays """ config_path = os.path.expanduser(config_path) logging.debug("Initialising {}".format(config_path)) logging.debug("Exists: {}".format(os.path.exists(config_path))) if not os.path.exists(config_path): # If user config file doesn't exist logging.warning("No config file found at {}. Creating...".format(config_path)) create_file(config_path) logging.info("Populating {}...".format(config_path)) with open(config_path, "w") as outfile: outfile.write(populate) def settings_file_path(filename:str): """Generate a full file path for a filename to be stored in user settings""" global USER_CONFIG_DIR return os.path.join(USER_CONFIG_DIR, filename) class JSONEncoder(json.JSONEncoder): def default(self, o, markers=None): # PiCamera fractions if isinstance(o, Fraction): Loading @@ -127,6 +36,7 @@ class JSONEncoder(json.JSONEncoder): # raising exceptions for unsupported types return json.JSONEncoder.default(self, o) # MAIN CONFIG CLASS class OpenflexureSettingsFile: Loading @@ -139,7 +49,7 @@ class OpenflexureSettingsFile: """ def __init__(self, config_path: str = None, expand: bool = True): global DEFAULT_CONFIG, USER_CONFIG_FILE global DEFAULT_CONFIG, USER_CONFIG_FILE_PATH self.expandable_keys = { "camera_settings": None, Loading @@ -147,7 +57,7 @@ class OpenflexureSettingsFile: } #: Dictionary of keys that can be passed as a file path string and expanded automatically # Set arguments self.config_path = config_path or USER_CONFIG_FILE self.config_path = config_path or USER_CONFIG_FILE_PATH self.expand = expand # Initialise basic config file with defaults if it doesn't exist Loading Loading @@ -250,3 +160,98 @@ class OpenflexureSettingsFile: return_config[key] = value return return_config # HANDLE BASIC LOADING AND SAVING OF SETTINGS FILES def load_json_file(config_path) -> dict: """ Open a .json config file Args: config_path (str): Path to the config JSON file. If `None`, defaults to `DEFAULT_CONFIG_PATH` """ config_path = os.path.expanduser(config_path) logging.info("Loading {}...".format(config_path)) with open(config_path) as config_file: try: config_data = json.load(config_file) except json.decoder.JSONDecodeError as e: logging.error(e) config_data = {} # Return loaded config dictionary return config_data def save_json_file(config_path: str, config_dict: dict): """ Save a .json config file Args: config_dict (dict): Dictionary of config data to save. config_path (str): Path to the config JSON file. """ config_path = os.path.expanduser(config_path) logging.info("Saving {}...".format(config_path)) logging.debug(config_dict) with open(config_path, "w") as outfile: json.dump(config_dict, outfile, cls=JSONEncoder, indent=2, sort_keys=True) def create_file(config_path): if not os.path.exists(os.path.dirname(config_path)): try: os.makedirs(os.path.dirname(config_path)) except OSError as exc: # Guard against race condition if exc.errno != errno.EEXIST: raise def initialise_file(config_path, populate: str = "{}\n"): """ Check if a file exists, and if not, create it and optionally populate it with content Args: config_path (str): Path to the file. populate (str): String to dump to the file, if it is being newly created """ config_path = os.path.expanduser(config_path) logging.debug("Initialising {}".format(config_path)) logging.debug("Exists: {}".format(os.path.exists(config_path))) if not os.path.exists(config_path): # If user config file doesn't exist logging.warning("No config file found at {}. Creating...".format(config_path)) create_file(config_path) logging.info("Populating {}...".format(config_path)) with open(config_path, "w") as outfile: outfile.write(populate) def settings_file_path(filename: str): """Generate a full file path for a filename to be stored in user settings""" global USER_CONFIG_DIR return os.path.join(USER_CONFIG_DIR, filename) # HANDLE THE DEFAULT CONFIGURATION FILE HERE = os.path.abspath(os.path.dirname(__file__)) DEFAULT_CONFIG_FILE_PATH = os.path.join(HERE, "microscope_settings.default.json") USER_CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".openflexure") USER_CONFIG_FILE_PATH = os.path.join(USER_CONFIG_DIR, "microscope_settings.json") # Load the default config with open(DEFAULT_CONFIG_FILE_PATH, "r") as default_rc: DEFAULT_CONFIG = default_rc.read() # Create the default user settings object user_settings = OpenflexureSettingsFile(config_path=USER_CONFIG_FILE_PATH, expand=True)
openflexure_microscope/microscope.py +7 −14 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ from openflexure_microscope.camera.mock import MockStreamer from openflexure_microscope.plugins import PluginMount from openflexure_microscope.task import TaskOrchestrator from openflexure_microscope.common.lock import CompositeLock from openflexure_microscope.config import OpenflexureSettingsFile from openflexure_microscope.config import user_settings class Microscope: Loading @@ -24,8 +24,6 @@ class Microscope: The camera and stage should already be initialised, and passed as arguments. Attributes: settings_file (:py:class:`openflexure_microscope.config.OpenflexureSettingsFile`): Runtime-config object, automatically created if None. lock (:py:class:`openflexure_microscope.common.lock.CompositeLock`): Composite lock controlling thread access to multiple pieces of hardware. camera (:py:class:`openflexure_microscope.camera.base.BaseCamera`): Camera object Loading @@ -50,10 +48,8 @@ class Microscope: # Create a task orchestrator self.task = TaskOrchestrator() # Attach to a settings file self.settings_file = OpenflexureSettingsFile(expand=True) # Apply settings loaded from file self.apply_config(self.settings_file.load()) self.apply_config(user_settings.load()) # Create plugin mount-point and attach plugins from maps self.plugin = PluginMount(self) Loading Loading @@ -223,14 +219,11 @@ class Microscope: settings_current_stage = self.stage.read_config() settings_current["stage_settings"] = settings_current_stage settings_full = self.settings_file.merge(settings_current) #if json_safe: # settings_full = settings_to_json(settings_full) settings_full = user_settings.merge(settings_current) return settings_full def save_config(self, backup: bool = True): def save_config(self): """ Merges the current settings back to disk """ Loading @@ -245,11 +238,11 @@ class Microscope: self.camera.save_config() if self.stage: self.stage.save_config() self.settings_file.save(current_config, backup=True) user_settings.save(current_config, backup=True) @property def config(self) -> dict: logging.warn( logging.warning( "Reading microscope through config property is deprecated.\ Please use read_config method instead." ) Loading @@ -257,7 +250,7 @@ class Microscope: @config.setter def config(self, config: dict) -> None: logging.warn( logging.warning( "Setting microscope through config property is deprecated.\ Please use apply_config method instead." ) Loading