ScreenshotNeo

BlogHow-to

How to Display a Screen in Python

Choose the right Python API for terminal text, images, desktop windows, processed frames, or interactive graphics with runnable examples.

By the ScreenshotNeo team1 October 20269 min read

“Display a screen” can mean several different things in Python. Decide where the result should appear and what you are showing:

  • Terminal text: use print().
  • A desktop interface: use Tkinter widgets or a Tkinter Canvas.
  • A still image: use Pillow’s ImageShow module or place the image in a GUI.
  • An image-processing or video frame: use OpenCV HighGUI.
  • A game or custom interactive display: use Pygame’s display Surface.
  • A screenshot of a web page: capture the page with a browser or a screenshot API such as ScreenshotNeo.

The examples below show the smallest complete program for each case, followed by configuration details, common errors, and selection guidance.

1. Display text in a terminal

For ordinary console output, call print(). It writes text to the process’s standard output stream.

print("Hello from Python")

name = "Ada"
print(f"Hello, {name}")

Run it from a shell:

python display_text.py

Use sep to control the separator and end to control what is written after the value:

print("loading", "data", sep="...", end="\n")
print("progress: 50%", end="\r", flush=True)

flush=True is useful for progress indicators because it asks Python to write buffered output immediately. Terminal output does not create a desktop window or a GUI widget. To show text inside an application window, use a toolkit such as Tkinter.

2. Display text, images, and controls in a Tkinter window

Tkinter is Python’s standard interface to Tcl/Tk. It provides widgets for desktop interfaces, and its Canvas can display text, images, bitmaps, geometric shapes, and embedded windows. The official documentation is at Python’s Tkinter reference.

Minimal label window

import tkinter as tk

root = tk.Tk()
root.title("Python screen")
root.geometry("420x160")

label = tk.Label(root, text="Hello from a Tkinter window", font=("Arial", 18))
label.pack(expand=True)

root.mainloop()

The call to mainloop() starts Tk’s event loop. Without it, the process exits or the window becomes unresponsive.

Update what is displayed

import tkinter as tk

root = tk.Tk()
root.title("Live value")

value = tk.StringVar(value="0")
label = tk.Label(root, textvariable=value, font=("Arial", 24))
label.pack(padx=30, pady=30)

def increment():
    value.set(str(int(value.get()) + 1))
    root.after(1000, increment)

root.after(1000, increment)
root.mainloop()

Use root.after(milliseconds, callback) for periodic updates. Avoid a long-running loop on the GUI thread; it prevents Tk from processing redraw and input events.

Display an image with Pillow and Tkinter

import tkinter as tk
from PIL import Image, ImageTk

root = tk.Tk()
root.title("Image viewer")

image = Image.open("photo.jpg")
tk_image = ImageTk.PhotoImage(image)

label = tk.Label(root, image=tk_image)
label.pack()

# Keep a reference to tk_image until the window closes.
root.mainloop()

The reference to tk_image must stay alive. If it is garbage-collected, the widget can become blank.

Use a Canvas for multiple items

import tkinter as tk

root = tk.Tk()
canvas = tk.Canvas(root, width=640, height=360, background="white")
canvas.pack()

canvas.create_rectangle(40, 40, 600, 320, outline="navy", width=3)
canvas.create_text(320, 180, text="Canvas content", font=("Arial", 28))

root.mainloop()

A Canvas is useful when the screen contains several independently positioned objects. Store the item IDs returned by create_text, create_image, or shape methods if you need to move or delete them later.

3. Open a still image with Pillow

Pillow’s ImageShow module delegates display to an available viewer. Its behavior depends on the runtime environment and installed viewers. See the Pillow ImageShow reference.

from PIL import Image

image = Image.open("photo.jpg")
image.show()

For explicit control:

from PIL import Image, ImageShow

image = Image.open("photo.png")
ImageShow.show(image, title="Preview")

On Windows, Pillow can use the default system PNG viewer. On macOS, Preview is one available option. Unix systems depend on viewer commands installed on the machine. This method may open a separate application rather than an embedded window, and it may do nothing useful on a headless server or minimal container.

For a predictable application window, convert the image to a Tkinter PhotoImage or use a GUI toolkit designed for your deployment target.

4. Show an image or video frame with OpenCV

OpenCV HighGUI provides windows for visualizing images and processed frames. After cv2.imshow(), call cv2.waitKey() or cv2.pollKey() so OpenCV can process window events. Without event processing, the window may not appear or can lock up. Refer to the OpenCV HighGUI documentation.

Display one image

import cv2

image = cv2.imread("photo.jpg")
if image is None:
    raise FileNotFoundError("Could not read photo.jpg")

cv2.namedWindow("Preview", cv2.WINDOW_NORMAL)
cv2.imshow("Preview", image)

# Wait until a key is pressed, then close all OpenCV windows.
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.imread() returns None when the path is wrong or the file cannot be decoded. WINDOW_NORMAL lets the user resize the window; otherwise a large image can exceed the available desktop area.

Display processed video frames

import cv2

cap = cv2.VideoCapture(0)
if not cap.isOpened():
    raise RuntimeError("Could not open the camera")

try:
    while True:
        ok, frame = cap.read()
        if not ok:
            break

        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        cv2.imshow("Camera", gray)

        # Press q to quit. waitKey also services the window event loop.
        if cv2.waitKey(1) & 0xFF == ord("q"):
            break
finally:
    cap.release()
    cv2.destroyAllWindows()

Use a small positive delay such as waitKey(1) for a live stream. The exact frame rate depends on camera, processing, and display workload; the documentation does not establish a universal speed advantage.

5. Create an interactive display with Pygame

Pygame’s display module creates a display Surface in a window or full screen. Drawing changes become visible only after calling pygame.display.flip() for the entire display or pygame.display.update() for selected regions. See the Pygame display reference.

import pygame

pygame.init()
screen = pygame.display.set_mode((800, 450))
pygame.display.set_caption("Pygame screen")

clock = pygame.time.Clock()
running = True

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill((25, 30, 45))
    pygame.draw.circle(screen, (80, 180, 255), (400, 225), 80)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Pygame maintains one active display Surface at a time. Handle events every frame so the operating system can close or respond to the window. Use pygame.display.update(rect) when only selected regions need refreshing on a software display.

6. Choose the API by output type

Goal Use Window ownership Refresh or event work
Terminal text print() Shell owns the terminal No GUI loop
Forms, labels, menus Tkinter Your program owns the Tk window mainloop()
Quick still-image preview Pillow ImageShow An available external viewer Viewer-dependent
Processed image or camera frame OpenCV HighGUI OpenCV window waitKey() or pollKey()
Games and custom graphics Pygame Pygame display Surface flip()/update() plus event handling

7. Installation and environment checks

Check that Python is available:

python --version

Install the libraries used by the examples:

python -m pip install pillow opencv-python pygame

Tkinter is commonly included with desktop Python installations, but some Linux distributions package Tcl/Tk separately. A quick check is:

python -c "import tkinter; print(tkinter.TkVersion)"

GUI programs require a graphical session. A remote shell, CI runner, Docker container, or server without a display may need a virtual display or a non-GUI output path such as saving files.

8. Troubleshooting common display errors

“The window opens and immediately closes”

The program probably exits without an event loop or wait call. Use Tkinter’s mainloop(), OpenCV’s waitKey(), or Pygame’s loop and quit handling.

“The Tkinter window is frozen”

A callback is doing blocking work on the GUI thread. Move long work to a worker thread or process, then schedule UI updates with after(). Do not call Tk widgets directly from a worker thread.

“Tkinter is not found”

Your Python build may lack Tcl/Tk. Install the operating system’s Tk package or use a Python distribution that includes it, then rerun the import check.

“Pillow opens no viewer”

ImageShow relies on a suitable viewer command or desktop integration. Install a viewer, run inside a graphical session, or embed the image with Tkinter instead.

“OpenCV shows a black or missing window”

Check that imread() returned an image, that the path is correct, and that waitKey() or pollKey() is called. Headless environments cannot display HighGUI windows normally.

“OpenCV says GUI functions are unavailable”

The installed OpenCV package may be a headless build. Install a GUI-capable package for local display, or save frames to disk and inspect them elsewhere.

“Pygame draws nothing”

Draw onto the Surface returned by set_mode(), then call flip() or update(). Also ensure the event loop is running and the display was initialized with pygame.init().

“The image is blank in Tkinter”

Keep a Python reference to every ImageTk.PhotoImage. A local variable that is collected after setup can leave the widget blank.

9. Performance, reliability, and deployment notes

  • Keep UI callbacks short. Blocking file, network, or CPU work stops redraw and input handling.
  • Resize before display when appropriate. Very large images consume more memory and may create oversized windows.
  • Reuse windows. In a video loop, create one OpenCV or Pygame window and update its contents instead of creating a new window per frame.
  • Close resources. Release cameras with cap.release(), destroy OpenCV windows, and call pygame.quit().
  • Plan for headless machines. Save images, stream them to a browser, or use a screenshot service when no desktop session exists.
  • Use explicit paths. Relative paths depend on the process working directory. Log or validate the resolved path when an image cannot be loaded.
  • Do not assume viewer availability. Pillow’s external viewer behavior varies by operating system and installed software.

10. Or skip the browser setup

If “display a screen” means obtaining a clean screenshot of a web page, ScreenshotNeo provides one GET request that returns PNG, JPEG, WebP, or PDF. The API accepts the cookie or consent banner before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.

See the ScreenshotNeo API documentation for all options.

cURL

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

Python

import requests

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

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://python.org' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const buffer = Buffer.from(await res.arrayBuffer());
require('node:fs').writeFileSync('shot.webp', buffer);

ScreenshotNeo also supports full-page capture with lazy images loaded, CSS element capture, dark mode, device presets and custom viewports, retina scale, PDF settings, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed image links, asynchronous jobs, signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. An MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan. Create a free ScreenshotNeo account.

11. Frequently asked questions

Can Python print an image directly in a normal terminal?

Ordinary print() writes characters. Use a terminal image protocol supported by your terminal, or open the image with a viewer or GUI instead.

Which option should a beginner choose for a desktop app?

Start with Tkinter when you need labels, buttons, forms, or a simple Canvas. It is included with many desktop Python installations and has a straightforward event loop.

Which library is best for a game window?

Use Pygame when you need a continuously updated display Surface, keyboard or mouse events, and custom drawing.

Can these APIs run on a server?

Only when a graphical environment is available. For headless services, save output files, return images over HTTP, or use a remote screenshot API.

Why does OpenCV require both imshow() and waitKey()?

imshow() schedules the image for display; waitKey() or pollKey() processes the window events needed to render and keep the window responsive.