From 010ab73b13d5726eec669a74b908fd3ad1ec7384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20C=C3=B4t=C3=A9?= Date: Wed, 8 Jul 2026 01:34:20 -0400 Subject: [PATCH 1/4] Defer matplotlib and numpy imports in the spectrometers package Problem: Importing anything from hardwarelibrary.spectrometers eagerly loaded matplotlib and numpy. base.py did `from .viewer import *` (viewer is a pure matplotlib GUI module) purely to reach SpectraViewer, and imported numpy at module scope. oceaninsight.py imported matplotlib (backends/pyplot/animation/Button/TextBox) that it never actually used, plus numpy at module scope. __init__.py imported SpectraViewer at the top. As a result `import hardwarelibrary` paid the full matplotlib + numpy cost even when no plot was ever drawn. Solution: Move the heavy imports to their point of use. SpectraViewer is now imported inside Spectrometer.display(), OISpectrometer.display() and displayAny(). numpy is imported inside the __init__/getSpectrum methods that use it; base.py gains `from __future__ import annotations` so the `-> np.array` hint no longer forces numpy at class-definition time. The dead matplotlib import block in oceaninsight.py (never referenced) is removed. viewer.py is unchanged: it legitimately needs matplotlib, and is now only imported when a viewer is actually shown. Co-Authored-By: Claude Opus 4.8 (1M context) --- hardwarelibrary/spectrometers/__init__.py | 2 +- hardwarelibrary/spectrometers/base.py | 7 +++++-- hardwarelibrary/spectrometers/oceaninsight.py | 16 +++++++++------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/hardwarelibrary/spectrometers/__init__.py b/hardwarelibrary/spectrometers/__init__.py index d9eb12c..988082a 100644 --- a/hardwarelibrary/spectrometers/__init__.py +++ b/hardwarelibrary/spectrometers/__init__.py @@ -22,12 +22,12 @@ def __init__(self): from .base import Spectrometer, getAllSubclasses from .oceaninsight import OISpectrometer, USB2000, USB4000, USB2000Plus, USB4000_2000Plus, USB650 -from .viewer import SpectraViewer def any() -> Spectrometer: return Spectrometer.any() def displayAny(): + from .viewer import SpectraViewer spectrometer = Spectrometer.any() if spectrometer is not None: spectrometer.initializeDevice() diff --git a/hardwarelibrary/spectrometers/base.py b/hardwarelibrary/spectrometers/base.py index 4839907..791036a 100644 --- a/hardwarelibrary/spectrometers/base.py +++ b/hardwarelibrary/spectrometers/base.py @@ -1,5 +1,6 @@ +from __future__ import annotations + import time -import numpy as np from abc import abstractmethod from struct import * import csv @@ -17,7 +18,6 @@ from pathlib import * from hardwarelibrary.physicaldevice import PhysicalDevice, DeviceState -from hardwarelibrary.spectrometers.viewer import * class NoSpectrometerConnected(RuntimeError): pass @@ -32,6 +32,8 @@ class Spectrometer(PhysicalDevice): idVendor = None idProduct = None def __init__(self, serialNumber=None, idProduct:int = None, idVendor:int = None): + import numpy as np + PhysicalDevice.__init__(self, serialNumber=serialNumber, idProduct=idProduct, idVendor=idVendor) self.model = "" self.wavelength = np.linspace(400,1000,1024) @@ -57,6 +59,7 @@ def display(self): """ if self.state != DeviceState.Ready: self.initializeDevice() + from hardwarelibrary.spectrometers.viewer import SpectraViewer viewer = SpectraViewer(spectrometer=self) viewer.display() diff --git a/hardwarelibrary/spectrometers/oceaninsight.py b/hardwarelibrary/spectrometers/oceaninsight.py index 39dbe8e..d694a43 100644 --- a/hardwarelibrary/spectrometers/oceaninsight.py +++ b/hardwarelibrary/spectrometers/oceaninsight.py @@ -1,6 +1,5 @@ try: import time - import numpy as np from struct import * import csv from typing import NamedTuple @@ -17,18 +16,12 @@ import usb.util import usb.backend.libusb1 - import matplotlib.backends as backends - import matplotlib.pyplot as plt - import matplotlib.animation as animation - from matplotlib.widgets import Button, TextBox - except Exception as err: print('** Error importing modules. {0}'.format(err)) print('We will attempt to continue and hope for the best.') from hardwarelibrary.spectrometers.base import * -from hardwarelibrary.spectrometers.viewer import * """ This is a simple script to use an Ocean Insight USB2000 spectrometer. You can @@ -627,6 +620,8 @@ def getSpectrumData(self): The spectrum, in 16-bit integers corresponding to each wavelength available in self.wavelength. """ + import numpy as np + spectrum = [] for packet in range(32): @@ -726,6 +721,8 @@ def getSpectrumData(self): The spectrum, in 16-bit integers corresponding to each wavelength available in self.wavelength. """ + import numpy as np + spectrum = [] if not self.lastStatus.isHighSpeed: @@ -847,6 +844,8 @@ class Emitter(NamedTuple): intensity:float = None def __init__(self): + import numpy as np + self.model = "Debug - Nothing is connected" self.wavelength = np.linspace(400,1000,1024) self.integrationTime = 10 @@ -862,6 +861,8 @@ def getSerialNumber(self): return "000-000-000" def getSpectrum(self): + import numpy as np + spectrum = [] time = self.getIntegrationTime() for wavelength in self.wavelength: @@ -880,6 +881,7 @@ def getSpectrum(self): def display(self): """ Display the spectrum with the SpectraViewer class.""" + from hardwarelibrary.spectrometers.viewer import SpectraViewer viewer = SpectraViewer(spectrometer=self) viewer.display() From ffb14578cbc8912df6d23b9e44eb4b4d6c547612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20C=C3=B4t=C3=A9?= Date: Wed, 8 Jul 2026 01:34:30 -0400 Subject: [PATCH 2/4] Defer matplotlib import in OscilloscopeDevice Problem: oscilloscopedevice.py imported matplotlib.pyplot at module scope, so `import hardwarelibrary` (which imports the oscilloscope package) loaded matplotlib even though plt is only ever used by the optional displayWaveforms() method. Solution: Move `import matplotlib.pyplot as plt` into displayWaveforms(), its only caller. Co-Authored-By: Claude Opus 4.8 (1M context) --- hardwarelibrary/oscilloscope/oscilloscopedevice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hardwarelibrary/oscilloscope/oscilloscopedevice.py b/hardwarelibrary/oscilloscope/oscilloscopedevice.py index bbc80a4..a27180b 100644 --- a/hardwarelibrary/oscilloscope/oscilloscopedevice.py +++ b/hardwarelibrary/oscilloscope/oscilloscopedevice.py @@ -4,7 +4,6 @@ from hardwarelibrary.communication.serialport import SerialPort from hardwarelibrary.physicaldevice import * from hardwarelibrary.notificationcenter import NotificationCenter, Notification -import matplotlib.pyplot as plt class Channels(Enum): CH1 = "CH1" @@ -35,6 +34,8 @@ def __init__(self, serialNumber:str = None, idProduct = 0x6001, idVendor = 0x040 self.delay = None def displayWaveforms(self, channels=None): + import matplotlib.pyplot as plt + if channels is None: channels = [Channels.CH1, Channels.CH2] From ba2a1022e4e7bd1277eeffdff4a20fc4d41f87ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20C=C3=B4t=C3=A9?= Date: Wed, 8 Jul 2026 01:34:30 -0400 Subject: [PATCH 3/4] Defer cv2 (OpenCV) import in the cameras module Problem: camera.py imported cv2 at module scope behind a try/except. Importing the cameras package therefore loaded OpenCV eagerly, and a missing OpenCV only surfaced as a printed warning at import time while leaving cv2 undefined for later calls. Solution: Remove the top-level guarded import and import cv2 inside the three methods that use it (captureLoopSynchronous, openCVProperties, doInitializeDevice). The module now imports without OpenCV present, and a missing dependency surfaces as a clear ModuleNotFoundError only when a camera operation is actually invoked. Co-Authored-By: Claude Opus 4.8 (1M context) --- hardwarelibrary/cameras/camera.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hardwarelibrary/cameras/camera.py b/hardwarelibrary/cameras/camera.py index 216dc58..a359bf2 100644 --- a/hardwarelibrary/cameras/camera.py +++ b/hardwarelibrary/cameras/camera.py @@ -4,12 +4,6 @@ from hardwarelibrary.physicaldevice import PhysicalDevice from hardwarelibrary.notificationcenter import NotificationCenter, Notification from threading import Thread, RLock -try: - import cv2 -except Exception as err: - print("No support for OpenCVcameras") - pass - import re class CameraDeviceNotification(Enum): @@ -71,6 +65,8 @@ def stop(self): raise RuntimeError("No monitoring loop running") def captureLoopSynchronous(self): + import cv2 + frame = None NotificationCenter().postNotification(notificationName=CameraDeviceNotification.didStartCapture, notifyingObject=self) @@ -114,6 +110,8 @@ def __init__(self, serialNumber:str = None, idProduct:int = None, idVendor:int = self.cvCameraIndex = int(serialNumber) def openCVProperties(self): + import cv2 + properties = {} for property in dir(cv2): if re.search('CAP_', property) is not None: @@ -139,6 +137,8 @@ def isCompatibleWith(cls, serialNumber, idProduct, idVendor): return wasAcquired def doInitializeDevice(self): + import cv2 + super().doInitializeDevice() with self.lock: # FIXME: Open the first camera we find From 601324655293676caffee37f1302cac08a17e805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20C=C3=B4t=C3=A9?= Date: Wed, 8 Jul 2026 01:34:39 -0400 Subject: [PATCH 4/4] Remove dead pyftdi imports from SutterDevice and EchoDevice Problem: sutterdevice.py and echodevice.py both did `from pyftdi.ftdi import Ftdi`, each already flagged with a `#FIXME: should not be here.` comment. Neither file references Ftdi anywhere, so the imports were dead code that pulled pyftdi in eagerly. Solution: Delete the unused imports. SutterDevice reaches FTDI hardware through SerialPort, which owns the pyftdi dependency. Co-Authored-By: Claude Opus 4.8 (1M context) --- hardwarelibrary/echodevice.py | 2 -- hardwarelibrary/motion/sutterdevice.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/hardwarelibrary/echodevice.py b/hardwarelibrary/echodevice.py index e0a2f26..9586b9f 100644 --- a/hardwarelibrary/echodevice.py +++ b/hardwarelibrary/echodevice.py @@ -8,8 +8,6 @@ import time from struct import * -from pyftdi.ftdi import Ftdi #FIXME: should not be here. - class EchoDevice(PhysicalDevice): classIdProduct = 0x6001 diff --git a/hardwarelibrary/motion/sutterdevice.py b/hardwarelibrary/motion/sutterdevice.py index d4e3805..eda5bb6 100644 --- a/hardwarelibrary/motion/sutterdevice.py +++ b/hardwarelibrary/motion/sutterdevice.py @@ -10,8 +10,6 @@ import time from struct import * -from pyftdi.ftdi import Ftdi #FIXME: should not be here. - class SutterDevice(LinearMotionDevice): classIdVendor = 4930 classIdProduct = 1