Push events
veriCue supports push-based event subscriptions alongside the standard request/response model. The client subscribes to a Qt-level event; the server then sends asynchronous type: "event" messages whenever it fires.
Subscription types
| Subscribe command | Pushed event | When it fires |
|---|---|---|
subscribe_signal(path, signal) | signal_emitted | Every time the signal fires |
subscribe_property(path, property) | property_changed | Every time the property's NOTIFY signal fires |
subscribe_destroyed(path) | object_destroyed | Once, when the QObject is destroyed |
Event message shape
json
{
"v": 1,
"type": "event",
"subscription_id": "sub-7",
"event": "signal_emitted",
"path": "MainWindow/okButton",
"data": {
"signal": "clicked",
"args": [false]
},
"timestamp": "2026-06-21T10:42:13.001Z"
}The data field depends on the event:
json
// signal_emitted
"data": { "signal": "clicked", "args": [false] }
// property_changed
"data": { "property": "text", "value": "Logged in" }
// object_destroyed
"data": { "path": "MainWindow/dialogX" }Subscription lifecycle
- Subscriptions are bound to the TCP connection. When you disconnect, every subscription on that connection is dropped server-side.
- A subscription survives the QObject it tracks being temporarily destroyed and recreated - but only if the new instance is reachable by the same path.
- For
object_destroyed, after the event fires the subscription is automatically removed (one-shot).
Python
python
sub_id = await client.subscribe_signal(
"MainWindow/okButton", "clicked",
callback=lambda evt: print("Click:", evt["timestamp"]),
)
# Either consume from the shared queue …
event = await client.next_event(timeout=5.0)
# … or async-iterate every event
async for event in client.events():
print(event)
# Cancel
await client.unsubscribe(sub_id)C++
cpp
QObject::connect(&client, &vericue::VeriCueClient::eventReceived,
[](const QString &subId, const QString &event,
const QString &path, const QJsonObject &data) {
qInfo() << event << "on" << path << ":" << data;
});
client.subscribeSignal("MainWindow/okButton", "clicked");C#
csharp
string subId = await client.SubscribeSignalAsync(
"MainWindow/okButton", "clicked");
// Single await with timeout
var evt = await client.NextEventAsync(TimeSpan.FromSeconds(5));
// Or async-stream
await foreach (var e in client.EventsAsync(ct))
Console.WriteLine($"{e.EventName} on {e.Path}");
// Or callback at subscribe time
await client.SubscribeSignalAsync(
"MainWindow/dropZone", "fileDropped",
callback: e => Console.WriteLine(e.Data));Common patterns
Wait for "any of N" outcomes
wait_for_signal only handles one signal at a time. If your test waits for any of "Success", "Failure", "Cancelled", subscribe to all three and use whichever event arrives first:
python
ok = await client.subscribe_signal("MainWindow/dialog", "accepted")
err = await client.subscribe_signal("MainWindow/dialog", "rejected")
event = await client.next_event(timeout=10)
print("Got:", event["data"]["signal"])Recording all events for a test
For diagnostics, subscribe to many signals and dump every event to a log file:
python
for path, sig in interesting_signals:
await client.subscribe_signal(path, sig)
with open("events.jsonl", "w") as f:
async for event in client.events():
f.write(json.dumps(event) + "\n")
