Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
13b6513
silx.gui.plot.backends: Add pygfx/WGPU backend for 2D plotting
physwkim Mar 12, 2026
ecfe36e
Revert non-backend changes per review feedback
physwkim Mar 12, 2026
bef5d8d
Fix black formatting: remove extra blank line in image.py
physwkim Mar 12, 2026
0971cc1
Clean up pygfx backend PR: handle autoscale clim, remove examples
physwkim Mar 12, 2026
dd15d56
Merge branch 'main' into pygfx-backend-pr
physwkim Mar 12, 2026
476bb71
Add pygfx backend test infrastructure and test classes
physwkim Mar 19, 2026
b1851cf
Implement image and triangle picking in pygfx backend
physwkim Mar 19, 2026
8f8a6e3
Fix pygfx backend stale renderer during processEvents
physwkim Mar 19, 2026
70f8d9b
Flush dirty items in paintEvent for cross-platform compatibility
physwkim Mar 19, 2026
8ef4e91
Vectorize error bar segments and fix scalar/shaped error handling
physwkim Mar 19, 2026
1951689
Add pygfx backend examples
physwkim Mar 19, 2026
35d26b5
Fix pygfx examples: correct API usage, add colormap benchmark
physwkim Mar 19, 2026
7f7592f
Skip pygfx tests when wgpu device is not available
physwkim Mar 19, 2026
831f32f
Handle unavailable backend in testSwitchBackend
physwkim Mar 19, 2026
00f16fe
Add lavapipe software Vulkan to Linux CI for pygfx tests
physwkim Mar 19, 2026
c2dd1ad
Remove GPU device check, rely on lavapipe for CI pygfx tests
physwkim Mar 19, 2026
01bd077
Restore GPU device check and backend switch guard (lavapipe not working)
physwkim Mar 19, 2026
741bed5
Use VulkanCI (SwiftShader) instead of lavapipe for pygfx CI tests
physwkim Mar 19, 2026
5e618a7
Restore GPU device check, remove VulkanCI (SwiftShader not working wi…
physwkim Mar 19, 2026
ca29dff
Add pygfx backend for plot3d module
physwkim Mar 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions examples/pygfx_backend/01_basic_curves.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Basic curve plotting with pygfx backend.

Demonstrates: multiple curves, colors, line widths, symbols, fill, legend.
"""

import numpy
from silx.gui import qt
from silx.gui.plot import Plot1D


def main():
app = qt.QApplication([])

plot = Plot1D(backend="pygfx")
plot.setWindowTitle("pygfx - Basic Curves")
plot.setGraphTitle("Trigonometric Functions")
plot.setGraphXLabel("X")
plot.setGraphYLabel("Y")

x = numpy.linspace(0, 4 * numpy.pi, 500)

# Solid line
plot.addCurve(x, numpy.sin(x), legend="sin(x)", color="blue", linewidth=2)
# Dashed line with symbols
plot.addCurve(
x[::20],
numpy.cos(x[::20]),
legend="cos(x)",
color="red",
linewidth=1.5,
linestyle="--",
symbol="o",
)
# Filled curve
plot.addCurve(
x,
0.5 * numpy.sin(2 * x),
legend="0.5*sin(2x)",
color="green",
linewidth=1,
fill=True,
)

plot.setActiveCurveHandling(False)
plot.resetZoom()
plot.show()
app.exec()


if __name__ == "__main__":
main()
52 changes: 52 additions & 0 deletions examples/pygfx_backend/02_image_display.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Image display with pygfx backend.

Demonstrates: 2D image with colormap, RGBA image, origin/scale, colorbar.
"""

import numpy
from silx.gui import qt
from silx.gui.plot import Plot2D


def main():
app = qt.QApplication([])

# --- Plot2D with colormap image ---
plot = Plot2D(backend="pygfx")
plot.setWindowTitle("pygfx - Image Display")
plot.setGraphTitle("2D Gaussian + Noise")
plot.setGraphXLabel("X")
plot.setGraphYLabel("Y")

# Generate a 2D Gaussian
size = 256
x = numpy.linspace(-3, 3, size)
y = numpy.linspace(-3, 3, size)
xx, yy = numpy.meshgrid(x, y)
image = numpy.exp(-(xx**2 + yy**2)) + 0.1 * numpy.random.random((size, size))

plot.getDefaultColormap().setName("viridis")
plot.addImage(image, origin=(-3, -3), scale=(6 / size, 6 / size))
plot.setKeepDataAspectRatio(True)
plot.resetZoom()
plot.show()

# --- RGBA image window ---
plot2 = Plot2D(backend="pygfx")
plot2.setWindowTitle("pygfx - RGBA Image")
plot2.setGraphTitle("RGBA Gradient")

rgba = numpy.zeros((200, 300, 4), dtype=numpy.uint8)
rgba[:, :, 0] = numpy.linspace(0, 255, 300)[numpy.newaxis, :] # R gradient
rgba[:, :, 1] = numpy.linspace(0, 255, 200)[:, numpy.newaxis] # G gradient
rgba[:, :, 2] = 128
rgba[:, :, 3] = 255
plot2.addImage(rgba)
plot2.resetZoom()
plot2.show()

app.exec()


if __name__ == "__main__":
main()
35 changes: 35 additions & 0 deletions examples/pygfx_backend/03_scatter_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Scatter plot with pygfx backend.

Demonstrates: scatter points with colormap values, symbol sizes.
"""

import numpy
from silx.gui import qt
from silx.gui.plot import Plot1D


def main():
app = qt.QApplication([])

plot = Plot1D(backend="pygfx")
plot.setWindowTitle("pygfx - Scatter Plot")
plot.setGraphTitle("Random Scatter with Colormap")
plot.setGraphXLabel("X")
plot.setGraphYLabel("Y")
plot.getDefaultColormap().setName("plasma")

numpy.random.seed(42)
n = 200
x = numpy.random.randn(n)
y = numpy.random.randn(n)
value = numpy.sqrt(x**2 + y**2) # distance from origin

plot.addScatter(x, y, value, legend="distance", symbol="o")
plot.setKeepDataAspectRatio(True)
plot.resetZoom()
plot.show()
app.exec()


if __name__ == "__main__":
main()
67 changes: 67 additions & 0 deletions examples/pygfx_backend/04_line_styles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Line styles and symbols with pygfx backend.

Demonstrates: all line styles (solid, dashed, dash-dot, dotted),
various symbols, line widths, gap colors.
"""

import numpy
from silx.gui import qt
from silx.gui.plot import PlotWidget


def main():
app = qt.QApplication([])

plot = PlotWidget(backend="pygfx")
plot.setWindowTitle("pygfx - Line Styles & Symbols")
plot.setGraphTitle("Line Styles and Symbols")
plot.setGraphXLabel("X")
plot.setGraphYLabel("Y")

x = numpy.linspace(0, 10, 100)

# Line styles
styles = [
("-", "solid"),
("--", "dashed"),
("-.", "dash-dot"),
(":", "dotted"),
]
for i, (style, name) in enumerate(styles):
y = numpy.sin(x) + i * 2.5
plot.addCurve(x, y, legend=name, linestyle=style, linewidth=2, symbol="")

# Gap color example
y = numpy.sin(x) + 10
plot.addCurve(
x,
y,
legend="dashed+gapcolor",
linestyle="--",
linewidth=2,
symbol="",
color="blue",
)

# Symbols (only those supported by silx SymbolMixIn)
symbols = ["o", ".", "+", "x", "d", "s", ",", "|", "_"]
x_sym = numpy.linspace(0, 10, 30)
for i, sym in enumerate(symbols):
y_sym = numpy.cos(x_sym) + 15 + i * 1.5
plot.addCurve(
x_sym,
y_sym,
legend=f"sym '{sym}'",
symbol=sym,
linestyle=" ",
color=f"C{i % 10}",
)

plot.setActiveCurveHandling(False)
plot.resetZoom()
plot.show()
app.exec()


if __name__ == "__main__":
main()
57 changes: 57 additions & 0 deletions examples/pygfx_backend/05_markers_shapes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""Markers and shapes with pygfx backend.

Demonstrates: point markers with text, x/y markers, shapes (rectangle, polyline).
"""

import numpy
from silx.gui import qt
from silx.gui.plot import PlotWidget
from silx.gui.plot import items


def main():
app = qt.QApplication([])

plot = PlotWidget(backend="pygfx")
plot.setWindowTitle("pygfx - Markers & Shapes")
plot.setGraphTitle("Markers, Text Labels and Shapes")

# Background image for visual reference
size = 100
xx, yy = numpy.meshgrid(numpy.linspace(0, 1, size), numpy.linspace(0, 1, size))
image = numpy.sin(10 * xx) * numpy.cos(10 * yy)
plot.addImage(image, origin=(0, 0), scale=(100 / size, 100 / size))
plot.getDefaultColormap().setName("gray")

# Point markers with text
plot.addMarker(20, 80, legend="marker1", text="Point A", color="red", symbol="o")
plot.addMarker(50, 60, legend="marker2", text="Point B", color="blue", symbol="d")
plot.addMarker(80, 80, legend="marker3", text="Point C", color="green", symbol="s")

# Horizontal and vertical markers
plot.addXMarker(30, legend="x_marker", text="X=30", color="yellow")
plot.addYMarker(40, legend="y_marker", text="Y=40", color="cyan")

# Rectangle shape
rect = items.Shape("rectangle")
rect.setPoints(numpy.array([(10, 10), (45, 45)]))
rect.setColor("red")
rect.setLineWidth(2)
plot.addItem(rect)

# Polyline shape
poly = items.Shape("polylines")
poly.setPoints(numpy.array([(55, 10), (70, 40), (85, 15), (95, 35)]))
poly.setColor("green")
poly.setLineWidth(2)
plot.addItem(poly)

plot.setGraphXLimits(-5, 105)
plot.setGraphYLimits(-5, 105)
plot.resetZoom()
plot.show()
app.exec()


if __name__ == "__main__":
main()
63 changes: 63 additions & 0 deletions examples/pygfx_backend/06_error_bars.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Error bars with pygfx backend.

Demonstrates: curves with x and y error bars.
"""

import numpy
from silx.gui import qt
from silx.gui.plot import Plot1D


def main():
app = qt.QApplication([])

plot = Plot1D(backend="pygfx")
plot.setWindowTitle("pygfx - Error Bars")
plot.setGraphTitle("Curves with Error Bars")
plot.setGraphXLabel("X")
plot.setGraphYLabel("Y")

x = numpy.linspace(0, 10, 30)

# Symmetric Y errors
y1 = numpy.sin(x)
yerr1 = 0.1 + 0.1 * numpy.abs(numpy.sin(x))
plot.addCurve(
x,
y1,
legend="sym Y error",
color="blue",
symbol="o",
yerror=yerr1,
linewidth=1.5,
)

# Asymmetric Y errors
y2 = numpy.cos(x) + 3
yerr_low = 0.2 * numpy.ones_like(x)
yerr_high = 0.5 * numpy.abs(numpy.cos(x))
plot.addCurve(
x,
y2,
legend="asym Y error",
color="red",
symbol="s",
yerror=numpy.array([yerr_low, yerr_high]),
linewidth=1.5,
)

# X errors
y3 = 0.5 * x - 1.5
xerr = 0.3 * numpy.ones_like(x)
plot.addCurve(
x, y3, legend="X error", color="green", symbol="d", xerror=xerr, linewidth=1.5
)

plot.setActiveCurveHandling(False)
plot.resetZoom()
plot.show()
app.exec()


if __name__ == "__main__":
main()
40 changes: 40 additions & 0 deletions examples/pygfx_backend/07_log_axes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Logarithmic axes with pygfx backend.

Demonstrates: log scale on X and Y axes, grid.
"""

import numpy
from silx.gui import qt
from silx.gui.plot import Plot1D


def main():
app = qt.QApplication([])

plot = Plot1D(backend="pygfx")
plot.setWindowTitle("pygfx - Log Axes")
plot.setGraphTitle("Logarithmic Scale")
plot.setGraphXLabel("Frequency (Hz)")
plot.setGraphYLabel("Amplitude")

x = numpy.logspace(0, 5, 200)

# Power-law decay
y1 = 1e6 * x**-1.5
plot.addCurve(x, y1, legend="f^-1.5", color="blue", linewidth=2)

# Exponential decay
y2 = 1e4 * numpy.exp(-x / 1e4)
plot.addCurve(x, y2, legend="exp decay", color="red", linewidth=2)

plot.getXAxis().setScale("log")
plot.getYAxis().setScale("log")
plot.setGraphGrid("both")
plot.setActiveCurveHandling(False)
plot.resetZoom()
plot.show()
app.exec()


if __name__ == "__main__":
main()
Loading
Loading