Structural Bioinformatics Library
Template C++ / Python API for developping structural bioinformatics applications.
User Manual

Authors: S. Guo and E. Sarti and F. Cazals

Plugin_manager

Goals

Overview

The Plugin_manager package enables the automatic creation of cross‑platform graphical user‑interface (GUI) plugins for command‑line applications, from a single specification. Its objectives are to:

  • Simplify GUI plugin generation for existing command line interface (CLI) tools.
  • Facilitate interactive use of command‑line applications.
  • Provide extensibility for integration with molecular viewers such as PyMOL, VMD, and NGLViewer.
  • Extend to general scientific applications beyond structural biology, such as general geometric data analysis and visualization using Three.js viewer.

Architecture overview

In theory, developing GUI for $$n$$ applications targeting $$m$$ viewers/platforms require , $$n\times m$$ specific developments. In structural bioinformatics, typical platforms are are VMD, PyMOL, or web servers.

We instead propose a solution requiring in $$n+m$$ developments (Fig. Plugin generation). Our solution, detailed below, hinges on (i) one formal specification per application, and (ii) code generate modules which are platform specific.

Plugin generation using one specification file per application. For each application $$A_i$$, one provides a GUI specification $$S_i$$. Each targeted platform is handled by a dedicated code generator $$C_j$$: $$C_j(A_i)$$ generates the code of i-th application for the j-th platform. Platforms are supported by various rendering engines.

Terminology

  • CLI: Command Line Interface tool (compiled executable, executable script)
  • GUI library: The rendering toolkit used to instantiate widgets and connect them with the backend (e.g., pymol.Qt, PyQt6, tkinter, Panel).
  • Frontend: The set of UI widgets (labels, entries, checkboxes, buttons, output panels).
  • Backend: The logic that converts frontend inputs into a CLI command, executes it, maps results back to output panels, and updates the output panels and molecular viewer.
  • Platform: The hosting environment where the generated GUI runs (e.g., a VMD plugin, a PyMOL plugin, or a web application).
  • Plugin: A software component embedded within a Platform.
  • panel: An area within the GUI that displays input/update controls or output results.

Using existing plugins for the Structural Bioinformatics Library

Existing plugins

A number of plugins are available, see List of available plugins .

To regenerate the GUIs for all of the above at once, run the orchestration script (this is not needed to run because we have already generated them):

cd Applications/Plugin_manager
bash run_generator.sh
We pair tkinter with VMD and pymol.Qt with PyMOL because each host application is built around a different GUI stack. VMD's UI is Tcl/Tk-based and its Python plugin hooks are designed to create Tk windows; using Tkinter keeps us inside the same event loop, matches VMD's native look-and-feel, and avoids bundling an extra toolkit. PyMOL, in contrast, runs a Qt UI and exposes pymol.Qt and addmenuitemqt for plugins. We use the pymol.Qt compatibility layer which abstracts over PyQt5 and PyQt6, ensuring our plugins work seamlessly with both Qt5-based and Qt6-based PyMOL installations without code changes. PyQt6 in VMD is only practical as a separate external window (launched as another process). Embedding Qt inside VMD’s Tk application risks event-loop conflicts and complicates distribution. tkinter in PyMOL is theoretically possible if the embedded Python ships with _tkinter, but modern PyMOL builds often omit Tk, and mixing Tk with a Qt application is fragile.


And their installation

Pixi installation from the Anaconda sbl channel

Refer to section Installing and using SBL with Pixi (Linux 64, OSX 64, OSX ARM64) in the SBL Installation Guide for detailed steps on creating a Pixi environment, installing the SBL package, and running sbl-install-plugins.

Once SBL and the plugins are installed, you can use them as follows:

VMD plugins
Open VMD and navigate to Extensions → SBL to access the available plugins.

PyMOL plugins
Open PyMOL and navigate to Plugin menu to access the installed SBL plugins.

Web plugins
Launch the Web plugins dashboard:

sbl-web-plugins-launcher

Then open the URL printed in the terminal in your web browser. The URL includes a security token, e.g. http://127.0.0.1:8700/?token=abc123... — this token prevents other users on the same machine from accessing your dashboard and plugins.

Installing individual plugins manually

If you prefer to install a single plugin independently (without the full SBL installation), follow these platform‑specific instructions.

Installing an individual VMD plugin

  • 1) Locate the plugin folder (e.g., sbl_vorlume_pdb) containing pkgIndex.tcl and sbl_pluginname.tcl.
  • 2) Copy the folder to VMD's plugin directory:
    cp -r sbl_vorlume_pdb ~/sblvmdplugins/
    Rename sbl_pluginname.tcl to match your plugin (e.g., sbl_vorlume_pdb.tcl) and update all occurrences of pluginname inside both files with the actual plugin name.
  • 3) Ensure ~/sblvmdplugins is in VMD's auto_path. Add to ~/.vmdrc if needed:
    set auto_path [linsert $auto_path 0 [file join /path/to/HOME sblvmdplugins]]
  • 4) Register the plugin extension in ~/.vmdrc:
    vmd_install_extension sbl_vorlume_pdb sbl_vorlume_pdb "SBL/Vorlume"
  • 5) Add the SBL socket server configuration to ~/.vmdrc (required for plugin communication). Skip this step if the configuration is already present:
    # Start the SBL socket server once, on port 5555
    if {![info exists ::SBL_SOCKET_STARTED]} {
    if {[catch {socket -server ::sbl_socket_server 5555} err]} {
    puts "SBL socket server failed on port 5555: $err"
    } else {
    set ::SBL_SOCKET_STARTED 1
    puts "SBL socket server listening on port 5555"
    }
    proc ::sbl_socket_server {sock addr port} {
    fconfigure $sock -buffering line -eofchar {}
    fileevent $sock readable [list ::sbl_socket_eval $sock]
    }
    proc ::sbl_socket_eval {sock} {
    if {[eof $sock]} { catch {close $sock}; return }
    if {[gets $sock line] < 0} { return }
    if {[catch {uplevel #0 $line} result]} {
    puts "VMD socket eval failed: $result"
    catch {puts $sock "SBL_ERROR: $result"}
    catch {flush $sock}
    } else {
    puts "VMD socket eval success: $line"
    set _resp [string map {"\n" " " "\r" " "} $result]
    catch {puts $sock $_resp}
    catch {flush $sock}
    }
    }
    }
  • 6) Add the vmd_visualize_sbl_plugin convenience proc to ~/.vmdrc (for loading SBL visualization scripts). Skip this step if the proc is already defined:
    # Convenience: Load and run SBL .vmd scripts from a post directory
    if {[info commands vmd_visualize_sbl_plugin] eq ""} {
    proc vmd_visualize_sbl_plugin {post_dir} {
    # Delete all existing molecules to start with a clean slate
    set num_mols [molinfo num]
    if {$num_mols > 0} {
    puts "INFO: Deleting $num_mols existing molecule(s)"
    for {set i [expr $num_mols - 1]} {$i >= 0} {incr i -1} {
    mol delete $i
    }
    }
    # Collect .vmd files; sort for deterministic order
    set pattern [file join $post_dir "*.vmd"]
    set vmd_files [lsort -dictionary [glob -nocomplain -- $pattern]]
    if {[llength $vmd_files] == 0} {
    puts "INFO: No VMD command files found matching $pattern"
    return
    }
    foreach f $vmd_files {
    puts "INFO: Sourcing VMD command file $f"
    source $f
    }
    }
    }
  • 7) Restart VMD. The plugin appears under Extensions → SBL.

Installing an individual Web plugin

  • 1) Copy the plugin folder to the web plugins directory:
    cp -r sbl_vorlume_pdb ~/sblwebplugins/
  • 2) Ensure the web plugins launcher is installed. Then run:
    sbl-web-plugins-launcher
  • 3) The plugin will be listed in the dashboard at http://127.0.0.1:8700/.

Installing an individual PyMOL plugin

PyMOL plugins using pymol.Qt can be installed directly via PyMOL's plugin system:

  • 1) Locate the plugin folder (e.g., sbl_vorlume_pdb) containing __init__.py and gui.py.
  • 2) Copy the entire plugin folder to PyMOL's startup directory.
  • 3) Restart PyMOL. The plugin will appear under Plugin menu with its registered name.

Alternatively, use PyMOL's built‑in Plugin Manager:

  • 1) In PyMOL, go to Plugin → Plugin Manager → Install New Plugin.
  • 2) Select Install from local file and choose the plugin's __init__.py file.
  • 3) Restart PyMOL to activate the plugin.

Generating new plugins

Plugin layout


Plugin: structure. A generated plugin is organized into four areas (Fig. GUI architecture):

  • 1) Input area — receives options and file paths.
  • 2) Output area — displays results produced by the executable (text, tables, figures, pdfs, htmls).
  • 3) 3D molecular graphics area — displays structure/graphics outputs.
  • 4) Update area - contains widgets to refresh outputs and 3D graphics.

We adopt the Model–View–Presenter (MVP) pattern for both the generator and the generated plugins:

  • Model: data and state — the formal JSON specification and runtime metadata.
  • View: the concrete widget layout rendered by the target toolkit.
  • Presenter: generated glue that binds Model and View, handles user events, builds and executes the CLI, runs post‑analysis, and refreshes outputs and viewers.


Plugin at work: data flow.

  • 1) User options are collected in the input area and translated into a CLI command.
  • 2) After execution, an optional post‑analysis script extracts and formats results for the output area.
  • 3) Two callbacks are then triggered: one populates output panels; the other drives 3D visualization.
  • 4) The update area allows users to manually refresh outputs and 3D graphics.

On desktop platforms, VMD and PyMOL execute the viewer scripts (.vmd or .py) produced by the post analysis script and render them in their display windows. In the web platform, a Panel application embeds or links to an NGLViewer / Three.js viewer page served on a standalone port. The viewer is refreshed based on the json file generated by the post‑analysis script.

GUI: generic architecture
A plugin consists of four main areas: (1) the Input area enabling the specification of input data, (2) The output are to display statistics and figures, (3) The 3D molecular graphics area, and (4) The update area for refreshing outputs.

The three steps


Plugin design: the three steps. A typical workflow comprises three phases:

  • Step 1) Design — choose input options, desired outputs, update options, and the arrangement of input/output/update areas.
  • Step 2) Formal specification — automatically generate a JSON specification of the plugin layout and behavior.
  • Step 3) Code generation — produce concrete plugin code for the selected platform(s).

The user designed associated files are as follows:

  • layout.ui — Qt Designer file defining geometry for input/output/update areas.
  • selected_flags.txt — plain‑text list of CLI flags to expose in the input area.
  • update_area_flags.txt — optional plain‑text list of flags for the post‑analysis script.
  • post_analysis.py — Python script that converts raw CLI outputs into panel‑ready and viewer-ready files.


The script run_generator.sh A convenience script (run_generator.sh) in Applications/ <AppName>/plugins automates code generation. The command: bash run_generator.sh, which run in the plugins folder will produce the three GUIs: Tkinter for VMD, PyQt6 for PyMOL, and Panel for the web under the folder Applications/ <AppName>/plugins/step3_generated_code. Fig. GUI design workflow shows the design workflow of SBL GUI plugins using an example.


Files generated. For each application, say SBL_DIR/Application_XXX, the $\text{\texttt{plugins}}$ directory contains exactly three subfolders hosting the generated files:

  • $\text{\texttt{step1\_design/}}$: inputs for the generator, including $\text{\texttt{layout.ui}}$, $\text{\texttt{selected\_flags.txt}}$, and optional $\text{\texttt{update\_area\_flags.txt}}$.
  • $\text{\texttt{step2\_formal\_spec/}}$: the auto–generated formal specification in JSON format.
  • $\text{\texttt{step3\_generated\_code/}}$: the auto–generated plugin code, packaged as $\text{\texttt{vmd.tar.gz}}$, $\text{\texttt{pymol.tar.gz}}$, and $\text{\texttt{web.tar.gz}}$.

GUI: design example
An example of using the sbl-gui-generator.py to automatic create cross‑platform graphical user‑interface (GUI) plugins for command‑line applications.

Example -

Preparing the input files


Step 1: Preparing layout.ui. Requirements:

  • Each area (input, output, optional update) must have a geometry rectangle (x, y, width, height).
  • The input area must be named inputPanel. The update area is optional and must be named updatePanel.
  • Output widgets must be named by type and index: outputText\d *, outputFigure\d *, outputPDF\d *, outputTable\d *, outputHtml\d *, or outputHtmlTabs.

This .ui file is created using Qt Designer and saved as layout.ui.

The following is the layout file for the plugin sbl_spectraldom from the package Spectral_domain_explorer :

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1332</width>
<height>738</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QFrame" name="inputPanel">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>611</width>
<height>701</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
</widget>
<widget class="QFrame" name="outputPDF1">
<property name="geometry">
<rect>
<x>680</x>
<y>110</y>
<width>631</width>
<height>611</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
</widget>
<widget class="QFrame" name="updatePanel">
<property name="geometry">
<rect>
<x>680</x>
<y>20</y>
<width>631</width>
<height>81</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>


Step 1: Preparing selected_flags.txt. This file lists the options to expose in the inputPanel.

Note
Avoid underscores (_) in flag names; use hyphens (-) instead.

The following is the selected_flags.txt file for the plugin sbl_spectraldom from the package Spectral_domain_explorer-package:

--mode
--pdb-fpath
--load-chains
--msa-fpath
--structures-fpath
--kmin
--kmax
--dmax-contacts
--stiffness-cov
--stiffness-ncov
--stiffness-hbond
--stiffness-mode
--post
--output-dir

Mapping to widgets:

  • Options with parameters → an Entry (file/directory options also provide a Browse Button) plus a tooltip Label (shows CLI help on hover).
  • Boolean flags → a Checkbox plus a tooltip Label.
  • Each flag’s controls are grouped in a Frame.
  • An additional free‑form Entry accepts extra CLI options.
  • A Run Button is appended, with a tooltip displaying the CLI header.

Advanced usage of selected_flags.txt. Beyond the simple "one flag per line" format shown above, an optional CSV‑style format allows you to control tooltips, default values, and widget behaviour on a per‑flag basis. Each non‑comment line can contain up to four comma‑separated fields:

flag, info message, default value, entry type

Blank fields fall back to the values parsed from the CLI --help output. Comment lines starting with # are ignored. An example is provided :

# flag name, info message, default value, entry type
-f,,,isInputStructure
-P, chains of first partner (such as: AB),,
-P, chains of second parter (such as: C),,
--water,,true,
--hetatoms,,false,
-d,,,isOutputDir

The four columns have the following meaning:

  • flag: the CLI flag to expose (exactly as in --help).
  • info message: optional tooltip text overriding the description taken from --help (leave empty to use the help description).
  • default value: optional default value overriding the one from --help. For flags with an argument this fills the corresponding Entry; for boolean flags (no argument) this controls the initial state of the Checkbox (accepted truthy values include 1/true/yes/on/y/t; false values include 0/false/no/off/n/f).
  • entry type: optional semantic tag used by the generators to specialise behaviour. For instance, isInputStructure forces a file chooser to be attached to the Entry, and isOutputDir forces a directory chooser.


Step1: Preparing post_analysis.py. To align with widget names, the post‑analysis script must produce files using the following conventions:

  • outputFigure1outputFigure1.png (static image) and/or outputFigure1.dat (data for interactive figures)
  • outputPDF1outputPDF1.pdf (PDF)
  • outputTable1outputTable1.csv (table)
  • outputText1outputText1.txt (text)
  • outputHtml1outputHtml1.html (standalone HTML)
  • 1.vmd / 1.py / 1.json → viewer script or json for molecular graphics area

Reference script for the SBL Intervor plugin: $SBL_DIR/Applications/Space_filling_model_interface/scripts/intervor_abw_atomic_plugin_post_analysis.py

Example call:

intervor_abw_atomic_plugin_post_analysis.py \
-d /path/to/sbl_output \
-p /path/to/post_analysis_output \
-w 80 \
--viewer vmd/pymol/ngl
Note
The generated plugin invokes the post‑analysis script with -d (executable output directory), -p (post‑analysis output directory), --post-flag <value from Update area, if present>, and --viewer <vmd|pymol|ngl> depending on the target platform. The plugin then refreshes its Output area and the molecular viewer based on the files produced by the post‑analysis script.

The following is the plugin_post_analysis.py file for the plugin sbl_spectraldom from the package Spectral_domain_explorer-package:

#!/usr/bin/env python3
"""Runner for Spectraldom post-analysis.
This script only parses CLI arguments and delegates all logic to the
SpectraldomPostAnalyzer service.
"""
import argparse
import sys
from pathlib import Path
from spectraldom_post_analysis_services import SpectraldomPostAnalyzer
def parse_args() -> argparse.Namespace:
"""Parse command-line arguments for spectraldom post-analysis."""
parser = argparse.ArgumentParser(
description="Process spectraldom outputs: extract quality plot, local maxima, and prepare viewer assets."
)
parser.add_argument(
"-d", "--output-dir", required=True, help="Directory containing run outputs"
)
parser.add_argument(
"-p", "--post-dir", required=True, help="Directory to store post-analysis outputs"
)
parser.add_argument(
"-k", "--k-value", type=int, help="Desired k-value (overrides automatic selection from local maxima)"
)
parser.add_argument(
"--viewer", choices=["vmd", "pymol", "ngl"], help="Viewer type to prepare assets for"
)
return parser.parse_args()
def main() -> int:
"""CLI entrypoint for spectraldom post-analysis."""
args = parse_args()
out_dir = Path(args.output_dir).resolve()
post_dir = Path(args.post_dir).resolve()
if not out_dir.exists():
print(f"Error: Output directory does not exist: {out_dir}", file=sys.stderr)
return 1
analyzer = SpectraldomPostAnalyzer(out_dir, post_dir, k_value=getattr(args, "k_value", None), viewer=getattr(args, "viewer", None))
rc = analyzer.run()
if rc == 0:
print(f"Post-analysis complete. Files saved to: {post_dir}")
else:
print(
f"Post-analysis finished with issues. Check output for details.",
file=sys.stderr,
)
return rc
if __name__ == "__main__":
sys.exit(main())


Step1: Preparing update_area_flags.txt file. This optional file lists flags to expose in the updatePanel, which parameterize the post‑analysis step to update the outputs.

The following is the update_area_flags.txt file for the plugin sbl_spectraldom from the package Spectral_domain_explorer-package:

-k, K Value, K Value corresponds to different clustering results.

Generating the plugin code


Step 2: Generating the plugin formal specification JSON file. Once layout.ui, intervor_abw_atomic_plugin_post_analysis.py, selected_flags.txt, and update_area_flags.txt are ready, generate platform‑specific code via running the bash script run_generator.sh in the plugins folder:

./run_generator.sh --compress all

The above process call the main generator script sbl-gui-generator.py,

sbl-gui-generator.py \
--ui layout.ui \
--exe sbl-intervor-abw-atomic.exe \
--flags selected_flags.txt \
--post-script intervor_abw_atomic_plugin_post_analysis.py \
--update-flags update_area_flags.txt \
--format pyqt tkinter panel-ngljs \
--gui-output step3_generated_code/

This first produces the formal specification JSON file intervor-ABW-atomic_gui.json, which will finally located in the folder Applications/ <AppName>/plugins/step2_formal_spec.


Step 3: Generating the plugin code. Then it produces the platform-specific plugin code based on the formal specification JSON file. This creates the three GUIs: VMD, PyMOL, and Web. The generated code is located under the folder Applications/ <AppName>/plugins/step3_generated_code as vmd.tar.gz, pymol.tar.gz, and web.tar.gz.

(Advanced) Formal specifications: details

The used defined files layout.ui and selected_flags.txt and update_area_flags.txt are converted to a JSON specification that records widget types, geometry, and metadata.

In the following, we present the types and syntax used for this conversion.

Types

The following widget types in JSON currently includes:

  • Input widgets (5): "Label", "Frame", "Entry", "Button", "Checkbox".
  • Output widgets (4): "TextArea", "ImageView", "TableView", "HtmlView".

The JSON file consists of four sections–the first three being derived from the ui file:


Input panel section. Based on selected_flags.txt, we populate the children of inputPanel: flags with parameters from the CLI become an Entry (file/directory entries are paired with a Button to open a selector) followed by a tooltip Label that shows the help text on hover; boolean flags become a Checkbox plus a tooltip Label. Each flag’s controls are grouped in a Frame. An additional free–form Entry accepts extra CLI options. Finally, a Run Button is added with a tooltip showing the CLI header.


Output panel section. This section declares the widgets that present artifacts produced by the post-analysis step after the CLI finishes. Concretely, output panels are a set of typed widgets: TextArea, ImageView, TableView, each with a file pattern and refresh policy. After a run, the generator calls refresh_output_panel method: it scans output_dir for the newest files matching the source glob of each widget and updates the views accordingly.

  • TextArea: shows parsed text information from the CLI log and outputs.
  • ImageView: shows figures written by the post script (e.g., .png). (If a sidecar data file .dat exists with the same basename, the generator upgrades the view to an interactive plot (matplotlib in Tkinter/PyQt; Plotly in Panel) with pan/zoom and save-as.)
  • TableView: loads tabular outputs (.csv) into a sortable, filterable table (QTableWidget in PyQt, ttk/Tabulator in Tkinter/Panel).
  • HtmlView: renders standalone HTML outputs (.html) in an embedded browser (QWebEngineView in PyQt6, tkinterweb in Tkinter, native Panel HTML pane).


Update panel section. This optional section declares widgets that control the post-analysis step. Based on post_analysis_flags.txt, these controls mirror input widgets but are not passed to the CLI; instead, they parameterize the post‑analysis. Changing any of these parameters and clicking the Update button triggers a re‑run of the post‑analysis and selectively refreshes the affected output widgets and/or the 3D view.

All output widgets are read‑only and refresh automatically after a successful run or when the Update area triggers a refresh.


Metadata section. Includes a metadata block (e.g., exe_name, post_script).

Mapping JSON to backends

As already discussed, we automatically generated code for the targeted platform from the formal specification (Fig. fig-plugin-generation). The mapping between formal types and those used by the platforms is as follows:

Mapping between JSON widget types to types used by the backends.

(Advanced) Workflow and associated modules

We detail the implement of the sbl-gui-generator method here.

Workflow

The main entry point is $\text{\texttt{\$SBL\_DIR/Applications/Plugin\_manager/scripts/sbl-gui-generator.py}}$.

A convenience script, $\text{\texttt{run\_generator.sh}}$, in the same directory orchestrates the generation of all plugins; it delegates to each application’s own $\text{\texttt{plugins/run\_generator.sh}}$.

To generate a concrete plugin for an application, the generator copies the appropriate templates and injects application–specific pieces into $\text{\texttt{mvp/config.py}}$ and $\text{\texttt{mvp/ui/widget\_builders.py}}$.

Plugin manager modules. (A) Design phase. Using a layout editor, the designer (i) arranges the relative positions and sizes of widgets for the Input and Output areas; (ii) selects the CLI options (flags and arguments) to expose in the Input area; and (iii) specifies post–analysis parameters to surface in the optional Update area, whose changes selectively refresh the Output area and/or the 3D display. (B)Generation of the formal specification. A formal specification of the layout for the Input and Output areas is automatically generated. Plugin generation. (C) The python code of plugins is automatically generated. From the JSON, we synthesize frontend and backend code for the GUI libraries targeted (pymol.Qt, PyQt6, tkinter, or Panel). Widget types are mapped to their platform–specific counterparts and the generated backend logic constructs the CLI command, executes it, runs the post–analysis (if any), refreshes outputs, and displays results in the molecule viewer.

Modules

The previously described workflow is implemented by the following modules (Fig. Code modules); each has a clear responsibility and can be combined to target different GUI frameworks:

  1. $\text{\texttt{scripts/sbl-gui-generator.py}}$: entry point that invokes SBL::gui_generator_manager::GUIGeneratorManager.
  2. SBL::gui_generator_manager::GUIGeneratorManager ( $\text{\texttt{python/SBL/gui\_generator\_manager.py}}$): orchestrates the pipeline:
  3. $\text{\texttt{python/SBL/gui\_json\_schema.py}}$: JSON schema, dataclasses, and validation helpers.
  4. $\text{\texttt{python/SBL/gui\_ui2json.py}}$ and $\text{\texttt{gui\_ui2json\_*.py}}$: UI parsing and helpers for building the formal specification.
  5. $\text{\texttt{python/SBL/gui\_codegen.py}}$: dispatch to backends. Backends include:
  6. $\text{\texttt{gui\_panels\_generator\_*.py}}$ and $\text{\texttt{gui\_widgets\_generator\_*.py}}$: code‑emission helpers per framework.
  7. $\text{\texttt{gui\_codegen\_utils.py}}$: shared utilities for code generation.

Layout geometry

For PyQt6 and Tkinter, absolute positions and sizes from the $\text{\texttt{.ui}}$ file are stored in JSON for codes generation.

For Panel, which is row/column–based, we quantize absolute widget coordinates onto a fixed $5,px \times 5,px$ logical grid. Coordinates are translated so that the minimum top‑left corner defines the origin $(\min x, \min y)$.

The grid size is $\lceil(\max x -\min x)/5 \rceil$ columns by $\lceil(\max y -\min y)/5 \rceil$ rows.

A widget with bounding box $(x, y, \text{width}, \text{height})$ is mapped to 1‑based, inclusive indices: col_start = floor((x − min_x)/5) + 1; col_end = floor((x + width − 1 − min_x)/5) + 1; row_start = floor((y − min_y)/5) + 1; row_end = floor((y + height − 1 − min_y)/5) + 1.

Indices are clamped to the grid bounds. An optional overlap resolver relocates later widgets (row‑major scan) to the first free slot that accommodates their span; if no slot exists, new rows are appended at the bottom. The final indices are stored in JSON for Panel code generation. Widths/heights smaller than one pixel are treated as $1,px$ to ensure each widget occupies at least one grid cell.

For PyQt6 and tkinter, absolute positions and sizes from the .ui file are used directly. For Panel, which is row/column–based, we assign grid coordinates from the absolute widget positions.

Templates and MVP layout (VMD, PyMOL, Web)

Templates root. Target–agnostic templates live under $SBL_DIR/Applications/Plugin_manager/template_files/ with three subfolders: vmd/, pymol/, and web-ngljs/, web-threejs.

VMD (Tkinter). Each VMD plugin template provides gui.py, pkgIndex.tcl, and sbl_pluginname.tcl, plus an mvp/ subtree with: config.py, presenter.py, services/, and ui/.

PyMOL (PyQt6). Same mvp/ structure as VMD (no Tcl files), with PyQt6–based UI code.

Web-NGL (Panel). Mirrors the same mvp/ layout and includes an ngl_viewer/ directory for NGL static assets.

Web-Three.js (Panel). Mirrors the same mvp/ layout and includes a threejs_viewer/ directory for Three.js static assets.

NGL viewer assets. The NGL static assets are developed in $SBL_DIR/Applications/Plugin_manager/viewer_app/ (TypeScript). Build them with:

cd $SBL_DIR/Applications/Plugin_manager/viewer_app/
npm install
npm run build

The build outputs to $SBL_DIR/Applications/Plugin_manager/template_files/web/ngl_viewer/.

(Advanced) Web apps and Panel

The PANEL framework

PANEL provides a unified framework to build dashboards and interactive applications in Python, leveraging the PyData ecosystem (e.g., Pandas, Matplotlib, Plotly). In our workflow, GUI-generation outputs are translated into Panel widgets and layout primitives. A typical launch command is:

panel serve app.py

While suitable for prototyping, this mode lacks production features such as HTTPS, traffic routing, process supervision and standard web port exposure. These limitations are addressed via our deployment stack (Sec. Deployment Stack).

NGL viewer integration

Our Panel-based web GUIs embed advanced 3D viewers — for example NGL Viewer (molecular structures) or Three.js (geometry) — via iframes. Data (PDB/JSON) are exported by the post-analysis routine, the viewer is re-loaded with a cache-busting query parameter, and the rendering is handled entirely client-side. This separation of execution (server) and rendering (browser) simplifies deployment under web security policies.

Web visualization (NGL Viewer / three.js). For Panel targets, the generated interface embeds an NGL Viewer iframe for molecular structures or a three.js iframe for geometric primitives. The iframe is reloaded after each execution to present the updated molecular model or geometry output.


Deployment Stack

Since the default panel serve app.py mode is intended for development rather than production, our deployment uses a robust two-tier production stack.


Two-tier architecture.

  • Tier 1 — FastAPI launcher. A central FastAPI application (web_plugins_launcher/launcher.py) serves an HTML dashboard listing all discovered plugins and exposes a REST API for plugin lifecycle management. It runs on a single port (8700 in local mode, 9000 in remote/production mode).
  • Tier 2 — Per-plugin Panel servers. Each plugin runs as an independent Panel/Bokeh server spawned on demand via panel serve gui.py. In remote mode each instance gets a port from a fixed pool (9100–9199) for deterministic Nginx routing; in local mode the OS assigns a free ephemeral port to avoid collisions when multiple users run their own launcher on a shared machine.

Launcher API and plugin discovery


REST API. The launcher exposes the following endpoints:

Method Path Purpose
GET / HTML dashboard listing all discovered plugins
POST /api/start Start (or reuse) a plugin server; returns JSON with id, url, port
POST /api/stop Stop a plugin server and release its port
GET /healthz Health check — returns {"ok":true,"running":N}
GET /api/ports Debug — shows the full port-pool state


Plugin discovery. The launcher discovers plugins by scanning for directories that contain a gui.py file. It searches the following roots in order:

  • The launcher's own directory.
  • Directories listed in the environment variable SBL_WEB_PLUGINS_DIRS (colon-separated).
  • The default fallback path ~/sblwebplugins.

A directory is recognized as a plugin if it contains gui.py. Directories named templates, static, __pycache__, nginx, or systemd are skipped. Optional icon files (icon.png, icon.svg, etc.) are picked up and served on the dashboard.

Port allocation

Port allocation differs between operating modes:


Local mode — OS-assigned ports. Each plugin server is assigned a free ephemeral port by the operating system (socket.bind("127.0.0.1",\ 0)). This eliminates port conflicts when multiple users run independent launcher instances on the same shared machine. The port registry is not used in local mode.


Remote mode — fixed-pool allocation. Port allocation is managed by a file-backed JSON registry (port_registry.py) using POSIX file locking (fcntl.flock) for mutual exclusion across concurrent threads and processes.

  • Port range: 9100–9199 (configurable via SBL_PORT_RANGE_START / SBL_PORT_RANGE_END).
  • Backing store: port_registry.json — located under /var/lib/sblwebplugins/.
  • Allocation: On each request, stale entries (dead PIDs detected via os.kill(pid,0)) are reaped first, then the lowest available port is assigned.
  • Error handling: A PortPoolExhausted exception is raised if all 100 ports are in use.

Plugin lifecycle and auto-cleanup


Plugin start. When a user clicks a plugin card on the dashboard:

  • 1) If the plugin is already running and its process is alive, the existing URL is returned immediately.
  • 2) Otherwise, a port is allocated from the pool, and panel serve gui.py is spawned as a subprocess in its own process group (os.setsid).
  • 3) The launcher polls the port for up to 8 seconds until the Panel server accepts TCP connections.
  • 4) In remote mode, the Nginx routing map is updated and Nginx is reloaded.
  • 5) The plugin URL is returned to the browser, which opens it in a new tab.


Plugin stop.

  • 1) The entire process group is sent SIGTERM, with a 5-second grace period followed by SIGKILL if needed.
  • 2) The port is released back into the pool.
  • 3) In remote mode, the Nginx map is updated accordingly.


Browser-side heartbeat. A JavaScript heartbeat on the dashboard page polls every 1 second to detect when a plugin's browser tab has been closed. Upon detection, it automatically calls /api/stop to shut down the server and free resources. On page unload (beforeunload), all active plugin windows are closed and stop requests are sent with keepalive:true to ensure delivery.


Launcher shutdown. On shutdown (via signal or lifespan exit), the launcher iterates all running plugin processes, kills each process tree, releases all ports, and — in remote mode — writes an empty Nginx map so that no traffic is routed to stale upstream ports.

Nginx reverse proxy

In production (remote) mode, an Nginx reverse proxy sits in front of the entire stack. The routing is organized as follows:

Route Target Notes
/plugins/ FastAPI launcher (port 9000) Dashboard and API
/plugins/api/ FastAPI launcher (port 9000) REST endpoints
/plugins/ngl/ Static file alias NGL/Three.js viewer assets
/plugins/app/<name>/ Panel server (port 9100–9199) Per-plugin; full WebSocket support


Dynamic Nginx map. Each time a plugin starts or stops, the launcher regenerates an Nginx map configuration file (sbl_plugins_map.conf) that maps plugin names to their assigned 127.0.0.1:<port> upstream. The file is written atomically (write to .tmp then rename) and Nginx is reloaded via sudo /bin/systemctl reload nginx.


WebSocket support. The Panel/Bokeh server requires WebSocket connections for real-time interactivity. The Nginx location blocks for /plugins/app/ include proxy_http_version 1.1, Upgrade, and Connection headers, with 300-second read/send timeouts.

Local vs. remote operating modes

The launcher operates in two modes, controlled by the SBL_SERVER_MODE environment variable or the --mode CLI flag:

Aspect Local Remote
Launcher port 8700 9000
URL prefix (none) /plugins
Nginx Not used Required reverse proxy
Plugin URL http://127.0.0.1:&lt;port&gt;/gui?token=… (direct) /plugins/app/<name>/ (via Nginx)
NGL viewer files Served by FastAPI from ~/.sbl/ Served by Nginx from /var/lib/sblwebplugins/ngl/
Plugin port allocation OS-assigned ephemeral port Fixed pool 9100–9199 (port_registry.json)
Authentication Startup token (SBL_AUTH_TOKEN) Nginx / external auth
WebSocket origins localhost, 127.0.0.1 SBL_PUBLIC_HOST (default: sbl.inria.fr)

In local mode, the launcher is started via:

sbl-web-plugins-launcher

and the dashboard URL (including a security token) is printed to the terminal, e.g.:

Starting SBL Plugins Launcher at http://127.0.0.1:8700/?token=a1b2c3… [mode=local]

Token authentication (local mode)

On shared machines (e.g. campus HPC workstations), binding to 127.0.0.1 is not sufficient to isolate users: any local user on the same host can connect to an open loopback port. To prevent unauthorized access, the launcher generates a random security token at startup (following the same model as Jupyter Notebook).


How it works.

  • On startup the launcher generates a cryptographically random token (64 hex characters) and publishes it via the SBL_AUTH_TOKEN environment variable.
  • The full URL including the token is printed to the terminal. Opening this URL in a browser sets a session cookie so that subsequent requests (API calls, static assets) do not require the token in the URL.
  • Every HTTP request to the launcher is validated by a FastAPI middleware: either the token query parameter or the sbl_token cookie must match the expected value. Requests that fail validation receive a 403 response.
  • When a plugin is started, its URL includes the token as a query parameter (/gui?token=…). The child Panel server validates the token via a Bokeh auth module (panel_auth.py).
  • The token is also persisted at ~/.sbl/web_plugins_launcher/token (mode 0600) for programmatic retrieval by scripts.


Sharing with colleagues. To grant a colleague access to your running launcher instance, simply share the startup URL (including the token). To revoke access, restart the launcher — a new token is generated each time.


Multi-user port isolation. In local mode, both the launcher port and all plugin ports are assigned by the operating system, so multiple users can run independent instances simultaneously without port conflicts.

Server installation

For production deployment, the install script sbl-install-server.sh (run as root) performs the following steps:

  • 1) Creates runtime directories /var/lib/sblwebplugins/{ngl,sessions} owned by the sbl user.
  • 2) Installs a sudoers rule (/etc/sudoers.d/sbl-nginx) so the sbl user can reload Nginx without a password.
  • 3) Installs the Nginx map file to /etc/nginx/conf.d/.
  • 4) Installs the Nginx location snippet to /etc/nginx/proxy.d/, tests, and reloads Nginx.
  • 5) Installs a systemd service unit (sbl-web-plugins-launcher.service), enables and starts it.

The systemd unit runs under user sbl, sets SBL_SERVER_MODE=remote and SBL_PUBLIC_HOST=sbl.inria.fr, and is configured to restart automatically on failure.

After installation, the production instance is accessible at https://sbl.inria.fr/plugins/. Verification:

curl -s http://127.0.0.1:9000/healthz
curl -sk https://sbl.inria.fr/plugins/
journalctl -u sbl-web-plugins-launcher -n 80 --no-pager