Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Yes, on X11: use QScreen.grabWindow() with the window’s native ID, usually obtained from QWidget.winId(). It captures pixels from the composed screen, not the window’s private contents, so a window in front of your Qt window will appear in the image. On Wayland, capture follows a different, portal-based path and requires compositor permission.

What “screenshot an overlapped window” means

There are two different goals that can sound like the same task:

  • Capture what is visible on the desktop: use QScreen.grabWindow(). Any window covering the target contributes its pixels to the screenshot.
  • Recover the target’s content even when covered: a screen grab is not enough. The obscured pixels are not reliably available from this API. Render the Qt content off-screen or make the window visible and unobscured before capturing it.

This distinction matters because grabWindow() is a screen-pixel capture, not a request for the target application to redraw its complete contents into an image.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture a Qt window with PySide6 on X11

The example below creates a small Qt window, shows it, waits briefly for it to be mapped, and saves a screenshot using the window’s native ID. Run it in an X11 session, or in an XWayland context where this capture path is available.

from pathlib import Path
import sys

from PySide6.QtCore import QTimer
from PySide6.QtGui import QGuiApplication
from PySide6.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget

app = QApplication(sys.argv)

target = QWidget()
target.setWindowTitle("Qt window to capture")
layout = QVBoxLayout(target)
layout.addWidget(QLabel("This is the target Qt window."))
target.resize(420, 180)
target.show()


def capture():
    # winId() exposes the native window ID after the widget is shown.
    wid = target.winId()
    screen = target.screen() or QGuiApplication.primaryScreen()
    if screen is None:
        raise RuntimeError("Qt could not find a screen for the target window")

    pixmap = screen.grabWindow(
        wid,
        0,
        0,
        target.width(),
        target.height(),
    )
    output = Path.home() / "qt-window.png"
    if not pixmap.save(str(output), "PNG"):
        raise RuntimeError(f"Could not save screenshot to {output}")
    print(f"Saved {output}; device pixel ratio: {pixmap.devicePixelRatio()}")
    app.quit()


# Let the window be created and displayed before asking the screen to grab it.
QTimer.singleShot(1000, capture)
sys.exit(app.exec())
  1. Install PySide6 in the Python environment you intend to use.
  2. Save the script, for example as capture_qt.py, and run python capture_qt.py from a graphical Linux session.
  3. After the delay, check your home directory for qt-window.png. The delay is only to let this example’s window appear; increase it if your application needs more time to reach the state you want.

To capture another Qt window in your own program, replace the example widget with your target widget and call target.winId() after it has been created. If you want to include only part of the target, pass the desired rectangle as the x, y, width, and height arguments after the window ID. The coordinates and widget dimensions are in device-independent pixels.

Using PyQt6 instead

The capture call is the same in PyQt6. Change the imports to the corresponding PyQt6 modules; for example:

from PyQt6.QtCore import QTimer
from PyQt6.QtGui import QGuiApplication
from PyQt6.QtWidgets import QApplication, QWidget

Keep the same sequence: show the widget, obtain target.winId(), select target.screen() (falling back to QGuiApplication.primaryScreen()), then call screen.grabWindow(wid, ...). The rest of the runnable PySide6 example can be adapted by replacing its imports.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What happens when another window covers the target?

On X11, Qt’s documented behavior is explicit: grabWindow() grabs pixels from the screen, not directly from the window. If another window sits partly or entirely above the target, the grab includes the covering window’s pixels in the corresponding area. The result therefore represents the visible desktop composition, rather than a clean copy of the target as if it were unobstructed.

Qt also warns that obscured pixels can be undefined on X11 when the target window and root window have different depths. Do not rely on this call to reveal hidden content or to reconstruct pixels that the compositor is not presenting. If you need the whole target window’s visible contents, arrange for it to be unobscured before capture. If you need content regardless of desktop visibility, use an off-screen rendering approach appropriate to the Qt scene or widget instead.

Capture a different application’s window

On X11, QScreen.grabWindow() can also be given the native ID of an external window. You must obtain that integer through an X11-aware tool or binding, then pass it as the wid argument:

external_wid = ...  # Native X11 window ID obtained for the other application
screen = QGuiApplication.primaryScreen()
if screen is None:
    raise RuntimeError("No screen is available")

pixmap = screen.grabWindow(external_wid)
pixmap.save("external-window.png", "PNG")

The ellipsis is deliberately not a universal command: the way to discover an external window ID depends on the X11 tool or binding you choose. Treat that ID as session-specific, and do not use this technique as a portable way to select an arbitrary application under Wayland. As with a Qt-owned target, anything drawn over the external window can show up in the screen grab.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What changes on Wayland?

Do not assume the X11 behavior or external-window-ID workflow applies to a native Wayland session. Qt describes its Wayland screen-capture path as experimental and based on the XDG Desktop Portal’s ScreenCast service plus PipeWire. The compositor’s permission flow is part of capture, and Wayland’s restrictions mean the API cannot simply select an arbitrary target screen in the same way as the X11 path.

Design a Wayland capture workflow around the portal and user/compositor consent. In particular, do not promise that a background program can silently capture a hidden application window by supplying a native ID. If your application must work in both kinds of sessions, handle the capture result and permission flow as platform-dependent rather than treating a successful X11 grab as proof of Wayland support.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

High-DPI coordinates and multiple screens

The rectangle arguments to grabWindow() are device-independent coordinates. On X11, the coordinates are relative to the selected screen’s origin; for a widget capture beginning at its top-left, 0, 0 is the straightforward choice. With high-DPI scaling, the saved pixmap can have more physical pixels than the logical width and height suggest. Inspect pixmap.devicePixelRatio() when sizing, combining, or interpreting the image; do not assume its pixel dimensions equal target.width() and target.height().

For a window spanning monitors, choose the screen associated with the target when possible, as in target.screen(). The fallback to the primary screen is useful when Qt has not associated a screen with the widget, but the primary screen is not necessarily the one you intended. Check the chosen screen and the resulting image if your capture crosses display boundaries or uses different scaling on different monitors.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose the capture method for the result you need

  • You want a faithful screenshot of what a person can currently see: grab the window and accept that overlap, along with the screen’s cursor and decoration behavior, affects the result.
  • You want an external window’s visible area on X11: obtain its X11 native ID and ensure it is not covered by another window.
  • You want the target’s full content while it is hidden: do not expect a screen grab to recover it; render off-screen or temporarily expose the window.
  • You need a native Wayland workflow: use a portal-backed screen-capture flow and account for compositor consent.

Or skip the browser setup

ScreenshotNeo is for capturing websites, not local Linux desktop or Qt application windows; use the Qt method above for this task. If what you need instead is a clean screenshot of a web page, one GET request to the ScreenshotNeo API returns an image or PDF. The parameter names used by other screenshot APIs also work. See the API documentation for options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

In Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

In Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

For website captures, ScreenshotNeo accepts cookie or consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; you can turn each step off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month with no card.

Troubleshooting

  • The screenshot shows the window in front: that is the expected screen-pixel behavior. Move or hide the covering window before capturing, or use off-screen rendering if you need the target’s covered content.
  • The result is empty or does not match the target: ensure the widget has been shown and has a native window ID before the grab. In the example, the timer gives the window time to appear; adjust the delay to suit your application’s startup and layout.
  • No screen is available: check that the application is running in a graphical desktop session and that Qt can return a screen. The example raises an error rather than attempting a grab without one.
  • An external-window capture fails or targets the wrong window: verify that the ID came from an X11-aware source in the current session. External native IDs are not portable to Wayland.
  • The image dimensions seem unexpectedly large: account for high-DPI scaling by checking devicePixelRatio(); the API uses logical coordinates, while the resulting pixmap may contain more physical pixels.
  • Capture does not work as expected on Wayland: confirm that the portal-backed ScreenCast and PipeWire path is available and handle the compositor’s permission flow. Do not treat the X11 native-ID approach as a Wayland workaround.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.