Key Utilities and Patterns
Interface System
Component discovery and capability declaration using acq4/Interfaces.py:
# Register a component:
self.dm.declareInterface(name, ['device'], self)
# Discover components:
if hasattr(obj, 'implements') and obj.implements('my_api'):
# Safe to use API methods
DataManager
Handle-based file/directory access (thread-safe) using acq4/util/DataManager/:
from acq4.util.DataManager import getDataManager, getDirHandle
dm = getDataManager()
dirHandle = dm.getDirHandle('/path/to/data')
fileHandle = dirHandle['filename.ext']
data = fileHandle.read()
dirHandle.writeFile(data, 'output.ma')
Resource Locking
Prevent device conflicts using mutex locks (acq4/util/Mutex.py):
# Recommended context manager pattern:
with device.reserved():
device.doSomething()
# Multiple device reservation:
with manager.reserveDevices(['dev1', 'dev2'], block=True, timeout=20):
# Safe to operate on dev1 and dev2
Manager Access
from acq4.Manager import getManager
manager = getManager() # Get current manager singleton
# or often instances will keep a reference at `self.dm`
Task Execution
Tasks coordinate multi-device operations:
# Task creation:
task = manager.createTask(cmd)
task.execute() # Run all device tasks
result = task.getResult()
Dependency management relies on getConfigOrder() and getStartOrder() to declare task sequencing.
Data Formats and I/O
MetaArray (Primary Data Format)
from MetaArray import MetaArray as MA
# Create with metadata:
data = MA(array_data, info=[
{'name': 'Time', 'units': 's', 'values': time_array},
{'name': 'Channel', 'values': ['Voltage', 'Current']},
])
# Access with named indexing:
voltage = data['Channel', 'Voltage']
File Type System
Custom file handlers in acq4/filetypes/:
Each FileType declares
extensions,dataTypes, andpriorityImplements
read(fileHandle)andwrite(data, dirHandle, fileName)The system automatically selects appropriate handlers based on priority
Common Development Tasks
When working with this codebase:
Follow existing patterns for adding new devices or modules.
Use the Interface system for component discovery.
For UI work, check existing modules for patterns and conventions.
Use device locking when extended operations require continuous hardware control.
Keep heavy processing off the Qt GUI thread; otherwise call
Qt.QApplication.processEvents()in long loops.Access data files through DataManager handles, not direct file operations.
Use unit constants from
pyqtgraph.units; store values in unscaled SI units.DeviceTask lifecycle: configure → start → isDone → getResult.