qt_device_adapter#
Qt adapter that bridges pure-Python device signals into ``pyqtSignal``s.
GUI code (e.g. live plotting, image previews) needs Qt signals so that
emissions are delivered on the main thread with QueuedConnection
semantics. Devices built on
mesofield.devices.base.BaseDataProducer use psygnal and
remain Qt-free. This module is the seam: attach an adapter to a
device, expose the adapter’s pyqtSignal as an attribute on the
device, and the GUI reads that attribute.
Three adapters live here:
QtDeviceAdapter— for single-channel serial-style devices. Bridgessignals.dataintoserialDataReceived/serialSpeedUpdated.build_channel_adapter()— for multi-channel devices (e.g. a lick detector plotting both lick events and capacitance). Bridgessignals.datainto one{channel}UpdatedpyqtSignal per channel, each fed by a payload extractor.QtImageAdapter— for camera-shaped devices. Provides animage_ready(np.ndarray)pyqtSignal that the MDA viewer subscribes to. The device pushes frames into the adapter viaadapter.emit_frame(frame).
- class mesofield.gui.qt_device_adapter.QtDeviceAdapter[source]#
Bases:
QObjectBridges
device.signals.datainto Qt-friendly emissions.Subscribes to the device’s
signals.dataand re-emits:serialDataReceived(object)— the raw payload.serialSpeedUpdated(float, float)—(time_s, speed)whenever the payload is adictcarrying a"speed"key.time_sdefaults to the device timestamp; if absent, the queue timestamp.
- class mesofield.gui.qt_device_adapter.DeviceChannelSampler[source]#
Bases:
objectPull-based bridge from
device.signals.datato live plots.A fast serial device (e.g. ~1 kHz licks) emitting one Qt
pyqtSignalper sample floods the GUI thread’s event queue: the queue grows faster than it drains regardless of how cheap the slot is, and the window stalls.This sampler avoids Qt entirely on the hot path. It subscribes to the device’s
signals.data(a psygnal, invoked on the device thread) and only appends each channel’s scalar to a bounded per-channel ring buffer – no Qt signal, no cross-thread event. The GUI side pulls a snapshot on its own redraw timer viaprovider(), so the GUI touches the data at a fixed ~30 Hz no matter how fast the device streams.channel_sourcesmaps each channel name to the payload-dict key (or acallable(payload) -> value) yielding that channel’s scalar.tis rebased to the first sample so traces start at ~0 regardless of the device’s absolute clock.max_pointscaps each buffer (and thus memory).
- class mesofield.gui.qt_device_adapter.QtImageAdapter[source]#
Bases:
QObjectBridges per-frame ndarray emissions into a Qt
image_readysignal.The MDA gui’s static-viewer branch subscribes via
preview = ImagePreview(image_payload=cam.image_ready, ...), which callsimage_payload.connect(cb, type=QueuedConnection)– soimage_readyMUST be a realpyqtSignal. Devices built onBaseDataProducerare non-Qt; they hold an instance of this adapter and expose itsimage_readyattribute as their own.Usage:
class MyCam(BaseDataProducer): def __init__(self, cfg=None, **kwargs): super().__init__(cfg, **kwargs) self._qt_image_adapter = QtImageAdapter() self.image_ready = self._qt_image_adapter.image_ready def _run_loop(self): ... self._qt_image_adapter.emit_frame(frame)