analitics

Pages

Thursday, May 21, 2026

Python 3.10.11 : testing zernio social platform with python.

Today, I get two free account on the zernio webpage.
This python script does three things:
First calls Zernio by sends GET https://zernio.com/api/v1/accounts using your Bearer token and loads the JSON response.
Then filters YouTube accounts: from the returned accounts[] list, it keeps only the items that look like YouTube (based on fields like platform/provider/network == "youtube").
Last one, prints and searches identifiers:
It prints the full JSON object for each connected YouTube account (first ~4000 characters), so you can see what fields Zernio returns.
It recursively scans that object for any string that matches a YouTube Channel ID pattern (strings starting with UC...) and prints the path and value for each match (e.g., meta.channelId = UCxxxx).
The result returns by printing to the console:
how many YouTube accounts Zernio returned.
the JSON for each YouTube account object.
a list of candidate channelId strings (and where they appear in the JSON).
Let's see the script:
import requests
import json
import re

ZERNIO_TOKEN = "sk_API_KEY"
API_BASE = "https://zernio.com/api/v1"

headers = {
    "Authorization": f"Bearer {ZERNIO_TOKEN}",
    "Accept": "application/json",
}

def is_youtube_account(acc: dict) -> bool:
    for k in ("platform", "provider", "network"):
        v = acc.get(k)
        if isinstance(v, str) and v.lower() == "youtube":
            return True
    return False

def find_uc_strings(obj, path=""):
    hits = []
    if isinstance(obj, dict):
        for k, v in obj.items():
            hits += find_uc_strings(v, f"{path}.{k}" if path else k)
    elif isinstance(obj, list):
        for i, v in enumerate(obj):
            hits += find_uc_strings(v, f"{path}[{i}]")
    elif isinstance(obj, str):
        # Typical YouTube channel id: starts with UC and is 24 chars, but we’ll be flexible
        if re.match(r"^UC[a-zA-Z0-9_-]{10,}$", obj):
            hits.append((path, obj))
    return hits

resp = requests.get(f"{API_BASE}/accounts", headers=headers, timeout=30)
resp.raise_for_status()
data = resp.json()

accounts = data.get("accounts", [])
yt = [a for a in accounts if is_youtube_account(a)]

print(f"Found {len(yt)} YouTube accounts in Zernio.\n")

for idx, acc in enumerate(yt):
    print(f"--- YouTube account #{idx} full object ---")
    print(json.dumps(acc, ensure_ascii=False, indent=2)[:4000])  # first 4000 chars
    print()

    hits = find_uc_strings(acc)
    print("Possible channelId candidates (paths):")
    for p, v in hits:
        print(f"  - {p} = {v}")
    print()
This is a part of result:
...
  "displayName": "Cătălin George Feștilă",
  "enabled": true,
  "externalPostCount": 63,
  "followersCount": 110,
  "followersLastUpdated": "2026-05-21T18:31:51.857Z",
  "gcpProjectId": "default",
  "intentionalDisconnectAt": null,
  "isActive": true,
...
Possible channelId candidates (paths):
  - metadata.profileData.id = UC2Dv01HhPCb8Obb9IxO81Jw
  - platformUserId = UC2Dv01HhPCb8Obb9IxO81Jw

Sunday, May 17, 2026

Python Qt : network tool with PyQt6-Charts.

Today, this python script will create a tool for network with PyQt6-Charts.
You need to install the PyQt6-Charts:
python.exe -m pip install PyQt6-Charts
Collecting PyQt6-Charts
...
Installing collected packages: PyQt6-Charts-Qt6, PyQt6-Charts
Successfully installed PyQt6-Charts-6.11.0 PyQt6-Charts-Qt6-6.11.1
Let's see the python script.
import sys
import psutil
from pathlib import Path

from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel,
    QTableWidget, QTableWidgetItem, QHeaderView
)
from PyQt6.QtCore import QTimer, Qt
from PyQt6.QtCharts import QChart, QChartView, QLineSeries, QValueAxis


def bytes_to_human(n: int) -> str:
    symbols = ('B', 'KB', 'MB', 'GB', 'TB')
    prefix = {}
    for i, s in enumerate(symbols[1:], 1):
        prefix[s] = 1 << (i * 10)
    for s in reversed(symbols[1:]):
        if n >= prefix[s]:
            value = float(n) / prefix[s]
            return f"{value:.2f} {s}"
    return f"{n} B"


class NetworkMonitor(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Network Monitor – PyQt6 (Traffic + Ports)")
        self.resize(1000, 650)

        main_layout = QVBoxLayout(self)

        # ---------------- TOP LABELS ----------------
        self.label_up = QLabel("Upload: 0 B/s")
        self.label_down = QLabel("Download: 0 B/s")

        top_layout = QHBoxLayout()
        top_layout.addWidget(self.label_up)
        top_layout.addWidget(self.label_down)
        main_layout.addLayout(top_layout)

        # ---------------- CHART ----------------
        self.series_up = QLineSeries()
        self.series_down = QLineSeries()
        self.series_up.setName("Upload")
        self.series_down.setName("Download")

        self.chart = QChart()
        self.chart.addSeries(self.series_up)
        self.chart.addSeries(self.series_down)
        self.chart.setTitle("Network traffic (bytes/sec)")

        # Axes (Qt6 style)
        self.axis_x = QValueAxis()
        self.axis_y = QValueAxis()

        self.axis_x.setRange(0, 60)
        self.axis_y.setRange(0, 1024 * 1024)  # 1 MB/s default
        self.axis_x.setTitleText("Time (s)")
        self.axis_y.setTitleText("Bytes / second")

        self.chart.addAxis(self.axis_x, Qt.AlignmentFlag.AlignBottom)
        self.chart.addAxis(self.axis_y, Qt.AlignmentFlag.AlignLeft)

        self.series_up.attachAxis(self.axis_x)
        self.series_up.attachAxis(self.axis_y)
        self.series_down.attachAxis(self.axis_x)
        self.series_down.attachAxis(self.axis_y)

        self.chart_view = QChartView(self.chart)
        main_layout.addWidget(self.chart_view)

        # ---------------- PORTS TABLE ----------------
        self.table = QTableWidget(0, 5)
        self.table.setHorizontalHeaderLabels(
            ["Local IP:Port", "Remote IP:Port", "Status", "PID", "Process"]
        )
        self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)

        # 🔥 Sortare activată
        self.table.setSortingEnabled(True)

        main_layout.addWidget(self.table)

        # ---------------- STATE ----------------
        self.old_stats = psutil.net_io_counters()
        self.x_pos = 0

        # ---------------- TIMER ----------------
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_stats)
        self.timer.start(1000)  # 1s

    # ---------------------------------------------------------
    def update_stats(self):
        # --- traffic ---
        new_stats = psutil.net_io_counters()
        sent = new_stats.bytes_sent - self.old_stats.bytes_sent
        recv = new_stats.bytes_recv - self.old_stats.bytes_recv
        self.old_stats = new_stats

        self.label_up.setText(f"Upload: {bytes_to_human(sent)}/s")
        self.label_down.setText(f"Download: {bytes_to_human(recv)}/s")

        # --- chart data ---
        self.series_up.append(self.x_pos, sent)
        self.series_down.append(self.x_pos, recv)
        self.x_pos += 1

        # keep last 60 seconds visible
        if self.x_pos > 60:
            self.axis_x.setRange(self.x_pos - 60, self.x_pos)

        # auto-scale Y a bit
        max_val = max(sent, recv, 1)
        current_max = self.axis_y.max()
        if max_val > current_max * 0.9:
            self.axis_y.setRange(0, max_val * 1.5)

        # --- ports ---
        self.update_ports()

    # ---------------------------------------------------------
    def update_ports(self):
        conns = psutil.net_connections(kind="tcp")

        self.table.setSortingEnabled(False)  # prevenim flicker
        self.table.setRowCount(0)

        for c in conns:
            if not c.laddr:
                continue

            row = self.table.rowCount()
            self.table.insertRow(row)

            # Local
            local = f"{c.laddr.ip}:{c.laddr.port}"
            item_local = QTableWidgetItem(local)
            item_local.setData(Qt.ItemDataRole.UserRole, c.laddr.port)

            # Remote
            if c.raddr:
                remote = f"{c.raddr.ip}:{c.raddr.port}"
                remote_port = c.raddr.port
            else:
                remote = "-"
                remote_port = -1

            item_remote = QTableWidgetItem(remote)
            item_remote.setData(Qt.ItemDataRole.UserRole, remote_port)

            # Status
            item_status = QTableWidgetItem(c.status)

            # PID
            pid = c.pid if c.pid else -1
            item_pid = QTableWidgetItem(str(pid))
            item_pid.setData(Qt.ItemDataRole.UserRole, pid)

            # Process name
            proc_name = "-"
            if c.pid:
                try:
                    proc_name = psutil.Process(c.pid).name()
                except:
                    proc_name = "?"

            item_proc = QTableWidgetItem(proc_name)

            self.table.setItem(row, 0, item_local)
            self.table.setItem(row, 1, item_remote)
            self.table.setItem(row, 2, item_status)
            self.table.setItem(row, 3, item_pid)
            self.table.setItem(row, 4, item_proc)

        self.table.setSortingEnabled(True)  # reactivăm sortarea


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

Saturday, May 16, 2026

Python 3.10.11 : about the windows embeddable portable Python distributions fix pip.

Let's learn about the windows embeddable portable Python distributions.
  • The Windows embeddable Python distribution is a minimal, self‑contained build of Python designed to run entirely from its own directory without installation.
  • This distribution does not modify system settings, environment variables, or the Windows registry.
  • Its structure makes it suitable for embedding Python inside applications or distributing Python as a portable runtime.
Typical use cases
  • Bundling Python with standalone software that requires a predictable runtime environment.
  • Running Python scripts in isolated environments where system‑wide installations must not be affected.
  • Deploying portable utilities that must operate from removable storage or restricted systems.
When the embeddable distribution is not ideal
  • General development workflows that rely on pip, external packages, or virtual environments.
  • Educational or experimental setups where tutorials assume a standard Python installation.
  • Projects that depend on automatic module discovery and dynamic package management.
The Role of python310._pth
  • The file named python310._pth controls how the embeddable distribution locates and loads Python modules.
  • When this file is present, Python enters an isolated mode in which only the paths explicitly listed inside the file are used.
  • If the file does not include the line import site, the standard site initialization process is disabled, preventing access to site‑packages.
Typical structure of python310._pth
python310.zip
.
import site
Explanation of each entry
  • python310.zip specifies the location of the standard library packaged as a zip archive.
  • . allows Python to import modules from the root directory of the distribution.
  • import site activates the site module, enabling automatic loading of Lib and site‑packages.
Enabling pip and external modules
  • The embeddable distribution does not load external modules unless the appropriate paths are added to python310._pth.
  • To enable pip and other installed packages, the file must include the Lib and Lib\site-packages directories.
Example of a Fully Enabled python310._pth
python310.zip
.
Lib
Lib\site-packages
import site
Testing the updated configuration
python -c "import sys; print(sys.path)"
Installing pip after enabling site‑packages
  • Once the module paths are active, pip can be installed using standard methods.
python get-pip.py
python -m ensurepip
Verifying pip
python -m pip --version
Advantages of the embeddable distribution:
  • Provides a predictable and isolated runtime environment.
  • Does not interfere with system‑wide Python installations.
  • Ideal for packaging Python with standalone applications.
Disadvantages of the embeddable distribution:
  • pip and external modules are disabled by default.
  • Requires manual configuration to behave like a standard installation.
  • Not suitable for typical development workflows.
Clean, Ready‑to‑Use python310._pth File
python310.zip
.
Lib
Lib\site-packages
import site
This will fix the embeddable distribution, let's use this source code to fix the pip tool:
import os
import urllib.request
import zipfile
import shutil

PYTHON_DIR = r"C:\python-3_10_11"
SITE = fr"{PYTHON_DIR}\Lib\site-packages"

print("[INFO] Descarc pip.zip...")
urllib.request.urlretrieve(
    "https://github.com/pypa/pip/archive/refs/heads/main.zip",
    "pip.zip"
)

print("[INFO] Dezarhivez pip.zip...")
with zipfile.ZipFile("pip.zip", "r") as z:
    z.extractall("pip_src")

pip_src = "pip_src/pip-main/src/pip"

print("[INFO] Copiez pip în site-packages...")
target = os.path.join(SITE, "pip")
if os.path.exists(target):
    shutil.rmtree(target)

shutil.copytree(pip_src, target)

print("[INFO] Creez pip.dist-info minimal...")
dist = os.path.join(SITE, "pip.dist-info")
os.makedirs(dist, exist_ok=True)

with open(os.path.join(dist, "METADATA"), "w") as f:
    f.write("Name: pip\nVersion: 0\n")

print("[OK] pip instalat direct în Python.")
print("Rulează acum:")
print("   python -m pip --version")
Let's tun and test with PyQt6:
python fix_pip.py
[INFO] Descarc pip.zip...
[INFO] Dezarhivez pip.zip...
[INFO] Copiez pip în site-packages...
[INFO] Creez pip.dist-info minimal...
[OK] pip instalat direct în Python.
Rulează acum:
   python -m pip --version

python -m pip --version
pip 26.2.dev0 from C:\python-3_10_11\Lib\site-packages\pip (python 3.10)

python -m pip install PyQt6
Collecting PyQt6
  Downloading pyqt6-6.11.0-cp310-abi3-win_amd64.whl.metadata (2.2 kB)
...
Installing collected packages: PyQt6-Qt6, PyQt6-sip, PyQt6
Successfully installed PyQt6-6.11.0 PyQt6-Qt6-6.11.1 PyQt6-sip-13.11.1

Thursday, May 14, 2026

News : Good news from the Python Software Foundation 14.05.2026

Good news from the Python Software Foundation official blogger.
  • May 13, 2026 – The Python Software Foundation (PSF) is excited to announce that Hudson River Trading (HRT), a global leader in quantitative trading, has made a commitment to support Python and the PSF as a Visionary Sponsor.
  • May 12, 2026 - Announcing PSF Community Service Award Recipients!
  • May 11, 2026 - Strategic Planning at the PSF
  • May 10, 2026 - Python 3.14.5 is out!
  • May 7, 2026 - Python 3.15.0 beta 1 is here!
  • May 4, 2026 - Python 3.14.5 release candidate

Thursday, May 7, 2026

Python Qt : simple tiktok downloader.

Today, simple example with PyQt6 and yt_dlp.
Get the link from tiktok browser and use it to download the video for your storage.
I used the Copilot tool. It seems to know the Romanian language. For a developer, comments and source code are not an impediment, because it is very simplistic.
Let's see the source code:
import sys
import os
import yt_dlp
from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
    QLineEdit, QLabel, QFileDialog, QListWidget, QListWidgetItem,
    QProgressBar, QMessageBox
)
from PyQt6.QtCore import Qt

class TikTokDownloader(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("TikTok Downloader (yt-dlp)")
        self.setMinimumWidth(600)

        self.url_input = QLineEdit()
        self.url_input.setPlaceholderText("Introdu URL TikTok...")

        self.folder_input = QLineEdit()
        self.folder_input.setPlaceholderText("Folder download...")

        browse_btn = QPushButton("Selectează folder")
        browse_btn.clicked.connect(self.select_folder)

        fetch_btn = QPushButton("Caută stream-uri")
        fetch_btn.clicked.connect(self.fetch_streams)

        self.stream_list = QListWidget()

        download_btn = QPushButton("Descarcă")
        download_btn.clicked.connect(self.download_selected)

        self.progress = QProgressBar()
        self.progress.setValue(0)

        layout = QVBoxLayout()
        layout.addWidget(QLabel("URL TikTok:"))
        layout.addWidget(self.url_input)

        folder_layout = QHBoxLayout()
        folder_layout.addWidget(self.folder_input)
        folder_layout.addWidget(browse_btn)
        layout.addLayout(folder_layout)

        layout.addWidget(fetch_btn)
        layout.addWidget(QLabel("Stream-uri găsite:"))
        layout.addWidget(self.stream_list)
        layout.addWidget(download_btn)
        layout.addWidget(self.progress)

        self.setLayout(layout)

        self.streams = []
        self.selected_format = None

    def select_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "Selectează folder")
        if folder:
            self.folder_input.setText(folder)

    def fetch_streams(self):
        url = self.url_input.text().strip()
        if not url:
            QMessageBox.warning(self, "Eroare", "Introdu URL TikTok")
            return

        self.stream_list.clear()
        self.streams = []

        ydl_opts = {
            "quiet": True,
            "skip_download": True,
            "forcejson": True,
        }

        try:
            with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                info = ydl.extract_info(url, download=False)

            formats = info.get("formats", [])

            for f in formats:
                desc = f"{f.get('format_id')} | {f.get('ext')} | {f.get('resolution', '')} | {f.get('filesize', 'N/A')}"
                item = QListWidgetItem(desc)
                self.stream_list.addItem(item)
                self.streams.append(f)

        except Exception as e:
            QMessageBox.critical(self, "Eroare", str(e))

    def progress_hook(self, d):
        if d["status"] == "downloading":
            if d.get("total_bytes"):
                pct = int(d["downloaded_bytes"] * 100 / d["total_bytes"])
                self.progress.setValue(pct)

        if d["status"] == "finished":
            self.progress.setValue(100)

    def download_selected(self):
        folder = self.folder_input.text().strip()
        if not folder:
            QMessageBox.warning(self, "Eroare", "Selectează folderul de download")
            return

        selected = self.stream_list.currentRow()
        if selected < 0:
            QMessageBox.warning(self, "Eroare", "Selectează un stream din listă")
            return

        fmt = self.streams[selected]
        url = self.url_input.text().strip()

        ydl_opts = {
            "outtmpl": os.path.join(folder, "%(id)s.%(ext)s"),
            "format": fmt["format_id"],
            "progress_hooks": [self.progress_hook],
        }

        try:
            with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                ydl.download([url])

            QMessageBox.information(self, "Succes", "Download complet!")

        except Exception as e:
            QMessageBox.critical(self, "Eroare", str(e))

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

Wednesday, May 6, 2026

Python 3.13.0 : another tool with PyQt6 to build an NPC database.

... another tool for game development, see more on my youtube channel.

Python 3.13.0 : mimesis python example.

Mimesis is a powerful data generator for Python that can produce a wide range of fake data in multiple languages. This tool is useful for populating testing databases, creating fake API endpoints, generating custom structures in JSON and XML files, and anonymizing production data, among other things. With Mimesis, developers can obtain realistic, randomized data easily to facilitate development and testing.
This python module can be install with pip tool easy.
See this simple example:
from mimesis import Person, Address, Text, Datetime
from mimesis.enums import Gender
def test_mimesis(locale: str, sex: str):
    person = Person(locale)
    address = Address(locale)
    text = Text(locale)
    dt = Datetime()
    gender = Gender.MALE if sex == "male" else Gender.FEMALE
    data = {
        "first_name": person.first_name(gender=gender),
        "last_name": person.last_name(),
        "full_name": person.full_name(gender=gender),
        "email": person.email(),
        "telephone": person.telephone(),
        "occupation": person.occupation(),
        "address": address.address().replace("\n", ", "),
        "city": address.city(),
        "postal_code": address.postal_code(),
        "country": address.country(),
        "birth_date": dt.date(start=1970, end=2005),
        "bio": text.text(quantity=2),
    }
    return data
if __name__ == "__main__":
    npc = test_mimesis("en", "female")
    for k, v in npc.items():
        print(f"{k}: {v}")
The result is:
python_313 test_mimesis.py
first_name: Nisha
last_name: Rocha
full_name: Hertha Wynn
email: stakeholders2063@protonmail.com
telephone: +14172413972
occupation: Medical Physicist
address: 865 Cooper Highway
city: Elkhart
postal_code: 69168
country: France
birth_date: 2004-06-11
bio: It is also a garbage-collected runtime system. Do you come here often?

Tuesday, May 5, 2026

News : Pythono and nuitka best optimization.

Nuitka is the optimizing Python compiler written in Python that creates executables that run without a separate installer. Data files can both be included or put alongside.
You can read more on the official website.
The install is easy with pip tool, then you can use this command.
python -m nuitka --help
Usage: python.exe -m nuitka [--mode=compilation_mode] [--run] [options] main_module.py

    Note: For general plugin help (they often have their own
    command line options too), consider the output of
    '--help-plugins'.

Options:
  --help                show this help message and exit
  --version             Show version information and important details for bug
                        reports, then exit. Defaults to off.
This python package need to have the Visual Studio Build Tools 2022 and need to use:
  • Desktop development with C++
  • MSVC v143 build tools
  • Windows 10/11 SDK
  • C++ CMake tools
  • C++ ATL/MFC (optional but useful)
Let's install it.
I used this simple example:
import sys
from PyQt6.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout

def main():
    app = QApplication(sys.argv)

    window = QWidget()
    window.setWindowTitle("PyQt6 + Nuitka Demo")

    layout = QVBoxLayout()
    label = QLabel("Hello! This is a PyQt6 application compiled with Nuitka.")
    layout.addWidget(label)

    window.setLayout(layout)
    window.show()

    sys.exit(app.exec())

if __name__ == "__main__":
    main()
Use this command to see all plugins:
nuitka --plugin-list
                 The following plugins are available in Nuitka
--------------------------------------------------------------------------------
 delvewheel        Required by 'delvewheel' using packages. (core)
 implicit-imports  Provide implicit imports of package as per package configuration files. (core) [auto-enabled]
 data-files        Include data files specified by package configuration files. (core, feature)
 dll-files         Include DLLs as per package configuration files. (core, feature)
 anti-bloat        Patch stupid imports out of widely used library modules source codes. (core, feature, package-support) [auto-enabled]
 options-nanny     Inform user about potential problems as per package configuration files. (core, package-support) [auto-enabled]
 pylint-warnings   Support PyLint / PyDev linting source markers. (feature)
 upx               Compress created binaries with UPX automatically. (integration)
 dill-compat       Required by 'dill' and 'cloudpickle' packages. (package-support) [has detector]
 eventlet          Required by 'eventlet' package. (package-support) [auto-enabled]
 gevent            Required by 'gevent' package. (package-support)
 gi                Required by 'gi' package. (package-support) [auto-enabled]
 glfw              Required by 'glfw' and 'PyOpenGL' packages. (package-support)
 kivy              Required by 'kivy' package. (package-support)
 matplotlib        Required by 'matplotlib' package. (package-support)
 multiprocessing   Required by 'multiprocessing' package. (package-support) [auto-enabled]
 no-qt             Disable inclusion of all Qt bindings. (package-support)
 pbr-compat        Required by 'pbr' package. (package-support)
 pkg-resources     Required by 'pkg_resources' package. (package-support) [auto-enabled]
 playwright        Required by 'playwright' package. (package-support)
 pmw-freezer       Required by 'Pmw' package. (package-support) [has detector]
 pywebview         Required by 'webview' package. (package-support)
 spacy             Required by 'spacy' package. (package-support)
 tk-inter          Required by 'tkinter' package. (package-support) [has detector]
 transformers      Required by 'transformers' package. (package-support) [auto-enabled]
 enum-compat       Required by 'enum' package on Python2. (package-support, python2)
 pyqt5             Required by 'PyQt5' package. (package-support, qt-binding) [has detector]
 pyqt6             Required by 'PyQt6' package. (package-support, qt-binding) [has detector]
 pyside2           Required by 'PySide2' package. (package-support, qt-binding) [has detector]
 pyside6           Required by 'PySide6' package. (package-support, qt-binding) [has detector]
... and standalone build process:
nuitka --standalone --enable-plugin=pyqt6 test_app.py
Nuitka-Options: Used command line options:
Nuitka-Options:   --standalone --enable-plugin=pyqt6 test_app.py
Nuitka-Plugins:pyqt6: Support for PyQt6 is not perfect, e.g. Qt threading does not work, so prefer
Nuitka-Plugins:pyqt6: PySide6 if you can.
Nuitka: Starting Python compilation with:
Nuitka:   Version '4.0.8' on Python 3.10 (flavor 'CPython Official')
Nuitka:   commercial grade 'not installed'.
Nuitka-Plugins:pyqt6: Including Qt plugins 'iconengines,imageformats,platforms,styles,tls' below
Nuitka-Plugins:pyqt6: 'PyQt6\Qt6\plugins'.
Nuitka: Completed Python level compilation and optimization.
Nuitka: Generating source code for C backend compiler.
Nuitka: Running data composer tool for optimal constant value handling.
Nuitka: Running C compilation via Scons.
Nuitka will use gcc from MinGW64 of winlibs to compile on Windows.

Is it OK to download and put it in local user cache.

Fully automatic, cached. Proceed and download? [Yes]/No :
Nuitka: Downloading
Nuitka: 'https://github.com/brechtsanders/winlibs_mingw/releases/download/14.2.0posix-19.1.1-12.0.0-msvcrt-r2/winlibs-x86_64-posix-seh-gcc-14.2.0-llvm-19.1.1-mingw-w64msvcrt-12.0.0-r2.zip'.
Nuitka: Extracting to
Nuitka: 'C:\Users\CATALI~1\AppData\Local\Nuitka\Nuitka\Cache\DOWNLO~1\gcc\x86_64\14.2.0posix-19.1.1-12.0.0-msvcrt-r2\mingw64\bin\gcc.exe'
Nuitka-Scons: Backend C compiler: gcc (gcc 14.2.0).
Nuitka-Scons: Backend C linking with 9 files (no progress information available for this stage).
Nuitka-Scons: Compiled 9 C files using ccache.
Nuitka-Scons: Cached C files (using ccache) with result 'cache miss': 9
Nuitka: Keeping build directory 'test_app.build'.
Nuitka: Successfully created 'C:\lucru\PyQt6\test_nuitka\test_app.dist\test_app.exe'.

Python Qt : python alias manager tool.

This script allow you to add alias commands on each python install.
NOTE : If this script find some python.exe and not works, it means that this python.exe is not a full Python installation, but only a launcher or an embedded runtime that is designed to run inside a virtual environment or a complete Python directory structure.
I used artificial intelligence, tested and works well, lets see the source code:
import sys
import os
import json
import subprocess
from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QPushButton,
    QTableWidget, QTableWidgetItem, QFileDialog, QMessageBox
)
from PyQt6.QtCore import Qt

CONFIG_FILE = "python_aliases.json"
ALIAS_DIR = os.path.join(os.environ["LOCALAPPDATA"], "Microsoft", "WindowsApps")

# ------------------------------
# Persistență JSON
# ------------------------------

def load_aliases():
    if os.path.isfile(CONFIG_FILE):
        try:
            with open(CONFIG_FILE, "r") as f:
                return json.load(f)
        except:
            return []
    return []

def save_aliases(data):
    with open(CONFIG_FILE, "w") as f:
        json.dump(data, f, indent=4)


# ------------------------------
# Detectare Python
# ------------------------------

def detect_where_python():
    """Detectează instalările Python folosind 'where python'."""
    found = []
    try:
        out = subprocess.check_output(["where", "python"], stderr=subprocess.STDOUT)
        lines = out.decode().splitlines()
        for line in lines:
            if line.lower().endswith("python.exe"):
                found.append(line.strip())
    except:
        pass
    return found


def search_python_in_folder_recursive(folder):
    """Caută python.exe în folder și în toate subfolderele recursiv."""
    found = []
    for root, dirs, files in os.walk(folder):
        if "python.exe" in files:
            found.append(os.path.join(root, "python.exe"))
    return found

# ------------------------------
# Detectare aliasuri existente
# ------------------------------

def detect_existing_aliases():
    """Caută aliasuri existente (*.cmd) în WindowsApps."""
    aliases = {}
    if not os.path.isdir(ALIAS_DIR):
        return aliases

    for file in os.listdir(ALIAS_DIR):
        if file.endswith(".cmd"):
            alias_name = file[:-4]
            cmd_path = os.path.join(ALIAS_DIR, file)

            try:
                with open(cmd_path, "r") as f:
                    line = f.readline().strip()
                    if line.startswith("@\"") and line.endswith("%*"):
                        python_path = line[2:-3].strip('"')
                        aliases[alias_name] = python_path
            except:
                pass

    return aliases

# ------------------------------
# Creare alias
# ------------------------------

def create_alias(alias_name, python_path):
    """Creează un fișier .cmd în WindowsApps pentru alias."""
    cmd_path = os.path.join(ALIAS_DIR, alias_name + ".cmd")

    try:
        with open(cmd_path, "w") as f:
            f.write(f'@"{python_path}" %*\n')
        return True
    except Exception as e:
        print("Eroare alias:", e)
        return False

# ------------------------------
# Interfață PyQt6
# ------------------------------

class PythonAliasManager(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Python Alias Manager – PyQt6")
        self.resize(800, 550)

        layout = QVBoxLayout(self)

        self.table = QTableWidget(0, 2)
        self.table.setHorizontalHeaderLabels(["Path Python", "Alias (editabil)"])
        self.table.horizontalHeader().setStretchLastSection(True)
        layout.addWidget(self.table)

        btn_detect = QPushButton("Detectează instalări (where python)")
        btn_detect.clicked.connect(self.detect_where)
        layout.addWidget(btn_detect)

        btn_add_folder = QPushButton("Adaugă folder custom (recursiv)")
        btn_add_folder.clicked.connect(self.add_folder)
        layout.addWidget(btn_add_folder)

        btn_search_folder = QPushButton("Caută python.exe recursiv în folder selectat")
        btn_search_folder.clicked.connect(self.search_folder)
        layout.addWidget(btn_search_folder)

        btn_aliases = QPushButton("Afișează aliasuri existente")
        btn_aliases.clicked.connect(self.show_existing_aliases)
        layout.addWidget(btn_aliases)

        btn_apply = QPushButton("Aplică aliasuri în Windows")
        btn_apply.clicked.connect(self.apply_aliases)
        layout.addWidget(btn_apply)

        self.data = load_aliases()
        self.refresh_table()

    # --------------------------
    # Tabel
    # --------------------------

    def refresh_table(self):
        self.table.setRowCount(0)
        for entry in self.data:
            row = self.table.rowCount()
            self.table.insertRow(row)

            self.table.setItem(row, 0, QTableWidgetItem(entry["path"]))

            alias_item = QTableWidgetItem(entry["alias"])
            alias_item.setFlags(alias_item.flags() | Qt.ItemFlag.ItemIsEditable)
            self.table.setItem(row, 1, alias_item)

    # --------------------------
    # Detectare instalări
    # --------------------------

    def detect_where(self):
        paths = detect_where_python()
        for p in paths:
            self.add_entry(p)
        self.refresh_table()
        save_aliases(self.data)

    def add_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "Selectează folder Python")
        if folder:
            paths = search_python_in_folder_recursive(folder)
            for p in paths:
                self.add_entry(p)
            self.refresh_table()
            save_aliases(self.data)

    def search_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "Selectează folder pentru scanare")
        if folder:
            paths = search_python_in_folder_recursive(folder)
            for p in paths:
                self.add_entry(p)
            self.refresh_table()
            save_aliases(self.data)

    # --------------------------
    # Aliasuri existente
    # --------------------------

    def show_existing_aliases(self):
        aliases = detect_existing_aliases()
        if not aliases:
            QMessageBox.information(self, "Aliasuri", "Nu există aliasuri în WindowsApps.")
            return

        msg = "\n".join([f"{a} → {p}" for a, p in aliases.items()])
        QMessageBox.information(self, "Aliasuri existente", msg)

    # --------------------------
    # Adăugare în listă
    # --------------------------

    def add_entry(self, path):
        if not any(e["path"] == path for e in self.data):
            alias = "python_" + os.path.basename(os.path.dirname(path)).replace(".", "_")
            self.data.append({"path": path, "alias": alias})

    # --------------------------
    # Aplicare aliasuri
    # --------------------------

    def apply_aliases(self):
        # actualizează aliasurile din tabel
        for row in range(self.table.rowCount()):
            self.data[row]["path"] = self.table.item(row, 0).text()
            self.data[row]["alias"] = self.table.item(row, 1).text()

        save_aliases(self.data)

        ok = 0
        for entry in self.data:
            if create_alias(entry["alias"], entry["path"]):
                ok += 1

        QMessageBox.information(
            self,
            "Aliasuri aplicate",
            f"Aliasuri create: {ok}\nAcum poți folosi comenzile în CMD/PowerShell."
        )

# ------------------------------
# Main
# ------------------------------

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

News : the new python 3.15.0a8.

The Python programming language expected the new version 3.15.0a8 to come with this changelog and release date: XXXX-XX-XX.
Major features and changes
1. Interpreter and JIT optimizations
  • Much improved JIT with optimizations for int, float, list, and tuple operations.
  • Reduces redundant reference counting and adds new specializations (for example for enum.Enum and concatenations).
  • Supports unwinding JIT frames in GDB and GNU backtrace, and sets frame pointers for better profiling.
  • Free-threading improvements remove bottlenecks in sys.intern(), PyObject_SetAttr(), PyMutex, and PyDict_Watch(), improving multi-thread scaling.
  • Garbage collector returns to generational GC as the default and exposes new APIs for external GC monitoring.
  • Runtime optimizations reduce stack usage and fix O(N²) behavior in constant folding, and speed up yield from, bytes.replace(), and memoryview.cast().
2. Security hardening
  • Updates bundled cryptography libraries (for example newer OpenSSL versions in some builds).
  • Fixes path traversal issues in shutil.unpack_archive().
  • Hardens remote debugging related code paths.
  • Addresses crashes and recursion issues in xml.parsers.expat.
  • Adds stricter validation in http.cookies, webbrowser, configparser, and related modules.
  • Introduces tighter limits for TOML keys and canonical Base64 handling.
3. Standard library enhancements
  • Command-line tools like argparse, pdb, regrtest, pickletools, tokenize, calendar, timeit, and http.server gain extended colorized output.
  • The inspect command-line interface is significantly improved.
  • timeit adds a --target-time option for more predictable benchmarking.
  • Typing gains support for features such as disjoint base decorators and richer TypeVarTuple options (bound, covariant, contravariant).
  • ForwardRef representations become clearer and easier to read.
  • Regular expression APIs move toward re.prefixmatch() instead of re.match().
  • array and memoryview support complex number formats like Zf and Zd.
  • frozendict is better integrated with dataclasses, plistlib, and dictionary merging.
  • UserDict.popitem() now behaves in a predictable, ordered way.
  • json adds an array_hook for more flexible decoding.
  • email, IDNA, and importlib.metadata receive fixes for Unicode handling and corrupted metadata.
  • asyncio adds TaskGroup.cancel(), improves debugging, shutdown behavior, and event loop performance.
  • The profiling.sampling module gains dump snapshots, --jsonl output, and --diff-flamegraph for visual comparisons.
4. C API and extension changes
  • Implements a unified slot system for types, simplifying extension type definitions.
  • Adds a stable ABI variant for free-threaded builds (abi3t).
  • Introduces new, safer functions for garbage collector traversal.
  • Makes PyCriticalSection part of the Stable ABI.
  • Provides hooks like PyInterpreterState_SetEvalFrameAllowSpecialization for controlling specialization behavior.
5. Build system and platforms
  • Windows builds adopt a new layout and support free-threaded configurations.
  • Updates to toolchains such as WASI SDK and modern compilers.
  • Improves performance on AArch64 and x86_64 architectures.
  • Fixes issues with PGO, Clang, and LLVM-based builds.
  • Adds options like --enable-static-libpython-for-interpreter for more flexible embedding.
6. Developer experience and ergonomics
  • More extensive colorization in command-line tools and error messages.
  • Better suggestions and messages for AttributeError and similar exceptions.
  • More robust REPL behavior and nicer pretty-printing, including support for new string forms.
  • Smarter import-related completions and diagnostics for everyday workflows.

Saturday, May 2, 2026

Python 3.10.x : Install ComfyUI tool with python and Krita.

This is a basic install of ComfyUI tool with python 3.10 version.
git clone https://github.com/comfyanonymous/ComfyUI
cd ComfyUI
python -m pip install -r requirements.txt
...
Successfully installed blake3-1.0.8 comfy-aimdo-0.3.0 comfy-kitchen-0.2.8 comfyui-embedded-docs-0.4.4 comfyui-frontend-package-1.42.15 comfyui-workflow-templates-0.9.66 comfyui-workflow-templates-core-0.3.221 comfyui-workflow-templates-media-api-0.3.73 comfyui-workflow-templates-media-image-0.3.133 comfyui-workflow-templates-media-other-0.3.187 comfyui-workflow-templates-media-video-0.3.83 glfw-2.10.0 kornia-0.8.2 kornia_rs-0.1.10 sentencepiece-0.2.1 simpleeval-1.0.7 spandrel-0.4.2 torchsde-0.2.6 trampoline-0.1.2
python main.py
You don't have all nodes for running, for example if you want to use with Krita then use this:

git clone https://github.com/comfyanonymous/ComfyUI
cd ComfyUI

echo ================================================
echo   Install all need for ComfyUI
echo ================================================
python -m pip install -r requirements.txt

echo ================================================
echo   Install nodes Krita AI Diffusion
echo ================================================
cd custom_nodes

REM --- ControlNet Preprocessors ---
if not exist comfyui_controlnet_aux (
    git clone https://github.com/Fannovel16/comfyui_controlnet_aux
)

REM --- IP-Adapter Plus ---
if not exist ComfyUI_IPAdapter_plus (
    git clone https://github.com/cubiq/ComfyUI_IPAdapter_plus
)

REM --- Tooling Nodes ---
if not exist comfyui-tooling-nodes (
    git clone https://github.com/Acly/comfyui-tooling-nodes
)

REM --- Inpaint Nodes ---
if not exist comfyui-inpaint-nodes (
    git clone https://github.com/Acly/comfyui-inpaint-nodes
)

cd ..

echo ================================================
echo   Install nodes
echo ================================================

REM --- ControlNet Aux ---
if exist custom_nodes\comfyui_controlnet_aux\requirements.txt (
    python -m pip install -r custom_nodes\comfyui_controlnet_aux\requirements.txt
)

REM --- IPAdapter Plus ---
if exist custom_nodes\ComfyUI_IPAdapter_plus\requirements.txt (
    python -m pip install -r custom_nodes\ComfyUI_IPAdapter_plus\requirements.txt
)

REM --- Tooling Nodes ---
if exist custom_nodes\comfyui-tooling-nodes\requirements.txt (
    python -m pip install -r custom_nodes\comfyui-tooling-nodes\requirements.txt
)

REM --- Inpaint Nodes ---
if exist custom_nodes\comfyui-inpaint-nodes\requirements.txt (
    python -m pip install -r custom_nodes\comfyui-inpaint-nodes\requirements.txt
)

echo ================================================
echo   Install packages
echo ================================================
python -m pip install blake3 comfy-aimdo comfy-kitchen comfyui-embedded-docs ^
comfyui-frontend-package comfyui-workflow-templates ^
comfyui-workflow-templates-core comfyui-workflow-templates-media-api ^
comfyui-workflow-templates-media-image comfyui-workflow-templates-media-other ^
comfyui-workflow-templates-media-video glfw kornia kornia_rs sentencepiece ^
simpleeval spandrel torchsde trampoline

echo ================================================
echo   Start ComfyUI
echo ================================================
python main.py

Thursday, April 23, 2026

Tuesday, April 21, 2026

Python Qt : sprites tool idea with ffmpeg.

This is a simple tool created in a minute with artificial intelligence to help me create sprites with a blend effect based on a map for ffmpeg. I used Python version 3.13.0, pyqt6, pygame, ...

Monday, April 20, 2026

Python 3.12.13 : Colab example with Ipywidgets.

Ipywidgets are interactive HTML widgets for Jupyter notebooks and the IPython kernel that allow users to create GUIs (sliders, text boxes, checkboxes) to visualize and control data in real time. They enable dynamic data exploration and parameter tuning, with over 30 built-in, customizable controls for building interactive dashboards directly in Python code.
You can see on my GitHub Colab repo.

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())

News : Major new features of the 3.15 series, compared to 3.14

Python 3.15 is still in development. This release, 3.15.0a7, is the seventh of eight planned alpha releases.
...The JIT compiler has been significantly upgraded, with 3-4% geometric mean performance improvement on x86-64 Linux over the standard interpreter, and 7-8% speedup on AArch64 macOS over the tail-calling interpreter

Wednesday, March 11, 2026

Saturday, March 7, 2026

Python 3.13.0 : schemdraw high-quality electrical circuit schematic.

Schemdraw is a Python package for producing high-quality electrical circuit schematic diagrams. Circuit elements are added, one at a time, similar to how you might draw them by hand, using Python methods.
This is the install step with pip tool:
pip install schemdraw
WARNING: Ignoring invalid distribution ~adquery-ocp (C:\Python313_64bit\Lib\site-packages)
Collecting schemdraw
  Downloading schemdraw-0.22-py3-none-any.whl.metadata (2.2 kB)
Downloading schemdraw-0.22-py3-none-any.whl (148 kB)
WARNING: Ignoring invalid distribution ~adquery-ocp (C:\Python313_64bit\Lib\site-packages)
Installing collected packages: schemdraw
WARNING: Ignoring invalid distribution ~adquery-ocp (C:\Python313_64bit\Lib\site-packages)
Successfully installed schemdraw-0.22

Tuesday, March 3, 2026

Python 3.13.0 : webcam Falticeni - processing HLS .m3u8 video stream.

Python can process data from an HLS (.m3u8) video stream, such as the one used on the Fălticeni webcam page (which loads the stream through index.m3u8).

Monday, March 2, 2026

Python 3.13.0 : the manim python module - part 002.

The last tutorial was on this post.
The manim packege can be install with pip tool:
pip install manim
...
Successfully installed audioop-lts-0.2.2 av-16.1.0 cloup-3.0.8 glcontext-3.0.0 isosurfaces-0.1.2 manim-0.20.1 manimpango-0.6.1 mapbox-earcut-2.0.0 moderngl-5.12.0 moderngl-window-3.1.1 pycairo-1.29.0 pydub-0.25.1 pyglet-2.1.13 pyglm-2.8.3 screeninfo-0.8.1 skia-pathops-0.9.2 srt-3.5.3
The last tutorial was on this post.

Saturday, February 28, 2026

Python 3.13.0 : simple script for update all python packages.

Simple script for update all python packages:
import subprocess
import sys
import datetime

log_file = "upgrade_log.txt"

def log(msg):
    with open(log_file, "a", encoding="utf-8") as f:
        f.write(msg + "\n")
    print(msg)

def get_installed_packages():
    result = subprocess.run(
        [sys.executable, "-m", "pip", "list", "--format=freeze"],
        capture_output=True,
        text=True
    )
    lines = result.stdout.strip().split("\n")
    packages = []
    for line in lines:
        if "@" in line:  # skip direct URL installs
            pkg = line.split("@")[0]
        else:
            pkg = line.split("==")[0]
        packages.append(pkg)
    return sorted(packages)

def upgrade_package(package):
    log(f"\n=== Updating: {package} ===")
    try:
        subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", package])
        log(f"[OK] Updated: {package}")
    except subprocess.CalledProcessError:
        log(f"[FAILED] Could not update: {package}")

def main():
    log(f"\n--- Upgrade started at {datetime.datetime.now()} ---\n")

    packages = get_installed_packages()
    log(f"Found {len(packages)} packages.\n")

    for pkg in packages:
        upgrade_package(pkg)

    log(f"\n--- Upgrade finished at {datetime.datetime.now()} ---\n")

if __name__ == "__main__":
    main()

Wednesday, February 25, 2026

Python 3.13.0 : build123d the BREP for 2D and 3D CAD.

build123d is a Python-based, parametric boundary representation (BREP) modeling framework for 2D and 3D CAD. Built on the Open Cascade geometric kernel, it provides a clean, fully Pythonic interface for creating precise models suitable for 3D printing, CNC machining, laser cutting, and other manufacturing processes.
You can find more about this project on the GitHub repo.
python -m pip install build123d
Collecting build123d
  Downloading build123d-0.10.0-py3-none-any.whl.metadata (6.7 kB)
...
Successfully installed anytree-2.13.0 asttokens-3.0.1 build123d-0.10.0 cadquery-ocp-7.8.1.1.post1 cadquery_ocp_proxy-7.9.3.1 cadquery_vtk-9.3.1 decorator-5.2.1 executing-2.2.1 ezdxf-1.4.3 ipython-9.10.0 ipython-pygments-lexers-1.1.1 lib3mf-2.5.0 matplotlib-inline-0.2.1 ocp_gordon-0.2.0 ocpsvg-0.5.0 pure-eval-0.2.3 stack_data-0.6.3 svgelements-1.9.6 svgpathtools-1.7.2 traitlets-5.14.3 trianglesolver-1.2 webcolors-24.8.0

Sunday, February 15, 2026

News : PySimpleGUI planned shutdown

The PySimpleGUI project’s planned shutdown was first announced in February 2025. At that time, we committed to supporting our Commercial customers through the end of 2025. PySimpleGUI 5 remained a very stable product throughout the year, with no significant issues reported.
Now that we’ve reached the end of that support period, the project is entering its final stage. During January 2026, the PySimpleGUI website, documentation, and PyPI servers will be taken offline as we officially close the project.
The private PyPI server will be shut down in January 2026 . If you currently install PySimpleGUI 5 using pip, you’ll need to switch to installing from a local wheel file instead.

Tools : Pickcode online I.D.E.

Pickcode is an online IDE, similar to Trinket. With a free account, you can create unlimited projects in Python, Java, and HTML/CSS/JS.
With our paid Pickcode Classroom plan, teachers can create lessons, set up classes and assignments, and view student work in real time.
Pickcode Classroom costs $500/teacher plus $10/student per year. We are considering options for adding a lower-priced plan similar to Trinket’s Code+, and will have more info about that later this year.

News : Trinket will be shutting down in early August 2026.

We are deeply grateful for your support over the years. Trinket has been used by millions of learners and educators, and it has been an honor to be part of your coding journey.
We're truly sorry for any disruption this may cause. If you have questions, please reach out to us at help@trinket.io.
If you have an annual subscription, you'll continue to have full access until the end of your current paid period. Your subscription will not renew.
Monthly subscriptions will not renew after June 30, 2026.
Trinket will be shutting down in early August 2026.
After this date, trinket.io will no longer be available, and you will not be able to access your trinkets, courses, or any content on this site. Please download anything you want to keep before the shutdown.

Saturday, February 14, 2026

Python Qt6 : using the py3Dmol .

NOTE : hacker provider ... :
Simple example with the py3Dmol:
import os
import sys

from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtCore import QUrl
import py3Dmol

SEROTONIN_SMILES = "C1=CC2=C(C=C1)C(=CN2)CCN"

def generate_html(smiles):
    view = py3Dmol.view(width=800, height=600)
    view.addModel(smiles, "smiles")
    view.setStyle({"stick": {}})
    view.zoomTo()
    return view._make_html()

class MoleculeViewer(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Structura 3D – Serotonină")

        layout = QVBoxLayout(self)
        self.browser = QWebEngineView()

        html = generate_html(SEROTONIN_SMILES)

        # IMPORTANT: QUrl, nu string!
        self.browser.setHtml(html, baseUrl=QUrl("https://localhost/"))

        layout.addWidget(self.browser)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MoleculeViewer()
    window.resize(900, 700)
    window.show()
    sys.exit(app.exec())