diff --git a/README.md b/README.md index b630697..41b69a2 100644 --- a/README.md +++ b/README.md @@ -3,21 +3,27 @@ # autoFISH - automated FISH experiments -Python library to control an automated fluidics system and perform microscope acquisition for iterative FISH experiments. +Python library to control an automated fluidics system and perform acquisition on a microscope for sequential FISH experiments. -Documentation of this **package**: +**TESTED FOR WIN 10 only**: micromanager and most microscope controls work only under Windows. -- [**PDF**](https://drive.google.com/file/d/1BJOog03O_1Qd0z9-Iew3j0tdCUELFUi3/view?usp=drive_link) -- [**WORD**](https://docs.google.com/document/d/1TDRwhDQSY43seuwou7y4bqMeq4aE-g-U/edit?usp=drive_link&ouid=115789064117209337821&rtpof=true&sd=true) +## Documentation -Documentation to **build the fluidics system**: +We provide details documentation for the fluidics system and the experimental workflows with the links below: -- [**PDF**](https://drive.google.com/file/d/1uwClR1c6G9sGJg4e6qd6qb3mEDM5nUBS/view?usp=drive_link) -- [**WORD**](https://docs.google.com/document/d/1WErUkE9Bn6kUm9p4lFHiR1-yyyQqX7Lv/edit?usp=drive_link&ouid=115789064117209337821&rtpof=true&sd=true) +### Fluidics system and software usage -**TESTED FOR WIN 10 only**: micromanager and most microscope controls work only under Windows. +- Documentation of this **package**: + [**PDF**](https://drive.google.com/open?id=1-Fr_Dbg0eQDSbhBmq2gmJ1FDb4Uy_ToI&usp=drive_fs) or + [**WORD**](https://docs.google.com/document/d/1-IA6f02elFrF7JSLvXx199cNjnm8ciBB?rtpof=true&usp=drive_fs) + +- Documentation to **build the fluidics system**: + [**PDF**](https://drive.google.com/open?id=1-3anf7ieKRPeKJ387K1XaBH0heWWIocm&usp=drive_fs) or + [**WORD**](https://docs.google.com/document/d/1-NUsaEacdTHfEi9gJ_SppbXEfD7z9-im?rtpof=true&usp=drive_fs) -![fluidics-system](docs/fluidics-overview.png) +- Documentation to **use a TTL trigger with the LEICA Thunder**: + [**PDF**](https://drive.google.com/open?id=1-bkihEoPfgZdNXIDN6bbxiwU0kXM9xOt&usp=drive_fs) or + [**WORD**](https://docs.google.com/document/d/1-ZXZ6kcjAJHre7fJ8rNfXpDT03TYYEI-?rtpof=true&usp=drive_fs) ## Getting started @@ -28,35 +34,32 @@ We recommend using a package manager (conda) to maintain a clean Python installa 1. Download latest version of miniconda from [**here**](https://docs.conda.io/en/latest/miniconda.html). 2. Open Anaconda terminal and create dedicated environment: `conda create --name autofish python=3.9` 3. Activate environment: `conda activate autofish` -4. Pip install autofish (as an alternative see development installation below) +4. Pip install **autofish** (as an alternative see development installation below) - Base version (without pycromanager) : `pip install -i https://test.pypi.org/simple/autofish` - - Install pycromanager : `pip install pycromanager` + - [Optional] Install pycromanager : `pip install pycromanager` -#### Installation for development - -If you want to further develop the package, several options exist. A simply one is local installation. +### Starting autofish -Rather than performing the pip install from pypi (step 4 above), you can install it locally +1. Open Anaconda terminal and activate environment: `conda activate autofish` +2. Start user interface with command `autofish` -1. Download the zip archive of the branch you are interested in. -2. Unzip on your machine. -3. Open Anaconda terminal at location of the autofish package. -4. Activate environment: `conda activate autofish` -5. Editable install `pip install . -e` +### Upgrading to a new version -### Pycromanager +1. Open Anaconda terminal and activate environment: `conda activate autofish` +2. Upgrade autoFISH `pip install --upgrade https://test.pypi.org/simple/autofish` -One of the acquisition options is via Pycromanager. We found that keeping both micromanager and Pycromanager up-to-date can help to prevent problems. +### Development installation -**Last tested, compatible versions**: +If you want to modify develop the package, several options exist. -- Pycromanager: 0.27.2 -- Micromanager: nightly 20230224 +1. Recommended is to **`clone` the repository**. This will then also allow to propose your changes to the main autoFISH repository as a PullRequest. See for instance [here](https://codex.so/fork-and-pull-en) for an explanation for how this is done. This also allows to easier add changes from the main repository +2. An alternative is a **local installation**. While this might be easier in the short run, it will be challenig to integrate future changes on the main repository. Rather than performing the pip install (step 4 above), you -### Starting autofish - -1. Open Anaconda terminal and activate environment: `conda activate autofish` -2. Start user interface with command `autofish` + 1. Download the zip archive of the branch you are interested in. + 2. Unzip on your machine. + 3. Open Anaconda terminal at location of the autofish package. + 4. Activate environment: `conda activate autofish` + 5. Editable install `pip install . -e` ### Configuration files @@ -67,6 +70,16 @@ The behavior of the fluidics and acquistion system is defined by several config - We provide config files that we use on our system (with a Nikon Ti): +## Pycromanager + +One of the acquisition options is by using Pycromanager. We found that keeping both micromanager and Pycromanager up-to-date can help to prevent problems. + +**Last tested, compatible versions**: + +- Pycromanager: 0.27.2 +- Micromanager: nightly 20230224 + + ## Reporting a problem/suggestion If you encounter a problem or you have a suggestion, please file an [**issue**](https://github.com/fish-quant/autofish/issues). diff --git a/autofish/autofish_gui.py b/autofish/autofish_gui.py index 8e41556..9f1bdc1 100644 --- a/autofish/autofish_gui.py +++ b/autofish/autofish_gui.py @@ -10,15 +10,16 @@ import threading from datetime import datetime import pathlib - +import autofish from autofish.automator import Robot from autofish.imager import pycroManager, fileSync_write, fileSync_create, TTL_sync from autofish.coordinator import Controller - +from importlib.metadata import version # --------------------------------------------------------------------------- # Functions # --------------------------------------------------------------------------- + sg.theme('DarkAmber') NAME_SIZE = 23 microscope_options = ('pycromanager', 'TTL sync', 'file synce - create', 'file sync - write') @@ -37,9 +38,10 @@ def make_window_control(): [sg.HorizontalSeparator()], [sg.Button('Initiate controller', key='-INITIATE_CONTROL-', disabled=True)], [sg.HorizontalSeparator()], - [sg.Button('RUN all ROUNDS!', key='-RUN_ALL_ROUNDS-', disabled=True), - sg.Button('STOP sequential RUN', key='-STOP_SEQ-', disabled=True)], - [sg.Text('Save path'), sg.Text(' ', key='-OUTPUT_DIR_SAVE_IMGS-')], + [sg.Button('RUN all ROUNDS!', key='-RUN_ALL_ROUNDS-', disabled=True) + #sg.Button('STOP sequential RUN', key='-STOP_SEQ-', disabled=True) + ], + [sg.Text(''), sg.Text(' ', key='-OUTPUT_DIR_SAVE_IMGS-')], ] return sg.Window('Automator - automate sequential FISH', layout, location=(800, 600), finalize=True) @@ -166,8 +168,9 @@ def make_window_fluidics(): [sg.Text(' >> Run sequences <<')], [sg.Text('Choose sequence: '), sg.Combo(['To-be-specified'], key='-SEQ_LIST-'), - sg.Button('RUN sequence', key='-RUN_SEQ-', disabled=True), - sg.Button('STOP sequence', key='-STOP_SEQ-', disabled=False)], + sg.Button('RUN sequence', key='-RUN_SEQ-', disabled=True) + #sg.Button('STOP sequence', key='-STOP_SEQ-', disabled=False) + ], #[sg.HorizontalSeparator()], #[sg.Text(' Pippette robot status'), @@ -206,6 +209,10 @@ def main(): handler.setFormatter(formatter) logger.addHandler(handler) + logger_stream.info(f'More detailed log file can be found here {handler.baseFilename}.') + logger_stream.info(f"Using autofish version {version('autofish')}.") + logger.info(f"Using autofish version {version('autofish')}.") + # >>> Event Loop while True: @@ -459,7 +466,6 @@ def main(): # >> FLUIDICS system # ****************************************************************************************************** elif event == '-INITIATE_SYSTEM-': - logger_stream.info(f'Initiate Robot & open serial ports. More info in log {f_log}') try: R = Robot(values['-CONFIG_SYSTEM-'], logger=logger, logger_short=logger_stream) diff --git a/autofish/automator.py b/autofish/automator.py index ba6da86..6a52e6d 100644 --- a/autofish/automator.py +++ b/autofish/automator.py @@ -16,6 +16,9 @@ import numpy as np import csv from pathlib import Path +import importlib + +from importlib.metadata import version # --------------------------------------------------------------------------- # ROBOT class: manages the entire fluidics system @@ -56,6 +59,9 @@ def __init__(self, config_file_system, logger=None, logger_short=None, demo=Fals else: self.logger_short = logger_short + # Log version + self.log_msg('info', f"Using autofish version {version('autofish')}.") + # Enable demo mode if demo: self.log_msg('info', "Demo mode ON") @@ -75,6 +81,7 @@ def __init__(self, config_file_system, logger=None, logger_short=None, demo=Fals self.volume_measurements.append(['Time', 'round', 'buffer', 'duration', 'vol_expected', 'vol_measured']) # General robot configuration + self.hardware_components = ["pump", "plate", "valve_in", "valve_out", "flow_sensor"] self.config_file_experiment = [] self.experiment_config = {} self.buffer_names = [] @@ -330,7 +337,7 @@ def run_step(self, step, round_id, total_time): action = list(step.keys())[0] param = list(step.values())[0] - self.log_msg('info', f'STEP: {action}, with parameter {param}') + self.log_msg('info', f'>> STEP: {action}, with parameter {param}') # == Move robot to specified buffer if action == 'buffer': @@ -524,7 +531,6 @@ def load_config_experiment(self, config_file_experiment): self.log_msg('info', 'Calculation positions of wells.') self.well_coords = self.calc_well_coords() - # Calculate well positions if 'valve_out' in self.experiment_config.keys(): self.log_msg('info', 'Output valve configuration found') @@ -827,7 +833,7 @@ def load_config_system(self): # Check if demo mode is enabled if "demo" in config_system.keys(): - self.log_msg('info', f"Config file contains demo specification: {config_system['demo']}") + self.log_msg('info', f"Demo mode: {config_system['demo']}") if config_system['demo'].lower() in ['1', 'true', 't', 'y', 'yes', 'on']: self.status['demo'] = True self.log_msg('info', "DEMO mode enabled") @@ -852,13 +858,26 @@ def initiate_system(self): """ config_system = self.config_system + error_open_serial_port = False # Loop over all hardware components to connect to serial port if not self.status['demo']: - self.log_msg('info', "Establishing serial port connection to different hardware components") + self.log_msg('info', "Opening serial port connection to different hardware components") + # >>> Connect to serial ports when specified for hardware_comp in config_system: + # Ignore demo entry + if hardware_comp == 'demo': + continue + + # Verify if hardware component exists + if not (hardware_comp in self.hardware_components): + self.log_msg('error', f" Unkown hardware component: {hardware_comp}") + self.log_msg('error', f" Supported are only: {self.hardware_components}") + error_open_serial_port = True + continue + # >>>> Connect to serial port when specified if ('COM' in config_system[hardware_comp].keys()): self.log_msg('info', f" {hardware_comp}: {config_system[hardware_comp]['type']} on port {config_system[hardware_comp]['COM']}") @@ -883,8 +902,10 @@ def initiate_system(self): except serial.SerialException as e: self.log_msg('error', f' ERROR when opening serial port: {e}') + error_open_serial_port = True - # >>> Assign all specified robot elements + # >>> Assign all specified components + self.log_msg('info', "Assigning all components to robot") try: if 'pump' in self.config_system.keys(): @@ -905,19 +926,19 @@ def initiate_system(self): if 'valve_out' in self.config_system.keys(): self.valve_out = self.assign_valve(valve_id='valve_out') else: - self.valve_out = None + self.valve_out = None if 'flow_sensor' in self.config_system.keys(): self.sensor = self.assign_sensor() else: self.sensor = None - if False not in (self.pump, self.valve_in, self.valve_out, self.plate, self.sensor): - self.log_msg('info', 'All specified components assigned.') + if False not in (self.pump, self.valve_in, self.valve_out, self.plate, self.sensor) and not (error_open_serial_port): + self.log_msg('info', 'All connected components assigned.') self.status['ports_assigned'] = True self.status['robot_zeroed'] = False else: - self.log_msg('error', 'Could not connect to one or more component (see error above).') + self.log_msg('error', 'Could not assign one or more component (see error above).') except (UnboundLocalError, AttributeError) as e: self.log_msg('error', f'Assignment of robot components failed. {e}') @@ -929,10 +950,10 @@ def assign_sensor(self): Returns: _type_: _description_ """ - self.log_msg('info', 'Assigning sensor') + self.log_msg('info', ' Assigning sensor') if self.config_system['flow_sensor']['type'] == 'Sensirion CSV': - self.log_msg('info', 'SENSIRION CSV flow sensor with CSV file') + self.log_msg('info', ' SENSIRION CSV flow sensor with CSV file') if not Path(self.config_system['flow_sensor']['log_file']).is_file(): self.log_msg('error', f'File for flow measurement not found {self.config_system["flow_sensor"]["log_file"]}!') self.log_msg('error', f'{self.config_system["flow_sensor"]}') @@ -949,7 +970,7 @@ def assign_sensor(self): return sensor else: - self.log_msg('error', f'Unknown flow sensor: {self.config_system["flow_sensor"]["type"]}') + self.log_msg('error', f' Unknown flow sensor: {self.config_system["flow_sensor"]["type"]}') return False def assign_pump(self): @@ -958,21 +979,21 @@ def assign_pump(self): Returns: _type_: _description_ """ - self.log_msg('info', f'Assigning pump: {self.config_system["pump"]["type"]}') + self.log_msg('info', f' Assigning pump: {self.config_system["pump"]["type"]}') # Function selecting the appropriate pump class if len(self.config_system['pump']['type']) == 0: - self.log_msg('error', 'No pump defined!') + self.log_msg('error', ' No pump defined!') self.log_msg('error', f'{self.config_system["pump"]}') return False if 'ser' not in self.config_system['pump'].keys(): - self.log_msg('error', 'No serial port connection for pump established!') + self.log_msg('error', ' No serial port connection for pump established!') self.log_msg('error', f'{self.config_system["pump"]}') return False if self.config_system['pump']['type'] == 'REGLO DIGITAL': - self.log_msg('info', f'REGLO DIGIAL pump on port {self.config_system["pump"]["ser"].portstr}') + self.log_msg('info', f' REGLO DIGIAL pump on port {self.config_system["pump"]["ser"].portstr}') # Make sure that baudrate is correct ser = self.config_system['pump']['ser'] @@ -986,7 +1007,7 @@ def assign_pump(self): return pump elif self.config_system['pump']['type'] == 'LONGER BT100': - self.log_msg('info', f'LONGER BT100 pump on port {self.config_system["pump"]["ser"].portstr}') + self.log_msg('info', f' LONGER BT100 pump on port {self.config_system["pump"]["ser"].portstr}') # Make sure that baudrate is correct ser = self.config_system['pump']['ser'] @@ -998,7 +1019,7 @@ def assign_pump(self): return pump elif self.config_system['pump']['type'] == 'MZR gear pump': - self.log_msg('info', f'MZR gear pump on port {self.config_system["pump"]["ser"].portstr}') + self.log_msg('info', f' MZR gear pump on port {self.config_system["pump"]["ser"].portstr}') # Make sure that baudrate is correct ser = self.config_system['pump']['ser'] @@ -1021,29 +1042,29 @@ def assign_valve(self, valve_id): _type_: _description_ """ - self.log_msg('info', f'Assigning valve : {valve_id}') + self.log_msg('info', f' Assigning valve: {valve_id}') # Function selecting the appropriate valve class if len(self.config_system[valve_id]['type']) == 0: - self.log_msg('error', 'No valve defined!') + self.log_msg('error', ' No valve defined!') self.log_msg('error', f'{self.config_system[valve_id]}') return False if 'ser' not in self.config_system[valve_id].keys(): - self.log_msg('error', 'No serial port connection for valve established!') + self.log_msg('error', ' No serial port connection for valve established!') self.log_msg('error', f'{self.config_system[valve_id]}') return False if self.config_system[valve_id]['type'] == 'HAMILTON MVP': - self.log_msg('info', f'HAMILTON valve on port {self.config_system[valve_id]["ser"].portstr}') + self.log_msg('info', f' HAMILTON valve on port {self.config_system[valve_id]["ser"].portstr}') # Make sure that baudrate is correct ser = self.config_system[valve_id]['ser'] ser.baudrate = self.config_system[valve_id]['baudrate'] return HamiltonMVPController(ser, logger=self.logger) - + elif self.config_system[valve_id]['type'] == 'AMC RVM': - self.log_msg('info', f'AMC RVM valve on port {self.config_system[valve_id]["ser"].portstr}') + self.log_msg('info', f' AMC RVM valve on port {self.config_system[valve_id]["ser"].portstr}') # Make sure that baudrate is correct ser = self.config_system[valve_id]['ser'] @@ -1051,7 +1072,7 @@ def assign_valve(self, valve_id): return AMCRVMController(ser, logger=self.logger) else: - self.log_msg('error', f'Unknown valve: {self.config_system[valve_id]["type"]}') + self.log_msg('error', f' Unknown valve: {self.config_system[valve_id]["type"]}') return False def assign_plate(self): @@ -1060,29 +1081,29 @@ def assign_plate(self): Returns: _type_: _description_ """ - self.log_msg('info', 'Assigning plate robot.') + self.log_msg('info', ' Assigning plate robot.') # Function selecting the appropriate valve class if len(self.config_system['plate']['type']) == 0: - self.log_msg('error', 'No plate robot defined!') + self.log_msg('error', ' No plate robot defined!') self.log_msg('error', f'{self.config_system["plate"]}') return False if 'ser' not in self.config_system['plate'].keys(): - self.log_msg('error', 'No serial port connection for plate robot established!') + self.log_msg('error', ' No serial port connection for plate robot established!') self.log_msg('error', f'{self.config_system["plate"]}') return False - if self.config_system['plate']['type'] == 'CNCRouter GRBL': - self.log_msg('info', f'3018 plate robot on port {self.config_system["plate"]["ser"].portstr}') + if self.config_system['plate']['type'] == 'GRBL robot': + self.log_msg('info', f' GRBL plate robot on port {self.config_system["plate"]["ser"].portstr}') # Make sure that baudrate is correct ser = self.config_system['plate']['ser'] ser.baudrate = self.config_system['plate']['baudrate'] - return CNCRouterGRBL(ser, self.config_system['plate']['feed'], logger=self.logger) + return GRBLrobot(ser, self.config_system['plate']['feed'], logger=self.logger) else: - self.log_msg('error', f'Unknown plate robot: {self.config_system["plate"]["type"]}') + self.log_msg('error', f' Unknown plate robot: {self.config_system["plate"]["type"]}') return False # --------------------------------------------------------------------------- @@ -1191,9 +1212,8 @@ def stop(self): # Plate controlller # --------------------------------------------------------------------------- - class plateController(): - """ Base class for cnc plate controller. + """ Base class for GRBL plate controller. """ def __init__(self): @@ -1206,8 +1226,8 @@ def move(self): '''Move valve ''' -class CNCRouterGRBL(plateController): - """ Control a CNC router 3018PRO with Gcode. +class GRBLrobot(plateController): + """ Control a GRBL robot with Gcode. Args: plateController (_type_): _description_ @@ -1221,7 +1241,7 @@ def __init__(self, ser, feed, logger): # Initiate self.ser = ser self.feed = feed - self.logger.info('CNCRouterGRBL controller initiated.') + self.logger.info('GRBLrobot controller initiated.') # Set status report ser.flushInput() diff --git a/configs/system_config__flow-sensor-en.json b/configs/system_config__flow-sensor-en.json index ee80668..36da31e 100644 --- a/configs/system_config__flow-sensor-en.json +++ b/configs/system_config__flow-sensor-en.json @@ -15,7 +15,7 @@ "response": "/0" }, "plate": { - "type": "CNCRouter GRBL", + "type": "GRBL robot", "COM": "COM8", "baudrate": 115200, "wake_up": "\r\n\r\n", diff --git a/configs/system_config__flow-sensor-fr.json b/configs/system_config__flow-sensor-fr.json index 752d657..880dc72 100644 --- a/configs/system_config__flow-sensor-fr.json +++ b/configs/system_config__flow-sensor-fr.json @@ -15,7 +15,7 @@ "response": "/0" }, "plate": { - "type": "CNCRouter GRBL", + "type": "GRBL robot", "COM": "COM8", "baudrate": 115200, "wake_up": "\r\n\r\n", diff --git a/configs/system_config__outlet-valve.json b/configs/system_config__outlet-valve.json index 849c48c..bb94c61 100644 --- a/configs/system_config__outlet-valve.json +++ b/configs/system_config__outlet-valve.json @@ -1,25 +1,32 @@ { "pump": { + "type": "REGLO DIGITAL", "test_command": "1#\r", "response": "REGLO DIGITAL", - "type": "REGLO DIGITAL", "baudrate": 9600, "COM": "COM11", "Revolution": "CCW", "Flowrate": 0.5 }, "valve_in": { + "type": "HAMILTON MVP", "test_command": "/1h30001R\r", "response": "/0", - "type": "HAMILTON MVP", "baudrate": 9600, "COM": "COM9" }, + "valve_out": { + "type": "HAMILTON MVP", + "test_command": "/1h30001R\r", + "response": "/0", + "baudrate": 9600, + "COM": "COM7" + }, "plate": { + "type": "GRBL robot", "wake_up": "\r\n\r\n", "test_command": "$I\n", "response": "VER:", - "type": "CNCRouter GRBL", "baudrate": 115200, "COM": "COM14", "feed": 500 @@ -32,12 +39,5 @@ "delimiter": ",", "separator_thousand": ",", "separator_decimal": "." - }, - "valve_out": { - "test_command": "/1h30001R\r", - "response": "/0", - "type": "HAMILTON MVP", - "baudrate": 9600, - "COM": "COM7" } } \ No newline at end of file diff --git a/configs/system_config__pump-LONGER.json b/configs/system_config__pump-LONGER.json index 525de2b..6316543 100644 --- a/configs/system_config__pump-LONGER.json +++ b/configs/system_config__pump-LONGER.json @@ -15,7 +15,7 @@ "response": "/0" }, "plate": { - "type": "CNCRouter GRBL", + "type": "GRBL robot", "COM": "COM8", "baudrate": 115200, "wake_up": "\r\n\r\n", diff --git a/configs/system_config__pump-MZR-gear.json b/configs/system_config__pump-MZR-gear.json index 1456dae..1021a46 100644 --- a/configs/system_config__pump-MZR-gear.json +++ b/configs/system_config__pump-MZR-gear.json @@ -15,7 +15,7 @@ "response": "/0" }, "plate": { - "type": "CNCRouter GRBL", + "type": "GRBL robot", "COM": "COM8", "baudrate": 115200, "wake_up": "\r\n\r\n", diff --git a/configs/system_config__pump-REGLO.json b/configs/system_config__pump-REGLO.json index 37a5930..63221ef 100644 --- a/configs/system_config__pump-REGLO.json +++ b/configs/system_config__pump-REGLO.json @@ -16,7 +16,7 @@ "response": "/0" }, "plate": { - "type": "CNCRouter GRBL", + "type": "GRBL robot", "COM": "COM8", "wake_up": "\r\n\r\n", "test_command": "$I\n", diff --git a/demo/system_config__demo.json b/demo/system_config__demo.json index 080f792..9cf2235 100644 --- a/demo/system_config__demo.json +++ b/demo/system_config__demo.json @@ -9,7 +9,7 @@ "Revolution": "CCW", "Flowrate": 0.5 }, - "valve": { + "valve_in": { "test_command": "/1h30001R\r", "response": "/0", "type": "HAMILTON MVP", @@ -17,15 +17,15 @@ "COM": "COM11" }, "plate": { + "type": "GRBL robot", "wake_up": "\r\n\r\n", "test_command": "$I\n", "response": "VER:", - "type": "CNCRouter3018PRO", "baudrate": 115200, "COM": "COM8" }, "flow_sensor": { - "type": "Sensirion_csv", + "type": "Sensirion CSV", "log_file": "C:\\Temp\\DataLog.csv", "kernel_size": 20, "flow_min": 20 diff --git a/setup.cfg b/setup.cfg index 6bb4f80..7942c90 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = autofish -version = 0.0.5 +version = 0.0.6 author = Florian Mueller author_email = fmueller@pasteur.fr url = https://github.com/fish-quant/autofish @@ -21,13 +21,11 @@ install_requires = pyserial PySimpleGUI==4.60.5 pyyaml - numpy - + numpy [options.extras_require] pycromanager = pycromanager - [options.entry_points] console_scripts = autofish = autofish.autofish_gui:main