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.
“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
ImageShowmodule 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 callpygame.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.


