PythonHardwareBase
PythonHardwareBase turns a HardwareObject subclass
into a Python-backed trampoline: a C++ shim whose virtuals forward
to a child Python interpreter over JSON IPC. A concrete trampoline
inherits both its hardware base class — AWG, Clock,
FtmwDigitizer, and so on — and this mixin via multiple
inheritance. The hardware base supplies the Qt slot/signal API the
rest of Blackchirp uses; the mixin owns the subprocess and the IPC
plumbing.
A subclass routes its hardware-base lifecycle hooks through the
mixin: initialize calls initPythonProcess() to bind the comm
pointer and the settings get/set callbacks; testConnection calls
testPythonConnection(), which lazily starts the subprocess on
the first invocation and dispatches the script’s
test_connection method on every call; sleep and
readSettings delegate to pythonSleep() and
pythonReadSettings(). Hardware-specific virtuals translate to
JSON dispatches through pu_process->sendRequest(). Push-style
hardware additionally calls pu_process->setEnabledProxies()
and connects waveformReceived to a shot handler.
The script path, class name, and Python environment directory are
read on demand from HardwareProfileManager.
startPythonProcess() refuses to start the subprocess if either
the script path or the class name is empty rather than substituting
a default. resolvePythonExecutable() probes the configured
environment for the standard venv and conda layouts and falls back
to the literal "python3" (resolved through PATH) when no
environment is set; findHostScript() locates the IPC host script
python_hw_host.py in the application directory or the
share/blackchirp/ install location. The profile workflow and
the user-side script API are documented in
Selecting a Python Driver and
Writing a Python Driver; the contributor-
level architecture of the trampoline mixin and the IPC host is
covered in Python Hardware.
API Reference
-
class PythonHardwareBase
Mixin that turns a HardwareObject subclass into a Python-backed trampoline by handing off lifecycle work to a child Python subprocess.
A trampoline is a C++ class that inherits from both a hardware base (e.g.
AWG,Clock,FlowController,FtmwDigitizer) and PythonHardwareBase via multiple inheritance. The hardware base supplies the Qt slot/signal API; this mixin owns the subprocess and IPC plumbing. Subclasses initialize the mixin withd_keyandd_model, callinitPythonProcess()from their hardware-base initialize hook, calltestPythonConnection()from the test-connection hook, delegatesleepand the read-settings hook (hwReadSettingsor the per-base variant:fcReadSettings,pcReadSettings,tcReadSettings,pgReadSettings,awgReadSettings,clockReadSettings,ftmwReadSettings,gpibReadSettings,ioReadSettings,lifLaserReadSettings,lifDigitizerReadSettings) topythonSleep()andpythonReadSettings(), and translate hardware-specific virtuals intopu_process->sendRequest()dispatches with snake_case method names matching the user script. Push-style hardware also callssetEnabledProxies()and connectspu_process->waveformReceivedto a shot handler.Script path, class name, and Python environment are resolved per- profile from HardwareProfileManager.
startPythonProcess()refuses to start the subprocess if either the script path or the class name is empty rather than substituting a default.See also
Subclassed by PythonAwg, PythonClock, PythonFlowController, PythonFtmwDigitizer, PythonGpibController, PythonIOBoard, PythonLifDigitizer, PythonLifLaser, PythonPressureController, PythonPulseGenerator, PythonTemperatureController
Public Functions
-
explicit PythonHardwareBase(const QString &key, const QString &model)
Construct the mixin with the owning HardwareObject’s identity.
- Parameters:
key – Hardware key in the form
"<Type>.<label>"(e.g.,"PythonAwg.Default"). Used to look up the profile’s script path, class name, and environment directory in HardwareProfileManager.model – Driver class name (e.g.,
"PythonAwg") forwarded to the Python script asself.settings.model.
-
virtual ~PythonHardwareBase()
Stops the subprocess if it is running.
-
void stopProcess()
Stop the Python subprocess if it is running. Idempotent; safe to call from the destructor of a subclass.
-
inline QString pythonErrorString() const
Returns the human-readable error message produced by the most recent failed startPythonProcess() or testPythonConnection() call. Empty when the last operation succeeded.
Public Static Functions
-
static QString resolvePythonExecutable(const QString &envPath)
Resolve the Python interpreter path from a profile’s environment directory.
Probes envPath for the standard venv and conda layouts (
bin/python3,bin/python,Scripts/python.exe). Falls back to the literal string"python3"— which the operating system resolves throughPATH— if envPath is empty or no interpreter is found inside it.- Parameters:
envPath – Path to a venv or conda environment directory, or an empty string for the system Python.
- Returns:
Absolute path to a Python executable, or
"python3".
-
static QString findHostScript()
Locate the IPC host script (
python_hw_host.py) shipped with Blackchirp.Searches the application directory and the standard
share/blackchirp/install location.- Returns:
Absolute path to the host script, or an empty string if it is not found in any of the search paths.
Protected Functions
-
void initPythonProcess(CommunicationProtocol *comm, PythonProcess::SettingsGetter getter, PythonProcess::SettingsSetter setter)
Construct
pu_processand bind the comm pointer and settings callbacks.Call from the hardware base class’s initialize hook. Does not start the subprocess; that happens lazily on the first testPythonConnection() so the registry-driven default-settings pass on profile creation does not spawn a Python interpreter.
- Parameters:
comm – Pointer to the trampoline’s CommunicationProtocol (
p_comm). The pointer is forwarded into the PythonProcess and refreshed on every testPythonConnection(); ownership stays with the HardwareObject.getter – Callback used to service
self.settings.getrelay requests. Typically a lambda capturing the trampoline’s SettingsStorage::get.setter – Callback used to service
self.settings.setrelay requests. Typically a lambda capturing the trampoline’s SettingsStorage::set (which is protected, so the lambda is the bridge that lets the script update persistent settings).
-
bool testPythonConnection(CommunicationProtocol *comm)
Start the subprocess if needed, refresh the comm pointer, and dispatch
test_connection.Call from the hardware base class’s test-connection hook (
testConnection,testClockConnection,fcTestConnection, …). On the first call, lazily starts the subprocess via startPythonProcess(); subsequent calls reuse the running process. Updates the bound comm pointer in case the protocol has been swapped since the last call.- Parameters:
comm – Pointer to the trampoline’s CommunicationProtocol (
p_comm).- Returns:
trueon success;falseif the subprocess could not be started or the script reported a failure (pythonErrorString() then carries the diagnostic).
-
bool startPythonProcess()
Launch the Python subprocess for this trampoline.
Reads
pythonScriptPath,pythonClassName, andpythonEnvPathfor the profile keyed by the constructor’skeyargument from HardwareProfileManager. Resolves the interpreter via resolvePythonExecutable() and the host script via findHostScript(), then delegates to PythonProcess::start. Refuses to start (and sets pythonErrorString()) if the script path or class name is empty rather than substituting a placeholder.- Returns:
trueif the process started and completed its_init/initializehandshake.
-
void pythonSleep(bool b)
Forward
sleep(b)to the Python script.Trampolines that override the HardwareObject sleep() hook delegate to this helper.
-
void pythonReadSettings()
Forward
read_settingsto the running Python script.Used by the trampoline’s per-base read-settings hook override to push post-edit settings back into the script without restarting the subprocess (a restart would re-run
initialize()and disrupt any connected state).
Protected Attributes
-
std::unique_ptr<PythonProcess> pu_process
The owned PythonProcess. Non-null after initPythonProcess() returns; the subprocess inside it is started lazily on first testPythonConnection().
-
explicit PythonHardwareBase(const QString &key, const QString &model)