PyVirtualDisplay
PyVirtualDisplay is a Python wrapper for Xvfb, Xephyr, and Xvnc programs. It enables running graphical applications or tests in a headless environment by creating a virtual display, making it suitable for CI/CD pipelines and automated GUI testing. The library is currently at version 3.0 and supports Python versions 3.6 through 3.12.
Warnings
- breaking Major API changes occurred in version 1.0, breaking compatibility with previous versions (e.g., 0.2.5). Code written for older versions will likely fail with 1.x or later without modification.
- gotcha PyVirtualDisplay is a Python wrapper and *requires* an underlying X server program (like Xvfb, Xephyr, or Xvnc) to be installed on the operating system. Without it, you will encounter `FileNotFoundError` or `pyvirtualdisplay.abstractdisplay.XStartError`.
- gotcha When running PyVirtualDisplay in a multi-threaded or multi-process environment, the default behavior of modifying `os.environ['DISPLAY']` is not thread-safe. This can lead to race conditions or incorrect display assignments.
- gotcha Starting with version 0.2.2, a 10-second timeout (`XStartTimeoutError`) was introduced for the X server to start up. If the display takes longer to become ready, this error will be raised.
- gotcha PyVirtualDisplay does not run natively on Windows as it relies on the X Window System. Attempts to use it directly on Windows will result in `FileNotFoundError` for X server binaries.
Install
-
pip install pyvirtualdisplay -
sudo apt-get install xvfb xserver-xephyr tigervnc-standalone-server x11-utils -
brew install xquartz
Imports
- Display
from pyvirtualdisplay import Display
- SmartDisplay
from pyvirtualdisplay.smartdisplay import SmartDisplay
Quickstart
from pyvirtualdisplay import Display
import time
import os
# Ensure Xvfb or a similar X server is installed on the OS
# Example for Linux: sudo apt-get install xvfb
# For thread-safe operations in concurrent environments, use manage_global_env=False
# and explicitly set os.environ['DISPLAY'] if needed within the thread/process.
# For simple, single-threaded use, the default is often sufficient.
with Display(visible=0, size=(1920, 1080), backend='xvfb') as disp:
print(f"Virtual display started on: {os.environ.get('DISPLAY')}")
print(f"Is display alive: {disp.is_alive()}")
# Simulate doing some work that requires a display
time.sleep(2)
print(f"Work done within virtual display. Is display still alive: {disp.is_alive()}")
print(f"Virtual display stopped. Is display alive: {disp.is_alive()}")
# os.environ['DISPLAY'] is restored to its original value after context manager exits