analitics

Pages

Tuesday, April 14, 2026

Python 3.12.13 : kernel CUDA for Tesla T4 in colab google - part 055.

Today, I will show a source code for a simple CUDA kernel for Tesla T4 in colab google with Python version 3.12.13.
The source code works very well, I will make an video with the results.
The T4 has thousands of CUDA Cores. The kernel divides the image into "Blocks" (16x16 pixel squares) and distributes them across these cores.
This source code is a high-performance image processing pipeline that bridges Python and CUDA (C++) to create a dynamic video effect. By using PyTorch's load_inline feature, it compiles custom GPU code on the fly to manipulate pixels at massive scale.
Loading the Image: It reads the image and moves it from the CPU (System RAM) to the GPU (Video RAM) using .cuda().
Loading the Image: It reads the image and moves it from the CPU (System RAM) to the GPU (Video RAM) using .cuda().
It runs a loop for each video frame, calculating where the "Zoom Ball" should be at that specific millisecond.
The code inside cuda_source is a Kernel, a special function designed to run on thousands of GPU cores simultaneously.
Instead of processing one pixel at a time (like a CPU would), the GPU assigns a specific thread to every single pixel in the image. If your image has 1 million pixels, 1 million threads start working at the same time.
If a pixel is inside the ball's radius, the thread "re-maps" its coordinate. It looks at the original image but pulls a pixel from closer to the center of the ball.
Because the zoom calculation for the top-left pixel doesn't depend on the bottom-right pixel, the GPU completes the entire frame transformation in microseconds.
Let's see the source code:
import torch
from torch.utils.cpp_extension import load_inline
from PIL import Image
import numpy as np
import cv2 # Pentru a salva animația ca video
from google.colab import files # Pentru a descărca rezultatul
import os
import shutil

# --- 0. Curățare și Pregătire Mediu ---
# Instalăm ninja dacă nu există
!pip install ninja -q

# Ștergem cache-ul vechi pentru a forța recompilarea curată
extensions_dir = '/root/.cache/torch_extensions/py312_cu128/swim_animator'
if os.path.exists(extensions_dir):
shutil.rmtree(extensions_dir)
print(f"Cache șters: {extensions_dir}")

# Verificăm GPU
print(f"GPU disponibil: {torch.cuda.is_available()}")
if torch.cuda.is_available():
print(f"Nume GPU: {torch.cuda.get_device_name(0)}")


# --- 1. Codul CUDA (Efect de Bilă de Zoom) ---
cuda_source = r"""
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <math.h>

__global__ void zoom_ball_kernel(
const unsigned char* __restrict__ input,
unsigned char* __restrict__ output,
int width, int height, float ball_center_x, float max_zoom)
{
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;

if (x < width && y < height) {
float ball_center_y = height / 2.0f;
float ball_radius = height / 2.0f; // Bila e înaltă cât imaginea

// 1. Calculăm distanța de la pixel la centrul bilei
float dx = x - ball_center_x;
float dy = y - ball_center_y;
float distance = sqrtf(dx*dx + dy*dy);

float src_x = (float)x;
float src_y = (float)y;

// 2. Dacă pixelul e în interiorul bilei, aplicăm zoom sferic
if (distance < ball_radius) {
// Factor de atenuare: 1 în centru, 0 la margine (smooth)
float norm_dist = distance / ball_radius;
float falloff = cosf(norm_dist * 3.14159f * 0.5f); // Smooth curve

// Calculăm zoom-ul local (maxim în centru, 1 la margine)
float current_zoom = 1.0f + (max_zoom - 1.0f) * falloff;

// Coordonate sursă modificate sferic față de centrul bilei
src_x = ball_center_x + (dx / current_zoom);
src_y = ball_center_y + (dy / current_zoom);
}

// 3. Limităm coordonatele la dimensiunea imaginii (handling edge cases)
int isrc_x = max(0, min(width - 1, (int)src_x));
int isrc_y = max(0, min(height - 1, (int)src_y));

int out_idx = (y * width + x) * 3;
int src_idx = (isrc_y * width + isrc_x) * 3;

output[out_idx] = input[src_idx]; // R
output[out_idx + 1] = input[src_idx + 1]; // G
output[out_idx + 2] = input[src_idx + 2]; // B
}
}

torch::Tensor apply_zoom_ball_effect(torch::Tensor input, float ball_center_x, float max_zoom) {
const int height = input.size(0);
const int width = input.size(1);
auto output = torch::empty_like(input);

dim3 block_dim(16, 16);
dim3 grid_dim((width + block_dim.x - 1) / block_dim.x,
(height + block_dim.y - 1) / block_dim.y);

zoom_ball_kernel<<<grid_dim, block_dim>>>(
input.data_ptr<unsigned char>(),
output.data_ptr<unsigned char>(),
width, height, ball_center_x, max_zoom);

return output;
}
"""

# Header C++ necesar
cpp_source = """
#include <torch/extension.h>
torch::Tensor apply_zoom_ball_effect(torch::Tensor input, float ball_center_x, float max_zoom);
"""

# --- 2. Compilare (JIT) ---
print("Compilăm kernel-ul CUDA... (poate dura 30-60 secunde)")
swim_module = load_inline(
name="swim_animator",
cpp_sources=cpp_source,
cuda_sources=cuda_source,
functions=["apply_zoom_ball_effect"],
extra_cuda_cflags=["-arch=sm_75"],
verbose=False
)
print("Compilare reușită!")


# --- 3. Funcția de procesare animație ---
def create_zoom_ball_video(image_path, output_video="bila_zoom.mp4"):
# Încărcare imagine
if not os.path.exists(image_path):
print(f"Eroare: Imaginea {image_path} nu există.")
return

img_pil = Image.open(image_path).convert('RGB')
width, height = img_pil.size
img_tensor = torch.from_numpy(np.array(img_pil)).cuda()

# Configurare video
fps = 30
duration_sec = 5 # Puțin mai lung pentru a vedea toată traversarea
num_frames = fps * duration_sec
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
video_writer = cv2.VideoWriter(output_video, fourcc, fps, (width, height))

print(f"Generăm {num_frames} cadre pe GPU...")

# Parametrii efectului
max_zoom = 2.5 # Cât de puternic e zoom-ul în centrul bilei
ball_radius = height / 2.0

# Calculăm traiectoria: din dreapta-afară în stânga-afară
# Start: width + radius, End: -radius
start_x = width + ball_radius
end_x = -ball_radius

for i in range(num_frames):
# Progresul animației de la 0.0 la 1.0
progress = i / (num_frames - 1)

# Poziția X curentă a bilei (interpolare liniară)
current_ball_x = start_x + (end_x - start_x) * progress

# Apelăm kernel-ul CUDA
frame_tensor = swim_module.apply_zoom_ball_effect(img_tensor, current_ball_x, max_zoom)

# Înapoi pe CPU și în format OpenCV (BGR)
frame_np = frame_tensor.cpu().numpy()
frame_bgr = cv2.cvtColor(frame_np, cv2.COLOR_RGB2BGR)
video_writer.write(frame_bgr)

video_writer.release()
print("Video salvat!")

# --- 4. Execuție ---
# Descărcăm o imagine de test mai lată pentru a vedea efectul de traversare
#!wget -O peisaj.jpg https://upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Altja_j%C3%B5gi_Lahemaal.jpg/1280px-Altja_j%C3%B5gi_Lahemaal.jpg
!wget -O peste.jpg https://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/Balantiocheilos_melanopterus_-_Karlsruhe_Zoo_02_%28cropped%29.jpg/960px-Balantiocheilos_melanopterus_-_Karlsruhe_Zoo_02_%28cropped%29.jpg
create_zoom_ball_video("peste.jpg")

# Descarcă rezultatul
files.download("bila_zoom.mp4")

Python 3.13.0 : converting documents to Markdown.

Python tool for converting files and office documents to Markdown.
Easy to use:
markitdown path-to-file.pdf > document.md
Or use -o to specify the output file:
markitdown path-to-file.pdf -o document.md
You can also pipe content:
cat path-to-file.pdf | markitdown
The project can be found on this GitHub repo.
Let's install with these commands:
git clone https://github.com/microsoft/markitdown.git
Cloning into 'markitdown'...
remote: Enumerating objects: 2168, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 2168 (delta 0), reused 0 (delta 0), pack-reused 2162 (from 2)
Receiving objects: 100% (2168/2168), 4.15 MiB | 2.50 MiB/s, done.
Resolving deltas: 100% (1238/1238), done.
Updating files: 100% (161/161), done.

cd markitdown

python -m pip install -e "packages/markitdown[all]"
Obtaining file:///C:/Python313_64bit/markitdown/packages/markitdown
...
Successfully installed XlsxWriter-3.2.9 azure-ai-documentintelligence-1.0.2 azure-core-1.39.0 azure-identity-1.25.3 
cobble-0.1.4 coloredlogs-15.0.1 humanfriendly-10.0 isodate-0.7.2 magika-0.6.3 mammoth-1.11.0 markdownify-1.2.2 
markitdown-0.1.6b2 msal-1.36.0 msal-extensions-1.3.1 olefile-0.47 onnxruntime-1.20.1 pdfminer-six-20251230 
pdfplumber-0.11.9 pypdfium2-5.7.0 python-pptx-1.0.2 speechrecognition-3.16.0 standard-aifc-3.13.0 standard-chunk-3.13.0 
xlrd-2.0.2 youtube-transcript-api-1.0.3

Python Qt : manages python packages from web.

Today, I created a script that looks for updates and manages them along with the already installed packages.
The script relies on three categories of data sources, all of them public, stable, and widely used in the Python ecosystem.
  • PyPI RSS Feeds (Official Python Package Index)
    • These are the official feeds published by PyPI.
    • 1. New packages feed
      • https://pypi.org/rss/packages.xml
      • This feed lists brand‑new packages uploaded to PyPI.
    • 2. Updated packages feed
      • https://pypi.org/rss/updates.xml
      • This feed lists new releases of existing packages.
    • These feeds are the most authoritative source for real‑time package activity.
  • Libraries.io Atom Feeds (Per‑package version history)
    • For each installed package, the script uses:
    • https://libraries.io/pypi/{PACKAGE_NAME}/versions.atom
    • This feed provides:
      • version history
      • release timestamps
      • links to source repositories
      • metadata about each release
    • Libraries.io aggregates data from PyPI, GitHub, GitLab, Bitbucket, and more.
  • Local Python Installation (importlib.metadata)
    • The script reads the list of packages installed on your system using:
    • importlib.metadata.distributions()
    • This gives:
      • installed package names
      • installed versions
      • metadata from the local environment
    • This is how the script knows whether to show Install or Update buttons.
  • SQLite Local Database (packages.db)
    • The script maintains a small local database that stores:
      • package name
      • installed version
      • last update timestamp
    • This allows the UI to:
      • remember previous states
      • show consistent information
      • track updates over time
What the Script Actually Does
  • On startup
    • Reads all installed Python packages
    • Synchronizes them into a SQLite database
    • Loads the three main tabs:
      • PyPI New Packages
      • PyPI Updated Packages
      • Libraries.io Installed Packages
  • In the PyPI tabs
    • For every RSS entry:
      • Extracts the package name
      • Checks if it is installed locally
      • Shows:
        • Title
        • Publish date
        • Link to PyPI
        • Install button (if not installed)
        • Update button (if installed)
    • This turns the PyPI feed into a package manager dashboard.
  • In the Libraries.io tab
    • For every installed package:
      • Builds the Libraries.io feed URL
      • Fetches version history
      • Displays:
        • Installed version
        • Release history
        • Links to releases
        • Update button
    • This tab becomes a per‑package release monitor.
  • When you click Install or Update
    • A confirmation dialog appears
    • If confirmed:
      • Runs python -m pip install PACKAGE
      • Or python -m pip install --upgrade PACKAGE
      • Runs pip in a background thread
      • Updates the SQLite database
      • Refreshes the UI
    • This makes the script a GUI package manager for Python.
See this demo real from my youtube channel:

Monday, April 13, 2026

Python 3.13.0 : bypasses pygame‑ce and use directly to Windows with ctypes.

Today, I test bypasses pygame‑ce and use directly to Windows, because the Python 3.13 + pygame‑ce 2.5.7, where DPI functions are missing.
You can read more about this idea on my pygame blogger, see the blogger post.
Windows exposes thousands of functions through: user32.dll, gdi32.dll, shcore.dll, kernel32.dll, dwmapi.dll.
If the OS provides the feature → Python can call it via ctypes.
Python can call Windows API functions directly whenever the OS provides a stable API, and you only perform operations that are safe at the OS level.
These are always safe to do from Python using ctypes, because they only interact with the OS, not with internal memory of another library.
  • Reading information
    • DPI
    • monitor list
    • window position
    • window size
    • screen resolution
    • system metrics
    • OS version
    • keyboard/mouse state
    • window styles
    • process info
  • Calling OS-level functions that modify the window
    • move window
    • resize window
    • change window title
    • change window transparency
    • change window z-order
    • set DPI awareness
    • toggle fullscreen
    • minimize / maximize
  • Creating new OS objects
    • timers
    • threads
    • windows (if you want)
    • file handles
    • pipes
    • events
  • Using OS-level graphics
    • GDI drawing
    • DWM effects
    • Aero shadow
    • blur behind window
  • Unsafe
    • Writing into internal memory of SDL2, Python, or any DLL
    • Overwriting function pointers
    • Injecting hooks
    • Modifying struct layouts
    • Freeing memory you don’t own
This is just one part of source code:
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
...
user32.EnumDisplayMonitors(0, 0, MonitorEnumProc(_monitor_enum_proc), 0)
...

Python 3.13.0 : testing streamlit Python framework for data scientists and AI/ML engineers.

Streamlit is an open-source Python framework for data scientists and AI/ML engineers to deliver interactive data apps – in only a few lines of code.
I run the module streamlit directly, even if Windows cannot find the streamlit command:
python -m pip install streamlit
Collecting streamlit
  Downloading streamlit-1.56.0-py3-none-any.whl.metadata (9.8 kB)
...
Successfully installed altair-6.0.0 gitdb-4.0.12 gitpython-3.1.46 pydeck-0.9.1 smmap-5.0.3 streamlit-1.56.0
Let's test it:
python -m streamlit hello

      Welcome to Streamlit!

      If you'd like to receive helpful onboarding emails, news, offers, promotions,
      and the occasional swag, please enter your email address below. Otherwise,
      leave this field blank.

      Email: ←[0m

  You can find our privacy policy at https://streamlit.io/privacy-policy

  Summary:
  - This open source library collects usage statistics.
  - We cannot see and do not store information contained inside Streamlit apps,
    such as text, charts, images, etc.
  - Telemetry data is stored in servers in the United States.
  - If you'd like to opt out, add the following to %userprofile%/.streamlit/config.toml,
    creating that file if necessary:

    [browser]
    gatherUsageStats = false


  Welcome to Streamlit. Check out our demo in your browser.

  Local URL: http://localhost:8501
  Network URL: http://192.168.1.75:8501

  Ready to create your own Python apps super quickly?
  Head over to https://docs.streamlit.io

  May you create awesome apps!
... and result is this:

Saturday, April 11, 2026

Python 3.13.0 : new video with qiskit and Serotonină versus Formaldehidă python script.

I wrote about this python package on my blogs and it is very useful for those who study quantum theory. Today, seeing this video from I.B.M. I decided to see how it works on a particular case, obviously using the Gemini artificial intelligence from Google.
First of all, I was never good at chemistry because I started in elementary school on a different correct informational substrate and it didn't match my math... After academic studies I can now say that the assimilation is different and that's why I took this test with this python package.
About the script I used is an quantum simulation between Serotonină versus Formaldehidă with Hartree energy.
Terminology: It uses standard scientific terms like "Covalent Bond" (the strong link for formaldehyde) and "Signal Transmitted" (the function of serotonin).
The Hartree Energy (Eh) is the atomic unit of energy. It is defined by the energy of an electron in a hydrogen atom in its ground state (technically, it is twice the ionization energy of hydrogen).
Hamiltonian Clarity: clarify that II is the base energy while ZZ and XX represent the quantum correlations between the atoms.
Future-Proofing: By using real_amplitudes (lowercase), the script will remain functional even after Qiskit 3.0 is released and the older RealAmplitudes class is removed.
This script is a Quantum Variational Eigensolver (VQE) simulation designed to calculate the lowest energy state (ground state) of two different molecular interactions.
The Quantum Template Ansatz (QTA) is a specialized strategy used in Variational Quantum Algorithms (VQAs), such as the Variational Quantum Eigensolver (VQE). In simple terms, an "Ansatz" is a mathematical guess or a starting structure.
The SLSQP (Sequential Least Squares Programming) optimizer is one of the most popular "classical" algorithms used in the hybrid quantum-classical loop. It is a gradient-based optimization method designed to solve non-linear programming problems.
What the script does
Defines the Problem - Hamiltonians : It converts chemical data into a "mathematical map" called a Hamiltonian. We used two different maps: one for Formaldehyde to simulating a strong, permanent bond and one for Serotonin to simulating a delicate, temporary signal.
Creates a Quantum Template Ansatz: It builds a quantum circuit (real_amplitudes) that acts as a flexible "key." The algorithm turns the "knobs" (parameters) of this key to find the shape that fits the energy map perfectly.
Finds the Minimum Energy: It uses a classical optimizer (SLSQP) to guide the quantum simulator until the lowest possible energy value is found.
Why you got this specific result
Formaldehyde (-2.7614 Hartree): The energy is very low (negative), which indicates a highly stable and strong "Covalent Bond." In chemistry, the more negative the energy, the harder it is to break that bond. This is why formaldehyde is dangerous—it "locks" onto proteins and doesn't let go.
Serotonin (-1.6942 Hartree): The energy is higher (less negative) than formaldehyde. This represents a "Transient Interaction." It is stable enough to send a signal to your brain, but weak enough to be released later so the receptor can reset.
The 0.01 Hartree Warning: We added a deliberate 0.01 error to simulate the "noise" of a real quantum computer. Because Serotonin's interaction is so delicate, an error of 0.59% is enough to make the simulation unreliable. This highlights why Chemical Accuracy is the biggest challenge in quantum medicine—if our "glasses" (the computer) are even slightly blurry, we cannot correctly predict if a drug will work.
In summary: Your result confirms that Formaldehyde is a permanent "toxin" while Serotonin is a flexible "messenger," and it proves that current quantum simulations need extreme precision to be biologically useful.
The result of the quantum simulation , over the magnitude.These small changes matter. Because a Hartree is such a large unit of energy, researchers often convert the final result into units used in a lab setting. While 74 Hartrees sounds like a lot, chemical reactions happen in the "milli-Hartree" range. See result:
--- QUANTUM SIMULATION INPUT PARAMETERS ---
Formaldehyde Hamiltonian: [('II', -1.8572), ('IZ', 0.45), ('ZI', -0.45), ('ZZ', -0.02), ('XX', 0.21)]
Serotonin    Hamiltonian: [('II', -1.1245), ('IZ', 0.28), ('ZI', -0.28), ('ZZ', -0.01), ('XX', 0.15)]
------------------------------------------------------------

--- QUANTUM ANALYSIS: PROTEIN INTERACTION ---

[FORMALDEHYDE + PROTEIN]
  Ideal Energy: -2.7614 Hartree
  Energy with Noise (0.01): -2.7514 Hartree
  Status: DANGER - Stable Covalent Bond

[SEROTONIN + RECEPTOR]
  Ideal Energy: -1.6942 Hartree
  Energy with Noise (0.01): -1.6842 Hartree
  Status: ACTIVE - Signal Transmitted

============================================================
CONCLUSION ON CHEMICAL ACCURACY:
An error of 0.01 Hartree represents 0.59% of Serotonin's energy.
WARNING: Error exceeds the safety threshold for biological prediction!
============================================================
This is the source code, created by Gemini artificial intelligence and tested by my:
import numpy as np
from qiskit.quantum_info import SparsePauliOp
from qiskit.circuit.library import real_amplitudes
from qiskit.primitives import StatevectorEstimator
from qiskit_algorithms import VQE
from qiskit_algorithms.optimizers import SLSQP

# --- QUANTUM SIMULATION INPUT DATA ---
# Formaldehyde + Protein (Strong Covalent bond simulation)
# High base energy (-1.85) and strong coupling terms
formaldehyde_input = [
    ("II", -1.8572), ("IZ", 0.45), ("ZI", -0.45), ("ZZ", -0.02), ("XX", 0.21)
]

# Serotonin + Receptor (Delicate Neurotransmitter interaction)
# Lower base energy (-1.12) and sensitive correlation terms
serotonin_input = [
    ("II", -1.1245), ("IZ", 0.28), ("ZI", -0.28), ("ZZ", -0.01), ("XX", 0.15)
]

def run_quantum_vqe(input_data):
    """Executes the VQE algorithm using the modern StatevectorEstimator."""
    hamiltonian = SparsePauliOp.from_list(input_data)
    # Using the functional 'real_amplitudes' to avoid Deprecation Warnings
    ansatz = real_amplitudes(num_qubits=2, reps=1)
    estimator = StatevectorEstimator()
    optimizer = SLSQP(maxiter=100)
    
    vqe = VQE(estimator, ansatz, optimizer)
    result = vqe.compute_minimum_eigenvalue(hamiltonian)
    return result.eigenvalue.real

# Execution
print("--- QUANTUM SIMULATION INPUT PARAMETERS ---")
print(f"Formaldehyde Hamiltonian: {formaldehyde_input}")
print(f"Serotonin    Hamiltonian: {serotonin_input}")
print("-" * 60)

# Computing Ideal Energies
ideal_energy_form = run_quantum_vqe(formaldehyde_input)
ideal_energy_sero = run_quantum_vqe(serotonin_input)

# Simulating the Chemical Accuracy Threshold (0.01 Hartree)
chemical_accuracy_threshold = 0.01
noisy_energy_form = ideal_energy_form + chemical_accuracy_threshold
noisy_energy_sero = ideal_energy_sero + chemical_accuracy_threshold

# --- FINAL QUANTUM ANALYSIS OUTPUT ---
print("\n--- QUANTUM ANALYSIS: PROTEIN INTERACTION ---")

print(f"\n[FORMALDEHYDE + PROTEIN]")
print(f"  Ideal Energy: {ideal_energy_form:.4f} Hartree")
print(f"  Energy with Noise (0.01): {noisy_energy_form:.4f} Hartree")
print(f"  Status: {'DANGER - Stable Covalent Bond' if ideal_energy_form < -1.5 else 'Inactive'}")

print(f"\n[SEROTONIN + RECEPTOR]")
print(f"  Ideal Energy: {ideal_energy_sero:.4f} Hartree")
print(f"  Energy with Noise (0.01): {noisy_energy_sero:.4f} Hartree")
print(f"  Status: {'ACTIVE - Signal Transmitted' if ideal_energy_sero < -1.0 else 'Binding Failure'}")

print("\n" + "="*60)
print("CONCLUSION ON CHEMICAL ACCURACY:")
error_percentage = (chemical_accuracy_threshold / abs(ideal_energy_sero)) * 100
print(f"An error of 0.01 Hartree represents {error_percentage:.2f}% of Serotonin's energy.")
if error_percentage > 0.5:
    print("WARNING: Error exceeds the safety threshold for biological prediction!")
print("="*60)

Friday, April 10, 2026

Python Qt : particles with pygame and pyqt6.

Today, I test one python script with pygame and pyqt6. The python script use classes and show particles. See the result:
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())

Thursday, April 9, 2026

Python 3.14.3 : Simple project with django version 6.0.4 on Fedora 44 beta 12.

Today I used Fedora 44 beta 12 server on VirtualBox, and I wanted to test how Python works with Django on this Linux distribution. In the Fedora distribution, I have Python version 3.14.3 and Django version 6.0.4. I created a basic project with multiple pages, CSS, JavaScript, and an admin interface. It seems that Django is functional, and the result is very good for a basic project.:

Wednesday, April 8, 2026

Python Qt6 : build a mini tool for memory game with PyQt6 !

Today, I tested this python script with PyQt6 to create this matrix data for memory game by click on canvas.
I add few lines of source code and I create the memory game window to test that matrix:

Sunday, March 29, 2026

News : Released v3.9.0 for NiceGUI.

The nicegui python package was released few days ago on the GitHub project, you can install with the pip tool:
python -m pip install nicegui
WARNING: Ignoring invalid distribution ~adquery-ocp (C:\Python313_64bit\Lib\site-packages)
Collecting nicegui
  Downloading nicegui-3.9.0-py3-none-any.whl.metadata (11 kB)
...
Successfully installed aiofiles-25.1.0 httptools-0.7.1 ifaddr-0.2.0 lxml-html-clean-0.4.4 markdown2-2.5.5 nicegui-3.9.0
You can find some examples on the official webpage.

Monday, March 23, 2026

News : Release 0.8.0 of mt940 library.

The mt940 is a library to parse MT940 files. MT940 is a specific SWIFT message type used by the SWIFT network to send and receive end-of-day bank account statements.
The install is easy with the pip tool:
pip install mt940==0.8.0
See the type file MT940 and the .
See the released on the official webpage.

Sunday, March 22, 2026

Python Qt6 : Folder encrypt and decrypt tool with PBKDF2 SALT derived key.

This script securely encrypts both the contents and the name of a folder using a password‑derived Fernet key (AES‑128 + HMAC), where PBKDF2 with a fixed SALT key.
A SALT is used in password‑based key derivation. If your system had no SALT, and someone used a weak password, an attacker could instantly look up the key in a rainbow table.
With a SALT the attacker would need a separate rainbow table for every possible SALT, which is infeasible.
SALT does NOT protect against brute‑force, because PBKDF2 treats the SALT as an input parameter, not as part of the password’s entropy.

Thursday, March 12, 2026

Python Qt6 : schemdraw - 001.

Today, simple example with PyQt6 and schemdraw :
# -*- coding: utf-8 -*-
import sys
import schemdraw
import schemdraw.elements as elm
from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout, QScrollArea
from PyQt6.QtGui import QPixmap
def draw_bistabil_grid(state):
    """
    state = 0 -> switch spre R3, LED1 aprins
    state = 1 -> switch spre R4, LED2 aprins
    """
    with schemdraw.Drawing(file='bistabil_grid.png', show=False) as d:
        d.config(unit=2.0)

        # Y COORDINATES
        y_led = 0.0
        y_r1  = -2.0
        y_rfb = -4.0
        y_swT = -7.0
        y_gnd = -10.0

        # X COORDINATES
        x_left   = -6.0
        x_right  = 6.0
        x_t1     = -3.0
        x_t2     = 3.0
        x_sw     = 0.0

        # COLORS
        col_T1 = 'green'
        col_T2 = 'orange'

        # LED-uri colorate în funcție de state
        led1_color = 'red' if state == 0 else 'gray'
        led2_color = 'red' if state == 1 else 'gray'

        # 1. LED1 and LED2
        led1 = d.add(elm.LED().down().at((x_left, y_led)).color(led1_color).label('LED1'))
        led2 = d.add(elm.LED().down().at((x_right, y_led)).color(led2_color).label('LED2'))

        # 2. R1 and R2
        r1 = d.add(elm.Resistor().down().at(led1.end).label('R1'))
        r2 = d.add(elm.Resistor().down().at(led2.end).label('R2'))

        n_r1 = d.add(elm.Dot().at(r1.end))
        n_r2 = d.add(elm.Dot().at(r2.end))

        # 3. R3 and R4 (feedback)
        d.add(elm.Line().at(n_r1.center).to((x_t1, y_rfb)))
        r3 = d.add(elm.Resistor().right().at((x_t1, y_rfb)).label('R3'))
        n_r3 = d.add(elm.Dot().at(r3.end))

        d.add(elm.Line().at(n_r2.center).to((x_t2, y_rfb)))
        r4 = d.add(elm.Resistor().left().at((x_t2, y_rfb)).label('R4'))
        n_r4 = d.add(elm.Dot().at(r4.end))

        # 4. Transistors
        t1 = d.add(
            elm.BjtNpn()
            .left()
            .flip()
            .at((x_t1, y_swT))
            .anchor('base')
            .label('T1')
        )

        t2 = d.add(
            elm.BjtNpn()
            .right()
            .at((x_t2, y_swT))
            .anchor('base')
            .label('T2')
        )

        # BASE CONNECTIONS
        d.add(elm.Line().at(n_r4.center).to(t1.base).color(col_T1))
        d.add(elm.Line().at(n_r3.center).to(t2.base).color(col_T2))

        # COLLECTOR CONNECTIONS
        d.add(elm.Line().at(t1.collector).to(n_r1.center).color(col_T1))
        d.add(elm.Line().at(t2.collector).to(n_r2.center).color(col_T2))

        # -------------------------------
        # 5. SWITCH SPDT DESENAT MANUAL
        # -------------------------------

        # Punctele a, b, c
        a = d.add(elm.Dot().at((x_sw - 1, y_swT)))
        b = d.add(elm.Dot().at((x_sw,     y_swT)))
        c = d.add(elm.Dot().at((x_sw + 1, y_swT)))

        # Linii FIXE către R3 și R4
        d.add(elm.Line().at(n_r3.center).to(a.center))
        d.add(elm.Line().at(n_r4.center).to(c.center))

        # Linia FIXĂ către GND
        d.add(elm.Line().at(b.center).to((b.center[0], y_gnd)))

        # Brațul mobil (UNICA linie care se mișcă)
        if state == 0:
            d.add(elm.Line().at(b.center).to(a.center))
        else:
            d.add(elm.Line().at(b.center).to(c.center))

        # 6. Ground bus
        d.add(
            elm.Line()
            .at((x_left - 2, y_gnd))
            .to((x_right + 2, y_gnd))
            .label('0V', loc='bottom')
        )
        d.add(elm.Ground().at((x_left - 2, y_gnd)))

        # EMITTERS TO GND
        d.add(elm.Line().at(t1.emitter).to((t1.emitter[0], y_gnd)).color(col_T1))
        d.add(elm.Line().at(t2.emitter).to((t2.emitter[0], y_gnd)).color(col_T2))


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

        self.state = 0

        self.setWindowTitle("Bistabil Animat – Schema completă")

        # Scroll area
        self.scroll = QScrollArea()
        self.label = QLabel()
        self.scroll.setWidget(self.label)
        self.scroll.setWidgetResizable(True)

        self.button = QPushButton("Comută switch-ul")
        self.button.clicked.connect(self.toggle_switch)

        layout = QVBoxLayout()
        layout.addWidget(self.scroll)
        layout.addWidget(self.button)
        self.setLayout(layout)

        self.update_image()

    def toggle_switch(self):
        self.state = 1 - self.state
        self.update_image()

    def update_image(self):
        draw_bistabil_grid(self.state)
        pix = QPixmap("bistabil_grid.png")
        self.label.setPixmap(pix)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    gui = BistabilGUI()
    gui.show()
    sys.exit(app.exec())