For Python users.
Sometimes one would like call draw(x, y, color) and see the pixel of color at x, y within a window. That is exactly what pixeldisplay.py helper has been written for.
pixeldisplay.py wraps pygame-ce interface to SDL. To give it a try
- install
pip3 install pygame-ce, probably within Python virtual environment - save script below into new file named
pixeldisplay.py - enjoy
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2023 Eugene V.S.
#
# *** MIT LICENSE **************************************************************
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# *** Author and contributors **************************************************
# Eugene V.S. aka ugnvs
# *** Tested in the environment ************************************************
# Oct 2023 -- Ubuntu MATE 22.04 64-bit Desktop
# -- Python 3.10.12
# -- pygame-ce 2.3.2 (SDL 2.26.5)
# Apr 2026 -- Ubuntu MATE 24.04 64-bit Desktop
# -- Python 3.12.3
# -- pygame-ce 2.5.7 (SDL 2.32.10)
#
# *** History & Milestones *****************************************************
# 2023-10-23 -- started & done
# ******************************************************************************
"""
Simple pixel display module for playing with pixel graphics.
Services: automatic and manual pausing, image saving
Usage example:
import pixeldisplay, random
# override defaults as needed
pixeldisplay.defaults['width'] = 400
pixeldisplay.defaults['height'] = 300
pixeldisplay.defaults['timeout'] = 4000
pixeldisplay.defaults['caption'] = 'White Noise'
# define a function to generate pixels of interest
def whitenoise():
while True:
x = random.randrange(pixeldisplay.defaults['width'])
y = random.randrange(pixeldisplay.defaults['height'])
tint = random.randrange(256)
# N.B. 'yield [x,y, (r,g,b)]' is used instead of 'draw(x, y, color)' call
yield [x, y, (tint, tint, tint)]
# Run calculation and drawing
pixeldisplay.run(whitenoise)
"""
import pygame, datetime
# Defaults
defaults = {'width' :800,
'height':600,
'background' : (0, 0, 0),
'foreground' : (255, 255, 255),
'fps' : 15, # frames per second
'timeout' : 0, # milliseconds, 0 is no timeout
'caption' : 'Pixel Display' # Window's caption & filename
}
# ==== Functions ===============================================================
def data():
'''
Default pixel data generator fills an area with default colour.
Generally, a generator may provide as finite set of pixels,
as an infinite (random) pixel sequence.
'''
for i in range(defaults['width']):
for j in range(defaults['height']):
yield [i, j, defaults['foreground']]
# ------------------------------------------------------------------------------
def run(generator=None):
print( '''
*****************************************
* When graphics window is active: *
* *
* <SPACE> toggles pause on calculations *
* <ENTER> saves image to disk *
* <ESC> quits *
* *
*****************************************
''')
if generator is None:
print('No pixel generator provided. Using default white filling.')
pixels = data()
else:
pixels = generator()
pygame.init()
screen = pygame.display.set_mode((defaults['width'], defaults['height']))
pygame.display.set_caption(defaults['caption'])
screen.fill(defaults['background'])
pygame.display.flip()
clock = pygame.time.Clock()
PAUSEEVENT = pygame.USEREVENT+1
FRAMEEVENT = pygame.USEREVENT+2
pygame.time.set_timer(PAUSEEVENT, defaults['timeout'])
pygame.time.set_timer(FRAMEEVENT, 1000 // defaults['fps'])
pause = False
while True:
if not pause:
try:
pixel = next(pixels)
except StopIteration:
pygame.time.set_timer(PAUSEEVENT, 0)
pause = True
print('Paused. No more pixel data available.')
screen.set_at((pixel[0], pixel[1]), pixel[2])
for event in pygame.event.get():
if event.type == pygame.QUIT:
print( 'Shutting down.')
pygame.quit()
return
elif event.type == FRAMEEVENT:
pygame.display.flip()
pygame.time.set_timer(FRAMEEVENT, 1000 // defaults['fps'])
elif event.type == PAUSEEVENT:
pygame.time.set_timer(PAUSEEVENT, 0)
print( 'Paused due to timeout')
pause = True
pygame.time.set_timer(PAUSEEVENT, 0) # timer event cancelled
elif event.type == pygame.KEYDOWN:
keys_pressed = pygame.key.get_pressed()
if keys_pressed[pygame.K_SPACE]:
if pause:
# restart the computation
print( 'Pause cleared')
pause = False
pygame.time.set_timer(PAUSEEVENT, defaults['timeout'])
else:
# shut down computation
pygame.time.set_timer(PAUSEEVENT, 0) # cancel pause timer
print( 'Pause invoked')
pause = True
elif keys_pressed[pygame.K_RETURN] or keys_pressed[pygame.K_KP_ENTER]:
fname = defaults['caption'] + ' ' + datetime.datetime.today().isoformat("_").replace(':','-')[:19] + '.png'
print( 'Saving image into ' + fname)
pygame.image.save(screen, fname)
elif keys_pressed[pygame.K_ESCAPE]:
print( 'Shutting down.')
pygame.quit()
return
else:
pass
# ==== End of definitions ======================================================
if __name__ == '__main__':
run()