Pages

luni, 13 aprilie 2026

Python-CE : How to fix DPI pygame‑ce on Python 3.13.0 and test your DPI monitors.

Today, I tried the high-DPI displays, and because I have Python 3.13.0, some features are not supported, but I fixed them.
This script is specifically designed for:
  • Python 3.13
  • pygame‑ce 2.5.7
  • SDL 2.32.x (bundled inside pygame‑ce)
These versions have incomplete DPI support in the Python bindings.
Many DPI‑related functions that exist in SDL2 are not exposed in pygame‑ce for Python 3.13.
Examples of missing functions in your build:
  • Window.drawable_size
  • Window.from_display_surface
  • Renderer.get_output_size
  • pygame.display.get_window()
Because these functions are missing, PyGame-CE cannot read DPI information directly.
I adopted the ctypes solution because ctypes allows Python to call native Windows API functions directly.
I used these native Windows API:
  • EnumDisplayMonitors → to list all monitors
  • GetDpiForMonitor → to read DPI for each monitor
  • SetProcessDpiAwareness → to enable DPI awareness
This bypasses pygame‑ce completely and talks directly to Windows, but pygame-ce can be used only for window creation and drawing.
ctypes is the only reliable way to get real DPI values on your current setup.
The script creates two independent SDL2 windows.
Drag each window to a different monitor.
Each window will show different DPI values.
You can compare scaling visually and numerically.
This script is designed for Python 3.13 + pygame‑ce 2.5.7, where DPI functions are missing.
I use ctypes to call Windows DPI APIs directly because pygame‑ce cannot access DPI.
The script creates two windows, each showing DPI and visual scaling tests.
With one monitor, both windows show the same DPI.
With multiple monitors, each window shows the DPI of its monitor.
This is the most advanced DPI test possible on your current setup.
I have only one monitor, and the result is the same.
Let's see the result:
Let's see the script:
import pygame
import pygame._sdl2 as sdl2
import ctypes
import sys

pygame.init()

# Windows DPI API
user32 = ctypes.windll.user32
shcore = ctypes.windll.shcore

# Enable per-monitor DPI awareness
try:
    shcore.SetProcessDpiAwareness(2)
except:
    pass

# --- Monitor enumeration ---
MONITORS = []

def _monitor_enum_proc(hmonitor, hdc, lprcMonitor, lparam):
    MONITORS.append(hmonitor)
    return 1

MonitorEnumProc = ctypes.WINFUNCTYPE(
    ctypes.c_int,
    ctypes.c_ulong,
    ctypes.c_ulong,
    ctypes.POINTER(ctypes.c_int * 4),
    ctypes.c_double
)

user32.EnumDisplayMonitors(0, 0, MonitorEnumProc(_monitor_enum_proc), 0)

def get_monitor_dpi(index):
    if index >= len(MONITORS):
        index = 0  # fallback for single-monitor setups

    dpi_x = ctypes.c_uint()
    dpi_y = ctypes.c_uint()

    shcore.GetDpiForMonitor(MONITORS[index], 0, ctypes.byref(dpi_x), ctypes.byref(dpi_y))
    return dpi_x.value, dpi_y.value


# --- Create two SDL2 windows ---
window1 = sdl2.Window("Make test on first monitor", size=(600, 400), position=(100, 100))
window2 = sdl2.Window("Make test on second monitor", size=(600, 400), position=(800, 100))

surface1 = window1.get_surface()
surface2 = window2.get_surface()

font = pygame.font.SysFont("Arial", 22)
clock = pygame.time.Clock()

# --- Drawing helpers ---
def draw_grid(surface, spacing=20):
    w, h = surface.get_size()
    for x in range(0, w, spacing):
        pygame.draw.line(surface, (60, 60, 60), (x, 0), (x, h))
    for y in range(0, h, spacing):
        pygame.draw.line(surface, (60, 60, 60), (0, y), (w, y))

def draw_square_test(surface, dpi_scale):
    size = int(50 * dpi_scale)
    pygame.draw.rect(surface, (200, 50, 50), (20, 200, size, size), 2)

# --- Main loop ---
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    dpi0 = get_monitor_dpi(0)
    dpi1 = get_monitor_dpi(1)

    scale0 = dpi0[0] / 96
    scale1 = dpi1[0] / 96

    # --- Window 1 ---
    surface1.fill((25, 25, 25))
    draw_grid(surface1)
    draw_square_test(surface1, scale0)

    y = 20
    for line in [
        "Advanced DPI Test",
        f"Monitor index: 0",
        f"DPI X: {dpi0[0]}",
        f"DPI Y: {dpi0[1]}",
        f"Scale factor: {scale0:.2f}x",
        "Red square = 50px scaled",
        "Grid = 20px spacing"
    ]:
        surf = font.render(line, True, (255, 255, 255))
        surface1.blit(surf, (20, y))
        y += 28

    window1.flip()

    # --- Window 2 ---
    surface2.fill((25, 25, 25))
    draw_grid(surface2)
    draw_square_test(surface2, scale1)

    y = 20
    for line in [
        "Advanced DPI Test",
        f"Monitor index: 1",
        f"DPI X: {dpi1[0]}",
        f"DPI Y: {dpi1[1]}",
        f"Scale factor: {scale1:.2f}x",
        "Red square = 50px scaled",
        "Grid = 20px spacing"
    ]:
        surf = font.render(line, True, (255, 255, 255))
        surface2.blit(surf, (20, y))
        y += 28

    window2.flip()

    clock.tick(60)

vineri, 10 aprilie 2026

PyGame : particles with the pygame and pyqt6 !

Today, I test one python script with pygame and pyqt6. The python script use classes and show particles. See the result:
These are python and pygame-ce, but the source code use pygame.
Python 3.13.0 (tags/v3.13.0:60403a5, Oct  7 2024, 09:38:07) [MSC v.1941 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import pygame
pygame-ce 2.5.7 (SDL 2.32.10, Python 3.13.0)
>>> print(pygame.version.ver)
2.5.7
>>> print(pygame.version.SDL)
2.32.10
This is the source code:
import sys
import random
import pygame
from pygame import Vector2
from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout,
    QSlider, QLabel, QSizePolicy
)
from PyQt6.QtCore import Qt, QTimer
from PyQt6.QtGui import QImage, QPainter

class Particle:
    def __init__(self, pos, vel, color):
        self.pos = Vector2(pos)
        self.vel = Vector2(vel)
        self.color = color
        self.life = 255

    def update(self, gravity):
        self.vel.y += gravity
        self.pos += self.vel
        self.life -= 2

    def draw(self, surf):
        if self.life > 0:
            pygame.draw.circle(
                surf,
                self.color,
                (int(self.pos.x), int(self.pos.y)),
                4
            )

class PygameWidget(QWidget):
    def __init__(self):
        super().__init__()

        pygame.init()
        pygame.display.init()

        self.w, self.h = 900, 600
        self.surface = pygame.Surface((self.w, self.h))

        self.particles = []
        self.spawn_rate = 5
        self.gravity = 0.1

        self.setSizePolicy(
            QSizePolicy.Policy.Expanding,
            QSizePolicy.Policy.Expanding
        )

        self.timer = QTimer()
        self.timer.timeout.connect(self.game_loop)
        self.timer.start(16)  # ~60 FPS

    def spawn_particles(self):
        for _ in range(self.spawn_rate):
            pos = (self.w // 2, self.h // 2)
            vel = (random.uniform(-2, 2), random.uniform(-2, 2))
            color = (255, random.randint(100, 255), 0)
            self.particles.append(Particle(pos, vel, color))

    def game_loop(self):
        self.surface.fill((20, 20, 20))

        self.spawn_particles()

        alive = []
        for p in self.particles:
            p.update(self.gravity)
            p.draw(self.surface)
            if p.life > 0:
                alive.append(p)

        self.particles = alive

        self.update()

    def paintEvent(self, event):
        data = pygame.image.tobytes(self.surface, "RGB")
        img = QImage(
            data,
            self.w,
            self.h,
            self.w * 3,
            QImage.Format.Format_RGB888
        )

        painter = QPainter(self)
        painter.drawImage(0, 0, img)
        painter.end()

    def resizeEvent(self, event):
        self.w = self.width()
        self.h = self.height()
        self.surface = pygame.Surface((self.w, self.h))

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("PyQt6 + pygame Particle System")

        main_layout = QVBoxLayout()

        # widget-ul pygame – ocupă tot spațiul
        self.pg_widget = PygameWidget()
        main_layout.addWidget(self.pg_widget, stretch=1)

        # panou de controale jos
        control_panel = QVBoxLayout()

        # ----- slider spawn rate -----
        spawn_layout = QHBoxLayout()
        spawn_label = QLabel("Spawn:")
        self.spawn_value = QLabel("5")

        spawn_slider = QSlider(Qt.Orientation.Horizontal)
        spawn_slider.setRange(1, 50)
        spawn_slider.setValue(5)
        spawn_slider.valueChanged.connect(self.update_spawn_rate)

        spawn_layout.addWidget(spawn_label)
        spawn_layout.addWidget(spawn_slider)
        spawn_layout.addWidget(self.spawn_value)

        # ----- slider gravity -----
        gravity_layout = QHBoxLayout()
        gravity_label = QLabel("Gravity:")
        self.gravity_value = QLabel("0.10")

        gravity_slider = QSlider(Qt.Orientation.Horizontal)
        gravity_slider.setRange(0, 50)
        gravity_slider.setValue(10)
        gravity_slider.valueChanged.connect(self.update_gravity)

        gravity_layout.addWidget(gravity_label)
        gravity_layout.addWidget(gravity_slider)
        gravity_layout.addWidget(self.gravity_value)

        control_panel.addLayout(spawn_layout)
        control_panel.addLayout(gravity_layout)

        main_layout.addLayout(control_panel)

        self.setLayout(main_layout)

    def update_spawn_rate(self, v):
        self.pg_widget.spawn_rate = v
        self.spawn_value.setText(str(v))

    def update_gravity(self, v):
        g = v / 100.0
        self.pg_widget.gravity = g
        self.gravity_value.setText(f"{g:.2f}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.resize(1000, 800)
    w.show()
    sys.exit(app.exec())

luni, 6 aprilie 2026

PyGame-CE : First test example with pygame-ce-2.5.7

The biggest technical difference in version 2.5.7 is the expanded use of SIMD (Single Instruction, Multiple Data)
It utilizes modern CPU instructions like AVX2 (Intel/AMD) and NEON (ARM/Apple Silicon).
Pixel-level operations—such as blending, alpha-compositing, and surface scaling 30% to 100% faster than the original Pygame. If you are doing real-time lighting or heavy particle effects
Let's install with:
python -m pip install pygame-ce
WARNING: Ignoring invalid distribution ~adquery-ocp (C:\Python313_64bit\Lib\site-packages)
Collecting pygame-ce
  Downloading pygame_ce-2.5.7-cp313-cp313-win_amd64.whl.metadata (11 kB)
Downloading pygame_ce-2.5.7-cp313-cp313-win_amd64.whl (10.4 MB)
   ---------------------------------------- 10.4/10.4 MB 2.8 MB/s  0:00:03
WARNING: Ignoring invalid distribution ~adquery-ocp (C:\Python313_64bit\Lib\site-packages)
Installing collected packages: pygame-ce
WARNING: Ignoring invalid distribution ~adquery-ocp (C:\Python313_64bit\Lib\site-packages)
Successfully installed pygame-ce-2.5.7
python
Python 3.13.0 (tags/v3.13.0:60403a5, Oct  7 2024, 09:38:07) [MSC v.1941 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import pygame
pygame-ce 2.5.7 (SDL 2.32.10, Python 3.13.0)
>>> print(pygame.version.ver)      # Should be 2.5.7
2.5.7
>>> print(pygame.version.vernum)   # Should show the CE suffix
2.5.7
Comparison Summary: Pygame-CE 2.5.7 vs. Legacy Pygame
Feature Pygame-CE 2.5.7 Legacy Pygame
Rendering Engine Highly optimized C-code with SIMD Basic SDL2 wrappers
OS Integration Full (System notifications, Dark Mode) Minimal
Updates Monthly (Active community) Yearly or less (Stagnant)
Math & Rects Modernized, faster collision logic Older, slower C implementation
Multi-threading Ready for Python 3.13+ "Free-thread" Limited by the GIL
Next python script demonstrates a real-time bridge between a graphical user interface and the operating system's memory by utilizing the Pygame-CE scrap module to exchange text data with the system clipboard via keyboard inputs.
import pygame

# 1. Initialize Pygame
pygame.init()

# 2. Create the window (CRITICAL: Scrap needs a window to talk to the OS)
screen = pygame.display.set_mode((600, 400))
pygame.display.set_caption(f"Pygame-CE 2.5.7 Scrap Fix")

# 3. Initialize scrap safely
try:
    pygame.scrap.init()
except Exception:
    # Some builds initialize this automatically with display.set_mode
    pass

font = pygame.font.SysFont("Arial", 22)
clipboard_text = "Press 'V' to paste"
status_message = "Found: pygame.scrap.get_text"

running = True
while running:
    screen.fill((30, 30, 30))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        
        if event.type == pygame.KEYDOWN:
            # COPY logic using scrap.put_text
            if event.key == pygame.K_c:
                msg = "Hello from Pygame-CE 2.5.7 Scrap!"
                try:
                    # Modern CE method for text
                    pygame.scrap.put_text(msg)
                    status_message = "Copied via scrap.put_text!"
                except AttributeError:
                    # Fallback for some 2.5.7 sub-builds
                    pygame.scrap.put(pygame.SCRAP_TEXT, msg.encode("utf-8"))
                    status_message = "Copied via scrap.put (fallback)!"
            
            # PASTE logic using scrap.get_text
            if event.key == pygame.K_v:
                # Based on your discovery, this IS the method
                txt = pygame.scrap.get_text()
                if txt:
                    # scrap.get_text() usually returns a string directly
                    clipboard_text = txt.replace('\x00', '') # Clean null bytes
                    status_message = "Pasted successfully!"
                else:
                    status_message = "Clipboard is empty."

    # Rendering
    surf_clip = font.render(f"Content: {clipboard_text}", True, (255, 255, 255))
    surf_stat = font.render(status_message, True, (0, 255, 0))
    screen.blit(surf_clip, (50, 150))
    screen.blit(surf_stat, (50, 250))
    
    pygame.display.flip()

pygame.quit()

joi, 22 ianuarie 2026

News : What is the difference between pygame and pygame-ce?

Python game development often begins with the popular pygame library. However, in recent years, a new alternative has emerged: pygame-ce (Community Edition). While both libraries share the same foundation, they differ significantly in development pace, features, and long-term vision.
  • pygame: The original library created in the early 2000s, widely used for 2D games.
  • pygame-ce: A community-driven fork created to modernize pygame and accelerate development.
  • pygame: Updates are infrequent and focus on stability rather than innovation.
  • pygame-ce: Receives frequent updates, bug fixes, and new features contributed by an active community.
  • Better performance: pygame-ce includes optimizations for rendering and event handling.
  • Improved math module: Enhanced Vector2 and Vector3 classes with additional methods.
  • Better timing and framerate control: More accurate clock behavior on modern systems.
  • Extended API: New helper functions and quality-of-life improvements not found in pygame.
  • pygame: May encounter issues on newer platforms such as macOS ARM (M1/M2).
  • pygame-ce: Designed to work smoothly on modern hardware, including ARM-based systems.
  • Better Windows support: pygame-ce fixes several long-standing Windows-specific bugs.
  • High compatibility: Most pygame projects run on pygame-ce without modification.
  • Backward-friendly: pygame-ce maintains the original API while adding optional improvements.
  • Future-proofing: pygame-ce is more likely to support new Python versions quickly.
  • Install pygame: pip install pygame
  • Install pygame-ce: pip install pygame-ce
  • pygame: Stable but slow-moving, with fewer contributors.
  • pygame-ce: Active community, open to new ideas, and focused on modernizing the ecosystem.
  • Future direction: pygame-ce aims to become the standard for new Python game projects.
  • Choose pygame-ce if you want modern features, better performance, and active development.
  • Choose pygame if you maintain older projects or prefer the original library.

sâmbătă, 3 ianuarie 2026

PyGame : conversion tool from shadertoy to shader pygame .

Today I tested a new tool that converts source code for shadertoy language into source code for pygame.
Shadertoy uses a permissive WebGL‑style GLSL ES shader language, while PyGame (through PyOpenGL) requires strict desktop GLSL 330 Core.
This is the result for this shadertoy example.

marți, 23 septembrie 2025

News : ... new pygame version 2.6.0 .

... seams pygame team development works and released a new version, see the GitHub project.

sâmbătă, 19 octombrie 2024

PyGame : 5by5 linux game project - part 001.

The game is about your brain skills to hack the code based on minimal information versus total information.
The code has 5 distinct letters.
  • click on the letters on the keypad
  • the number of guessed letters is displayed in the form: centered - guessed letters on positions and moved guessed letters but on other positions
I used agentpy python module and pygame python module
The game can be found on my fedora pagure account.

sâmbătă, 21 septembrie 2024

PyGame : 8in8 linux game project - part 001.

I started a game project with the python packages pygame and agentpy in the Fedora Linux distribution.
You can find it on my fedora pagure repo

duminică, 16 iunie 2024

PyGame : Game development in PyGame: making a basic map.

Example with PyGame with 3D features ...
The project can be found on the GitHub repo - stage 11 ...

sâmbătă, 30 martie 2024

PyGame : ... antialiased filled circle !

The pygame python module does not implement an antialiased filled circle and this is the scope of this tutorial.
The pygame module for drawing shapes are:
  • pygame.draw.rect - to draw a rectangle
  • pygame.draw.polygon - to draw a polygon
  • pygame.draw.circle - to draw a circle
  • pygame.draw.ellipse - to draw an ellipse
  • pygame.draw.arc - to draw an elliptical arc
  • pygame.draw.line - to draw a straight line
  • pygame.draw.lines - to draw multiple contiguous straight line segments
  • pygame.draw.aaline - to draw a straight antialiased line
  • pygame.draw.aalines - to draw multiple contiguous straight antialiased line segments.
Let's install pygame python module.
pip install pygame
Collecting pygame
  Downloading pygame-2.5.2-cp312-cp312-win_amd64.whl.metadata (13 kB)
...
Installing collected packages: pygame
Successfully installed pygame-2.5.2
Let's see the source code:
import pygame
import pygame.gfxdraw

TARGET_SIZE = 200
BG_ALPHA_COLOR = (0, 0, 0, 100)

class Target(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((TARGET_SIZE, TARGET_SIZE), pygame.SRCALPHA)
        self.rect = self.image.get_rect()
        self.color = (255, 0, 0)
        self.filled = False
        self.width = 1

    def DrawTarget(self):
        pygame.gfxdraw.aacircle(self.image, int(self.rect.width/2), int(self.rect.height/2),\
                int(self.rect.width/2 - 1), self.color)
        
        pygame.gfxdraw.filled_ellipse(self.image, int(self.rect.width/2), \
            int(self.rect.height/2), int(self.rect.width/2 - 1), int(self.rect.width/2 - 1), self.color)
        
        temp = pygame.Surface((TARGET_SIZE, TARGET_SIZE), pygame.SRCALPHA)
        
        if not self.filled:
            pygame.gfxdraw.filled_ellipse(temp, int(self.rect.width/2), int(self.rect.height/2), \
                int(self.rect.width/2 - self.width), int(self.rect.width/2 - self.width), BG_ALPHA_COLOR)
            pygame.gfxdraw.aacircle(temp, int(self.rect.width/2), int(self.rect.height/2), \
                int(self.rect.width/2 - self.width), BG_ALPHA_COLOR)
        
        self.image.blit(temp, (0, 0), None, pygame.BLEND_ADD)
pygame.init()
screen = pygame.display.set_mode((400, 400))

target = Target()
target.DrawTarget()

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

    screen.fill((255, 255, 255))
    screen.blit(target.image, (100, 100))
    pygame.display.flip()

pygame.quit()

luni, 10 iulie 2023

News : About my work and one of my websites.

I would like to bring to the attention of those who follow my activity on my websites and appreciate the inability to continue with one of the websites: free-tutorials.org. It is currently hosted on a free host, but I cannot import it 100%, which has led me not to complete it with new posts. The continuation of the activities there, considering the limited time, will be carried out on my blogs with the defined theme with which I started: Linux - Fedora, Graphics, Python, Pygame.
In the meantime, because the host is expensive and until now someone has helped me to host it on his server, it is possible to sell the domain: free-tutorials.org - I receive purchase offers at my personal Yahoo email address catafest@yahoo.com.
Minimum starting price 250 euros, because the domain is old from 2018.

sâmbătă, 8 aprilie 2023

PyGame : ovoid with a random pattern.

Here's how to create an ovoid with a random pattern. Run the script several times to see the differences:
import pygame
import random
pygame.init()

# Set up the display window
screen_size = (400, 400)
screen = pygame.display.set_mode(screen_size)
# Set window title
pygame.display.set_caption("Ovoid with Random Pattern")
# Define the ovoid
ovoid_pos = (150, 100)
ovoid_size = (100, 200)

# Create the ovoid surface
ovoid_surface = pygame.Surface(ovoid_size, pygame.SRCALPHA)

# Define the pattern
pattern_size = (random.randint(1, 9), random.randint(1, 9))
pattern_surface = pygame.Surface(pattern_size)
pattern_surface.fill((255, 255, 255))
pygame.draw.line(pattern_surface, (0, 0, 0), (0, 0), pattern_size)

# Create the mask surface
mask_surface = pygame.Surface(ovoid_size, pygame.SRCALPHA)
pygame.draw.ellipse(mask_surface, (255, 255, 255), mask_surface.get_rect(), 0)

# Apply the pattern to the ovoid surface
for x in range(0, ovoid_size[0], pattern_size[0]):
    for y in range(0, ovoid_size[1], pattern_size[1]):
        ovoid_surface.blit(pattern_surface, (x, y))

# Apply the mask to the ovoid surface
ovoid_surface.blit(mask_surface, (0, 0), special_flags=pygame.BLEND_RGBA_MULT)

# Draw the ovoid to the screen
screen.blit(ovoid_surface, ovoid_pos)

# Update the display
pygame.display.flip()

# Wait for the user to close the window
done = False
while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

# Quit pygame properly
pygame.quit()

vineri, 3 martie 2023

PyGame : simple web camera !

In this simple tutorial, I'll show you how to use pygame to use it with a webcam.
Let's install the pygame with the pip tool:
C:\PythonProjects\pygamecamera001>pip install pygame --user
Collecting pygame
  Downloading pygame-2.2.0-cp311-cp311-win_amd64.whl (10.4 MB)
     ---------------------------------------- 10.4/10.4 MB 9.0 MB/s eta 0:00:00
Installing collected packages: pygame
Successfully installed pygame-2.2.0
This is the source code for web camera:
import pygame.camera
import pygame.image
import sys

pygame.camera.init()

cameras = pygame.camera.list_cameras()

webcam = pygame.camera.Camera(cameras[0])

webcam.start()

img = webcam.get_image()

WIDTH = img.get_width()
HEIGHT = img.get_height()

screen = pygame.display.set_mode( ( WIDTH, HEIGHT ) )
pygame.display.set_caption("pyGame webcam")

while True :
    for e in pygame.event.get() :
        if e.type == pygame.QUIT :
            sys.exit()
    screen.blit(img, (0,0))
    pygame.display.flip()
    img = webcam.get_image()