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.