Skip to content

Repository files navigation

Python GUI Template

A batteries-included starting point for scientific desktop GUIs in Python, built on Traits, TraitsUI, Matplotlib and Mayavi.

CIPythonLicense: MITCode style: ruff

Clone it, run it, then replace the example panels with your own. You get a resizable two-pane window with an embedded Matplotlib canvas (including the standard navigation toolbar), an optional Mayavi 3D scene, and tabbed control panels wired to the display — all of which usually take a frustrating afternoon to assemble from scratch.

Features

  • Embedded Matplotlib canvas with the full navigation toolbar, in a resizable split pane.
  • Runs under Qt or wx. The Matplotlib editor picks the right canvas for whichever ETS toolkit is active, so you are not locked into wxPython.
  • Optional 3D. Mayavi powers a 3D scene tab when installed; when it is not, the tab degrades to an install hint and everything else keeps working.
  • Live-updating controls. Marker colour, style and size apply to the current plot immediately; panel controls redraw as you drag them.
  • Two worked example panels covering the editors you will actually reach for — ranges, enums, booleans, buttons, directory and file pickers, enabled_when, read-only readouts and CSV export.
  • Tested and linted, with a headless pytest suite and CI.

Requirements

  • Python 3.9 or newer
  • One GUI toolkit: Qt (recommended) or wx
  • TraitsUI 7.2+ — older versions render black backgrounds and other oddities

Installation

git clone https://github.com/bgriffen/PythonGUITemplate.git
cd PythonGUITemplate
python -m venv .venv &&source .venv/bin/activate

Then install with the toolkit you want. Pick one:

pip install -e '.[qt]'# Qt via PySide6 - recommended, installs cleanly everywhere
pip install -e '.[wx]'# wxPython - the original backend

Qt is the easier of the two to install; wxPython frequently needs to build from source on Linux. If you install both, set ETS_TOOLKIT=qt (or wx) to say which one wins.

Optional: the 3D scene

pip install -e '.[qt,3d]'

This pulls in Mayavi and VTK — a large download and the most fragile part of the stack. It is entirely optional: without it the app runs normally and the Mayavi tab shows installation instructions instead.

Running

python main.py

or, equivalently:

python -m pyguitemplate
pyguitemplate # console script, after installing

On macOS with the wx backend you may need pythonw main.py instead, so the process is treated as a proper windowed application. This is not necessary under Qt.

Project layout

pyguitemplate/
├── app.py # ApplicationMain: the window, the split layout, shared plot state
├── mpl_editor.py # Toolkit-agnostic TraitsUI editor embedding a Matplotlib figure
├── scene.py # Mayavi 3D tab, with a fallback when Mayavi is missing
├── colors.py # ColorTrait -> Matplotlib colour conversion
└── panels/
├── first_panel.py # Example: a tour of the common Traits editors
└── second_panel.py # Example: a small signal generator with CSV export
tests/ # Headless pytest suite
main.py # Launcher

Adding your own panel

Panels are plain HasTraits classes. They receive the ApplicationMain instance so they can drive the shared display, and they declare a View that lays out their controls.

# pyguitemplate/panels/my_panel.pyimportnumpyasnpfromtraits.apiimportAny, Button, HasTraits, Rangefromtraitsui.apiimportItem, ViewclassMyPanel(HasTraits):
main=Any()
exponent=Range(1.0, 4.0, 2.0)
plotbutton=Button("Plot")
view=View(
Item("exponent", label="Exponent"),
Item("plotbutton", show_label=False),
)
defreplot(self):
x=np.linspace(0.0, 5.0, 200)
self.main.plot(x, x**self.exponent)
# Traits calls this automatically whenever `exponent` changes.def_exponent_changed(self):
self.replot()
def_plotbutton_fired(self):
self.replot()
def__init__(self, main, **kwargs):
super().__init__(**kwargs)
self.main=main

Then register it in app.py — add an Instance trait, a _my_panel_default factory, and an Item in right_panel:

my_panel=Instance(MyPanel)
def_my_panel_default(self):
returnMyPanel(self)
right_panel=Tabbed(
...,
Item("my_panel", style="custom", label="My Tab", show_label=False),
)

Two conventions worth following:

  • Plot through main.plot(x, y) rather than touching the figure yourself. It records the line it draws, which is what lets the marker controls restyle it afterwards.
  • Run colours through to_mpl_color() if you read a ColorTrait. TraitsUI stores a toolkit-native QColor/wx.Colour, and Matplotlib rejects both.

Testing

pip install -e '.[qt,dev]'
pytest

The suite never opens a window — it builds the trait objects and inspects the Matplotlib figure directly — so it runs headless. On a machine with no display, set QT_QPA_PLATFORM=offscreen.

ruff check .# lint
ruff format .# format

Troubleshooting

SymptomCause and fix
ImportError naming a toolkit on startupNo GUI toolkit installed. pip install -e '.[qt]'.
Black backgrounds, misdrawn widgetsTraitsUI older than 7.2. Upgrade it.
Mayavi tab shows an install hintExpected without Mayavi. pip install -e '.[qt,3d]' to enable it.
Wrong toolkit picked when both installedSet ETS_TOOLKIT=qt or ETS_TOOLKIT=wx.
Window does not focus on macOS (wx)Launch with pythonw main.py, or use the Qt backend.
qt.qpa.plugin errors on a headless Linux boxSet QT_QPA_PLATFORM=offscreen.

Further reading

License

MIT — see LICENSE.

About

A batteries-included starting point for scientific desktop GUIs in Python, built on Traits, TraitsUI, Matplotlib and Mayavi.

Topics

Resources

Stars

11 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages