Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/open_ephys/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.0.0"
__version__ = "1.0.1"
27 changes: 27 additions & 0 deletions src/open_ephys/streaming/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,30 @@ stream.start(ttl_callback=ttl_callback,

To stop listening, press `ctrl-C`.


### Running the event listener in a separate thread

To receive and respond to events in a separate thread, you can use the Python `threading` library:

```python
import threading
from open_ephys.streaming import EventListener

stream = EventListener(port=5557)

thread = threading.Thread(
target = stream.start,
args = (ttl_callback, spike_callback), # Arguments to the target function
daemon = True # Ensures the main program doesn't exit when the thread finishes
)

thread.start()
```

To stop listening, call the `EventListener.stop()` method, and allow the thread to shut down gracefully by calling `thread.join()`:

```python
stream.stop()
thread.join()
```

21 changes: 18 additions & 3 deletions src/open_ephys/streaming/event_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import zmq
import json
import threading


def default_spike_callback(info):
Expand Down Expand Up @@ -99,6 +100,8 @@ def __init__(self, ip_address="127.0.0.1", port=5557):
self.socket = self.context.socket(zmq.SUB)
self.socket.connect(self.url)
self.socket.setsockopt(zmq.SUBSCRIBE, b"")
self.socket.setsockopt(zmq.RCVTIMEO, 1000) # 1 second timeout
self._stop_event = threading.Event()

print("Initialized EventListener at " + self.url)

Expand All @@ -120,8 +123,9 @@ def start(
"""

print("Starting EventListener")
self._stop_event.clear() # Clear stop event

while True:
while not self._stop_event.is_set():
try:
parts = self.socket.recv_multipart()

Expand All @@ -133,7 +137,18 @@ def start(
spike_callback(info)
else:
ttl_callback(info)

except zmq.Again:
# Timeout occurred, continue loop to check stop flag
continue
except KeyboardInterrupt:
print() # Add final newline
print("Stopped by KeyboardInterrupt")
break
except Exception as e:
print(f"Error: {e}")
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generic exception handler catches and prints errors but doesn't break the loop or stop execution. This could mask critical errors and cause the listener to continue running in an invalid state. Consider whether certain exceptions should stop the listener or if the exception should be re-raised after logging.

Suggested change
print(f"Error: {e}")
print(f"Error: {e}")
raise

Copilot uses AI. Check for mistakes.
break

print("EventListener stopped")

def stop(self):
"""Call this method to stop the listener"""
self._stop_event.set()