Spix provides an XMLRPC interface for remote control of Qt applications.
Use Python's built-in XMLRPC client:
import xmlrpc.client
s = xmlrpc.client.ServerProxy('http://localhost:9000')The default port is 9000. You can change it when constructing AnyRpcServer:
spix::AnyRpcServer server(8080); // Use port 8080# List all available methods
print(s.system.listMethods())
# Get help for a specific method
print(s.system.methodHelp('mouseClick'))| Method | Signature | Description |
|---|---|---|
mouseClick |
mouseClick(path) |
Click at item center |
mouseClickWithButton |
mouseClickWithButton(path, button, modifier) |
Click with specific button and modifiers |
mouseClickWithOffset |
mouseClickWithOffset(path, offsetX, offsetY) |
Click with pixel offset from top-left |
mouseClickWithProportion |
mouseClickWithProportion(path, propX, propY) |
Click at proportional position (0.0-1.0) |
mouseBeginDrag |
mouseBeginDrag(path) |
Begin drag operation |
mouseEndDrag |
mouseEndDrag(path) |
End drag operation |
mouseDropUrls |
mouseDropUrls(path, [url1, url2, ...]) |
Drop URLs (file paths) onto item |
Mouse buttons: 1 = Left, 2 = Right, 4 = Middle
Key modifiers: 0 = None, 1 = Shift, 2 = Control, 4 = Alt, 8 = Meta
# Basic click
s.mouseClick("mainWindow/button")
# Right-click with Ctrl held
s.mouseClickWithButton("mainWindow/item", 2, 2)
# Click 10 pixels right and 5 pixels down from top-left
s.mouseClickWithOffset("mainWindow/item", 10, 5)
# Click at 75% width, 50% height
s.mouseClickWithProportion("mainWindow/item", 0.75, 0.5)
# Drag and drop
s.mouseBeginDrag("mainWindow/source")
s.mouseEndDrag("mainWindow/target")
# Drop files
s.mouseDropUrls("mainWindow/dropArea", ["file:///path/to/file.txt"])| Method | Signature | Description |
|---|---|---|
inputText |
inputText(path, text) |
Type text into the focused widget |
enterKey |
enterKey(path, keyCode, modifier) |
Press and release a key |
# Type text
s.inputText("mainWindow/textField", "Hello World")
# Press Enter (Qt::Key_Return = 16777220)
s.enterKey("mainWindow/textField", 16777220, 0)
# Press Ctrl+A (Qt::Key_A = 65)
s.enterKey("mainWindow/textField", 65, 2)| Method | Signature | Description |
|---|---|---|
getStringProperty |
getStringProperty(path, property) -> string |
Get property value as string |
setStringProperty |
setStringProperty(path, property, value) |
Set property value |
getBoundingBox |
getBoundingBox(path) -> [x, y, width, height] |
Get item bounds in screen coordinates |
existsAndVisible |
existsAndVisible(path) -> bool |
Check if item exists and is visible |
# Read text property
text = s.getStringProperty("mainWindow/label", "text")
# Set property
s.setStringProperty("mainWindow/label", "text", "New Text")
# Get position for external automation
bbox = s.getBoundingBox("mainWindow/button")
x, y, width, height = bbox
# Check existence
if s.existsAndVisible("mainWindow/dialog"):
s.mouseClick("mainWindow/dialog/okButton")| Method | Signature | Description |
|---|---|---|
invokeMethod |
invokeMethod(path, method, args) -> result |
Call a QML method |
# Call built-in method
s.invokeMethod("mainWindow/textArea", "select", [100, 200])
# Call custom QML function
result = s.invokeMethod("mainWindow/item", "customFunction", ["arg1", 42])See QtQuick Guide for type conversion details.
| Method | Signature | Description |
|---|---|---|
wait |
wait(milliseconds) |
Wait for specified time |
waitForItem |
waitForItem(path, timeout) -> bool |
Wait for item to appear |
# Fixed wait
s.wait(500)
# Wait up to 5 seconds for item
if s.waitForItem("mainWindow/loadedContent", 5000):
print("Content loaded")
else:
print("Timeout")| Method | Signature | Description |
|---|---|---|
takeScreenshot |
takeScreenshot(path, filePath) |
Save screenshot to file |
takeScreenshotAsBase64 |
takeScreenshotAsBase64(path) -> string |
Get screenshot as base64 |
# Save to file
s.takeScreenshot("mainWindow", "/tmp/screenshot.png")
# Get as base64
import base64
b64 = s.takeScreenshotAsBase64("mainWindow")
image_data = base64.b64decode(b64)| Method | Signature | Description |
|---|---|---|
getErrors |
getErrors() -> [string, ...] |
Get accumulated errors |
errors = s.getErrors()
if errors:
print("Errors occurred:", errors)| Method | Signature | Description |
|---|---|---|
quit |
quit() |
Close the application |
command |
command(name, payload) |
Execute custom command |
# Quit app
s.quit()
# Custom command (requires handler registration)
s.command("reset", "full")Register handlers in your C++ code:
spix::AnyRpcServer server;
server.setGenericCommandHandler([](std::string command, std::string payload) {
if (command == "reset") {
// Handle reset command
}
});s.command("reset", "")Spix generates Qt events directly inside the application. The mouse cursor does not move visually, but events are delivered correctly. This works on any platform and over the network.
Query Spix for screen coordinates, then use external tools like PyAutoGUI to generate real mouse/keyboard events:
import pyautogui
import xmlrpc.client
s = xmlrpc.client.ServerProxy('http://localhost:9000')
# Get screen position
bbox = s.getBoundingBox("mainWindow/button")
center_x = bbox[0] + bbox[2] / 2
center_y = bbox[1] + bbox[3] / 2
# Generate real mouse event
pyautogui.click(center_x, center_y)See RemoteCtrl example for a complete implementation.