Python tutorials with source code, examples, guides, and tips and tricks for Windows and Linux development.
Pages
▼
Thursday, July 9, 2026
Wednesday, July 8, 2026
Python Qt : Script for Game Engine projects.
Simple python example script for Godot Engine projects.
This script allows you to see how big is the project, size of media files, size of scripting files, lines of source code and more:
import sys
import os
import pathlib
import datetime
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout,
QPushButton, QTextEdit, QProgressBar, QFileDialog, QMessageBox
)
from PyQt6.QtCore import Qt, QThread, pyqtSignal
def human_size(num_bytes: int) -> str:
units = ["B", "KB", "MB", "GB", "TB"]
size = float(num_bytes)
for unit in units:
if size < 1024.0:
return f"{size:.2f} {unit}"
size /= 1024.0
return f"{size:.2f} PB"
class FolderStatsWorker(QThread):
progress = pyqtSignal(int)
finished = pyqtSignal(str)
def __init__(self, folder_path: str):
super().__init__()
self.folder_path = folder_path
def run(self):
base = pathlib.Path(self.folder_path)
all_files = []
for root, dirs, files in os.walk(base):
for name in files:
all_files.append(pathlib.Path(root) / name)
total_files = len(all_files)
gd_files = []
media_files = []
total_lines_gd = 0
media_exts = {
".png", ".jpg", ".jpeg", ".bmp", ".gif", ".webp",
".ogg", ".wav", ".mp3", ".flac",
".mp4", ".mkv", ".avi", ".webm",
".md"
}
total_size = 0
gd_size = 0
media_size = 0
tree_lines = []
# Build tree view
for root, dirs, files in os.walk(base):
rel_root = pathlib.Path(root).relative_to(base)
indent_level = len(rel_root.parts)
indent = " " * indent_level
tree_lines.append(f"{indent}{rel_root if rel_root.parts else base.name}/")
for name in files:
tree_lines.append(f"{indent} {name}")
for idx, f in enumerate(all_files):
try:
stat = f.stat()
total_size += stat.st_size
ext = f.suffix.lower()
if ext == ".gd":
gd_files.append(f)
gd_size += stat.st_size
try:
txt = f.read_text(encoding="utf-8", errors="ignore")
if txt:
total_lines_gd += txt.count("\n") + 1
except Exception:
pass
if ext in media_exts:
media_files.append(f)
media_size += stat.st_size
except Exception:
pass
if total_files > 0:
self.progress.emit(int((idx + 1) * 100 / total_files))
report_lines = []
report_lines.append(f"Folder: {self.folder_path}")
report_lines.append("")
report_lines.append("=== Summary ===")
report_lines.append(f"Total files: {total_files}")
report_lines.append(f"Total .gd files: {len(gd_files)}")
report_lines.append(f"Total media files (incl. .md): {len(media_files)}")
report_lines.append(f"Total lines in .gd files: {total_lines_gd}")
report_lines.append("")
report_lines.append(f"Total size: {human_size(total_size)}")
report_lines.append(f"Total .gd size: {human_size(gd_size)}")
report_lines.append(f"Total media size: {human_size(media_size)}")
report_lines.append("")
report_lines.append("=== Tree ===")
report_lines.extend(tree_lines)
report_text = "\n".join(report_lines)
# Save report
now = datetime.datetime.now()
ts = now.strftime("%Y%m%d_%H%M%S")
folder_name = base.name
report_name = f"{folder_name}_{ts}.txt"
report_path = base / report_name
try:
report_path.write_text(report_text, encoding="utf-8")
except Exception:
pass
self.finished.emit(report_text)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Godot Project Stats")
self.setFixedSize(640, 480)
self.folder_path = None
self.worker = None
central = QWidget()
layout = QVBoxLayout(central)
self.btn_open = QPushButton("Open Folder")
self.btn_process = QPushButton("Process")
self.progress = QProgressBar()
self.progress.setRange(0, 100)
self.text = QTextEdit()
self.text.setReadOnly(True)
layout.addWidget(self.btn_open)
layout.addWidget(self.btn_process)
layout.addWidget(self.progress)
layout.addWidget(self.text)
self.setCentralWidget(central)
self.btn_open.clicked.connect(self.open_folder)
self.btn_process.clicked.connect(self.start_processing)
def open_folder(self):
folder = QFileDialog.getExistingDirectory(self, "Select Godot Project Folder")
if folder:
self.folder_path = folder
self.text.clear()
self.text.append(f"Selected folder:\n{folder}")
def start_processing(self):
if not self.folder_path:
QMessageBox.warning(self, "No folder", "Please select a folder first.")
return
if self.worker is not None and self.worker.isRunning():
QMessageBox.information(self, "Processing", "Already processing.")
return
self.progress.setValue(0)
self.text.append("\nProcessing...")
self.worker = FolderStatsWorker(self.folder_path)
self.worker.progress.connect(self.progress.setValue)
self.worker.finished.connect(self.on_finished)
self.worker.start()
def on_finished(self, report_text: str):
self.text.clear()
self.text.append(report_text)
self.progress.setValue(100)
def main():
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()Tuesday, July 7, 2026
Python Qt : simple tool to convert any source code to html area development.
Today. I will show this simple idea to convert any source code for html tag code with PyQt6.
The source code of this tool is not very big.I don't share because can be simple or you can customize as you want starting with any artificial intelligence and is more easy for you.
I used this on my blogger area, let's see the result for a simple example source code Google Apps Script from Gemini AI to html source code with div , pre and code html tags included in the result:

Python Qt : simple tool for audio dialogue in game development
Today, this script tool is a small desktop tool that lets you visually synchronize spoken audio with written dialogue. You load an audio file and the program generates a waveform so you can click or drag to select exact time ranges. At the same time, you select the matching text, and the tool creates timestamped dialogue segments.
Each segment includes a dialog ID, start time, end time, and the associated text. Segments appear in a list, can be played individually, deleted, or tested in a separate window where each dialog ID becomes a playback button. This makes it easy to verify timing and structure.
The entire project—audio path, full text, and all segments—can be saved or loaded as a JSON file. Older JSON formats containing only segments are automatically converted. The final JSON is ready for use in game engines like Godot or Unity for precise voice‑over playback.
I used artificial intelligence to fix some issues, this is the result:

Monday, July 6, 2026
Tools : TraceFlow artificial intelligence for python tasks.
This tool with artificial intelligence can help with python programming tasks.
TraceFlow captures crashes, traces every variable, and streams an AI-powered fix — all inside VSCode. No context switching. No guessing.
Perfect for trying out TraceFlow on personal projects.
- 5 analysis per day
- Crash detection & variable tracing
- AI root cause explanation
- One-click fix apply
- Call stack inspection
- Module & function level analysis
See the official website.
Sunday, July 5, 2026
Python 3.10.11 : python library-skills for your artificial intelligence.
The Python package library-skills is a lightweight command‑line toolkit designed to help developers build, test, and validate modular AI “skills.” These skills are small, self‑contained units of logic that can be executed independently or integrated into larger AI agents. The package focuses on simplicity, portability, and clear structure, making it useful for developers who want to experiment with tool‑calling systems or create custom capabilities for AI workflows.
A skill typically consists of a JSON descriptor and a Python function. The JSON file defines the skill’s name, description, and input schema, while the Python file contains the actual execution logic. This separation ensures that skills remain easy to document, validate, and reuse across different projects. With library-skills, developers can quickly inspect a skill’s schema, run it with custom input, or verify that its output matches the expected structure.
The command‑line interface provided by the package allows users to list installed skills, execute them directly, and validate input files without writing additional code. This makes the development cycle faster and more predictable. Instead of manually wiring functions together, developers can rely on a consistent interface that handles loading, parsing, and execution.
One of the main advantages of library-skills is its role in AI agent development. Modern agents often rely on tool‑calling, where the AI selects and triggers external functions based on user intent. Skills created with this package can be easily integrated into such agents, providing clear schemas and predictable behavior. This helps ensure that AI systems remain reliable, debuggable, and easy to extend.
Overall, library-skills is a practical utility for anyone building structured AI tools. It encourages clean design, modularity, and transparency, making it a valuable addition to Python environments focused on AI experimentation and agent development.
Let's install this python package.
python -m pip install library-skills
Collecting library-skills
Downloading library_skills-0.0.19-py3-none-any.whl.metadata (5.4 kB)
...
Successfully installed library-skills-0.0.19 rich-toolkit-0.20.1 tomli-2.4.1Let's make the first run:
library-skills.exe
context
Project root c:\lucru\PythonProjects
Target Python environment not found
Warning: No target Python environment with site-packages or node_modules was found. Run from a
project root after installing dependencies, for example with 'uv sync' for Python or 'npm install' for
Node.js.
No installed or discovered skills found.
Copy the Library Skills tool skill into the project so agents know how to update, repair, and check
managed skills?
■ Copy Library Skills tool skill into the project so agents know how to update, repair, and check
managed skills? Copy Library Skills tool skill
Target Status Path
universal tool skill: missing .agents\skills\library-skills
Copied: library-skills (universal) -> .agents\skills\library-skillsNow you can create skills for your artificial intelligence:
Create a folder with two files:
my_skill/
skill.json
skill.py
First file named skill.json:
{
"name": "hello_skill",
"description": "Returnează un mesaj simplu",
"input_schema": {
"type": "object",
"properties": {
"name": {"type": "string"}
},
"required": ["name"]
}
}
The second file named skill.py
def run(input):
name = input["name"]
return {"message": f"Salut, {name}!"}
Run the skill into your folder:
library-skills run my_skill --input '{"name": "Catalin"}'See the schema:
library-skills schema my_skillValidate the input:
library-skills validate my_skill input.jsonList the skills:
library-skills listThis is all you need for a default basic skill with the library-skills.
Python 3.10.11 : CVSS (Common Vulnerability Scoring System) with cvss python package.
This Python package contains CVSS v2, v3 and v4 computation utilities and interactive calculator (for v2 and v3 only) compatible with Python 3. CVSS (Common Vulnerability Scoring System) is an standardized method for rating the severity of security issues on a scale from 0 (no impact) to 10 (critical).
See the pypi.org - cvss website.
Let's install the cvss python package.
python -m pip install cvss
Collecting cvss
Downloading cvss-3.6-py2.py3-none-any.whl.metadata (3.8 kB)
Downloading cvss-3.6-py2.py3-none-any.whl (31 kB)
Installing collected packages: cvss
WARNING: The script cvss_calculator.exe is installed in 'C:\python-3_10_11\Scripts' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed cvss-3.6How this works:
NVD Database (online)
|
| JSON feed
v
Python script ----> parses CVE + CVSS vector
|
| uses cvss library
v
Scores vulnerabilities (Base, Temporal, Environmental)
|
| inserts results
v
Your local database (SQL)
|
v
Dashboard / API / Alerts Simple code source example :
#!/usr/bin/env python3
# Demonstrates how to score a CVSS vector using the open-source "cvss" library.
# Validation and error handling included.
from cvss import CVSS3 # CVSS2, CVSS3, CVSS4 are available
import sys
def score_cvss_vector(vector: str):
"""
Validates and scores a CVSS3 vector string.
Returns scores and severities.
"""
if not isinstance(vector, str) or not vector.strip():
raise ValueError("Vector must be a non-empty string.")
try:
c = CVSS3(vector)
except Exception as e:
raise ValueError(f"Invalid CVSS3 vector: {e}")
return c.clean_vector(), c.scores(), c.severities()
def main():
if len(sys.argv) != 2:
print("Usage: python cvss_score.py '<CVSS3_VECTOR>'")
print("Example:")
print("python main.py 'CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H'")
sys.exit(1)
vector = sys.argv[1]
try:
clean_v, scores, severity = score_cvss_vector(vector)
print("Input vector:", vector)
print("Normalized vector:", clean_v)
print("Scores:", scores)
print("Severity:", severity)
except ValueError as e:
print("Error:", e)
sys.exit(1)
if __name__ == "__main__":
main()The result is this:
python main.py CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Input vector: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Normalized vector: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Scores: (9.8, 9.8, 9.8)
Severity: ('Critical', 'Critical', 'Critical')Another source code with examples:
#!/usr/bin/env python3
# Requires: pip install cvss
from cvss import CVSS3
# Example vulnerabilities (safe, educational)
vulns = [
{
"language": "Python",
"title": "Unsafe eval usage",
"description": "Code that executes user-provided input using eval().",
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N"
},
{
"language": "C#",
"title": "Insecure deserialization",
"description": "BinaryFormatter deserialization of untrusted data.",
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
},
{
"language": "Godot Engine",
"title": "Unvalidated file path access",
"description": "Loading files from paths provided by the user without validation.",
"cvss_vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N"
}
]
def analyze_vulnerabilities(vuln_list):
for v in vuln_list:
print("\n====================================")
print("Language:", v["language"])
print("Issue:", v["title"])
print("Description:", v["description"])
print("CVSS Vector:", v["cvss_vector"])
try:
cv = CVSS3(v["cvss_vector"])
base, temp, env = cv.scores()
sev_base, sev_temp, sev_env = cv.severities()
print("Base Score:", base, "-", sev_base)
print("Temporal Score:", temp, "-", sev_temp)
print("Environmental Score:", env, "-", sev_env)
except Exception as e:
print("Invalid CVSS vector:", e)
if __name__ == "__main__":
analyze_vulnerabilities(vulns)This is the result:
python main_002.py
====================================
Language: Python
Issue: Unsafe eval usage
Description: Code that executes user-provided input using eval().
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N
Base Score: 8.1 - High
Temporal Score: 8.1 - High
Environmental Score: 8.1 - High
====================================
Language: C#
Issue: Insecure deserialization
Description: BinaryFormatter deserialization of untrusted data.
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Base Score: 9.8 - Critical
Temporal Score: 9.8 - Critical
Environmental Score: 9.8 - Critical
====================================
Language: Godot Engine
Issue: Unvalidated file path access
Description: Loading files from paths provided by the user without validation.
CVSS Vector: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
Base Score: 4.4 - Medium
Temporal Score: 4.4 - Medium
Environmental Score: 4.4 - MediumPython 3.10.11 : Show the CVE's results with opencve token.
Today, this simple source code use token from opencve.io - website to show CVE's results.
import requests
API_URL = "https://app.opencve.io/api/cve"
TOKEN = "opc_org.<token_id>.<secret>"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Accept": "application/json",
}
params = {
"vendor": "microsoft",
"cvss": "critical",
"page": 1,
}
resp = requests.get(API_URL, headers=headers, params=params)
data = resp.json()
for cve in data["results"]:
print(cve["cve_id"], cve["description"])
This is the result:
python main_001.py
CVE-2026-58289 Access of resource using incompatible type ('type confusion') in Microsoft Edge (Chromium-based) allows an unauthorized attacker to execute code over a network.
CVE-2026-45499 Server-side request forgery (ssrf) in Azure OpenAI allows an authorized attacker to elevate privileges over a network.
CVE-2026-41106 Url redirection to untrusted site ('open redirect') in M365 Copilot allows an unauthorized attacker to elevate privileges over a network.
CVE-2026-57100 Server-side request forgery (ssrf) in Microsoft Entra Provisioning Service (SyncFabric) allows an authorized attacker to elevate privileges over a network.
CVE-2026-54130 Missing authentication for critical function in M365 Copilot allows an unauthorized attacker to disclose information over a network.
CVE-2026-48584 Execution with unnecessary privileges in Azure Synapse allows an authorized attacker to elevate privileges over a network.
CVE-2026-45480 Improper authentication in Azure Active Directory allows an unauthorized attacker to elevate privileges over a network.
CVE-2025-62821 Microsoft HEIF Image Extensions 1.2.22.0 has an out-of-bounds read because CHEIFItemInfoEntry_GetDataSize can return success while leaving the reported data size as 0. This causes a caller to make a 1-byte allocation. Later, CopyPixels computes copy_size = stride * abs(roi_height) but does not check the source buffer length before a memmove call.
CVE-2026-47647 Improper access control in Microsoft Dynamics 365 allows an authorized attacker to elevate privileges over a network.
CVE-2026-48582 Missing authorization in Microsoft Exchange Online allows an authorized attacker to elevate privileges over a network.Saturday, July 4, 2026
Python Qt : simple uninstall and reinstall all packages.
This Python program creates a small desktop application using PyQt6. Its purpose is to show all Python packages installed in your environment and then uninstall and reinstall them. The interface has a button to load the list of installed packages, a list widget to display them, a button to start the reinstall process, and a progress bar to show how far the process has gone.
When you press Load Installed Packages, the program runs the command pip list --format=freeze and reads all installed packages. It extracts only the package names and displays them in the GUI list.
When you press Uninstall + Reinstall All, the program goes through each package one by one. For every package, it runs pip uninstall -y package to remove it, and then pip install package to install it again. The progress bar updates after each package so you can see the progress.
The important detail is that the program does not use threads. All operations run in the main GUI thread. Because of this, the window may freeze or stop responding while uninstalling and reinstalling packages. This is normal behavior for PyQt6 applications that perform long tasks without threading. Let's see the source code:
import sys
import subprocess
from PyQt6.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QPushButton,
QListWidget, QProgressBar, QMessageBox
)
from PyQt6.QtCore import Qt
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")
pkgs = [line.split("==")[0] for line in lines if "==" in line]
return pkgs
def uninstall_package(pkg):
subprocess.run([sys.executable, "-m", "pip", "uninstall", "-y", pkg])
def install_package(pkg):
subprocess.run([sys.executable, "-m", "pip", "install", pkg])
class PipReinstaller(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Reinstall All Python Packages")
layout = QVBoxLayout()
self.btn_load = QPushButton("Load Installed Packages")
self.btn_load.clicked.connect(self.load_packages)
layout.addWidget(self.btn_load)
self.list = QListWidget()
layout.addWidget(self.list)
self.btn_reinstall = QPushButton("Uninstall + Reinstall All")
self.btn_reinstall.clicked.connect(self.reinstall_all)
layout.addWidget(self.btn_reinstall)
self.progress = QProgressBar()
layout.addWidget(self.progress)
self.setLayout(layout)
def load_packages(self):
self.list.clear()
pkgs = get_installed_packages()
for p in pkgs:
self.list.addItem(p)
def reinstall_all(self):
pkgs = [self.list.item(i).text() for i in range(self.list.count())]
if not pkgs:
QMessageBox.warning(self, "Warning", "No packages loaded.")
return
total = len(pkgs)
self.progress.setValue(0)
for idx, pkg in enumerate(pkgs):
uninstall_package(pkg)
install_package(pkg)
percent = int((idx + 1) / total * 100)
self.progress.setValue(percent)
QMessageBox.information(self, "Done", "All packages reinstalled.")
if __name__ == "__main__":
app = QApplication(sys.argv)
win = PipReinstaller()
win.show()
sys.exit(app.exec())Wednesday, July 1, 2026
News : FastAPI new changes , now supports router dependencies ...
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python based on standard Python type hints.
FastAPI's router.frontend() now also supports router dependencies (e.g. add cookie auth to a router, and a frontend at /admin).
Handle the cookie auth in FastAPI, and let the frontend do its client-side routing (e.g. TanStack Router with React).
See the official website.
Python 3.10.11 : fix python store bad open with powershell.
Today, this powershell source code will fix the python command when start the windows store.
This is the powershell source code:
# Run first
# Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# --- Create Form ---
$form = New-Object System.Windows.Forms.Form
$form.Text = "Python Path Fixer"
$form.Size = New-Object System.Drawing.Size(500, 300)
$form.StartPosition = 'CenterScreen'
# --- Label & Path Input ---
$label = New-Object System.Windows.Forms.Label
$label.Text = "Python Directory:"
$label.Location = New-Object System.Drawing.Point(10, 20)
$label.AutoSize = $true
$form.Controls.Add($label)
$txtPath = New-Object System.Windows.Forms.TextBox
$txtPath.Location = New-Object System.Drawing.Point(10, 45)
$txtPath.Size = New-Object System.Drawing.Size(460, 20)
$txtPath.Text = "C:\PythonInstall"
$form.Controls.Add($txtPath)
# --- Log Box ---
$txtLog = New-Object System.Windows.Forms.TextBox
$txtLog.Multiline = $true
$txtLog.ScrollBars = 'Vertical'
$txtLog.Location = New-Object System.Drawing.Point(10, 80)
$txtLog.Size = New-Object System.Drawing.Size(460, 130)
$txtLog.ReadOnly = $true
$form.Controls.Add($txtLog)
# --- Fix Button ---
$btnFix = New-Object System.Windows.Forms.Button
$btnFix.Text = "Check and Fix Path"
$btnFix.Location = New-Object System.Drawing.Point(10, 220)
$btnFix.Size = New-Object System.Drawing.Size(460, 30)
$btnFix.Add_Click({
$pythonFolder = $txtPath.Text
$pythonExe = Join-Path $pythonFolder "python.exe"
$txtLog.Text = "Checking: $pythonExe`r`n"
if (Test-Path $pythonExe) {
$txtLog.AppendText("[+] Python found. Updating PATH...`r`n")
$currentPath = [Environment]::GetEnvironmentVariable("Path", "User")
$pathParts = $currentPath -split ";" | Where-Object { $_ -ne $pythonFolder -and $_ -ne "" }
$newPath = "$pythonFolder;" + ($pathParts -join ";")
[Environment]::SetEnvironmentVariable("Path", $newPath, "User")
$txtLog.AppendText("[OK] Priority set successfully!`r`nPlease restart your terminal.")
} else {
$txtLog.AppendText("[ERROR] python.exe not found in the specified directory!")
}
})
$form.Controls.Add($btnFix)
[void]$form.ShowDialog()Friday, June 26, 2026
News : PEP 661 – Sentinel Values.
About the sentinel object you can read on the official website :
The sentinel objects should behave as expected by a sentinel object: When compared using the is operator, it should always be considered identical to itself but never to any other object. Creating a sentinel object should be a simple, straightforward one-liner. It should be simple to define as many distinct sentinel values as needed. The sentinel objects should have a clear and short repr. It should be possible to use clear type signatures for sentinels. The sentinel objects should behave correctly after copying, and sentinels should have predictable behavior when pickled and unpickled. Such sentinels should work when using CPython 3.x and PyPy3, and ideally also with other implementations of Python. As simple and straightforward as possible, in implementation and especially in use. Avoid this becoming one more special thing to learn when learning Python. It should be easy to find and use when needed, and obvious enough when reading code that one would normally not feel a need to look up its documentation
Let's see one default example:
from typing import assert_type
MISSING = sentinel('MISSING')
def foo(value: int | MISSING) -> None:
if value is MISSING:
assert_type(value, MISSING)
else:
assert_type(value, int)Thursday, June 25, 2026
News : Python 3.15.0 beta 3 is here!
Python 3.15 is still in development. This release, 3.15.0b3, is the third of four planned beta releases, containing around 195 bugfixes, build improvements and documentation changes from 86 contributors since 3.15.0b2.
Beta release previews are intended to give the wider community the opportunity to test new features and bug fixes and to prepare their projects to support the new feature release.
See the official website.
News : Django 6.1 beta 1 released ...
Django 6.1 beta 1 is now available. It represents the second stage in the 6.1 release cycle and is an opportunity to try out the changes coming in Django 6.1.
Django 6.1 offers a harmonious mélange of new features and usability improvements, which you can read about in the in-development 6.1 release notes.
Tuesday, June 16, 2026
Python Qt : Network intrusion detection tool with alerts and real‑time HTTP attack detection.
This Python script acts as a local security monitor that watches incoming HTTP requests and alerts the user when suspicious activity is detected. It runs a lightweight HTTP server and inspects every GET and POST request for signs of intrusion, such as dangerous protocols (gopher://), command‑execution keywords (bash, powershell, cmd.exe), encoded payloads, or other attack patterns. Before scanning the request, the script verifies that the incoming connection comes from a specific trusted IP and MAC address. This prevents unauthorized devices from interacting with the server. If the IP or MAC does not match the configured values, the request is immediately rejected.
When a malicious pattern is detected, the script logs the event and displays a popup alert window on the user’s desktop. This popup is implemented using PyQt6 and appears as a small, always‑on‑top notification in the corner of the screen. It fades out automatically after a few seconds, ensuring the user is visually warned without relying on Windows system notifications.
The script also includes a tray icon with a menu that allows the user to update the trusted IP and MAC address. All events are written to a log file for later review. Overall, it functions as a simple intrusion‑detection and alerting tool for local network traffic.
This is a groper test on my browser and result was:

Let's see the source code:
import re
import threading
import queue
import subprocess
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
from datetime import datetime
import sys
from urllib.parse import unquote
# FIX pentru Windows 10/11 – AppID
import ctypes
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("Python.SecurityMonitor")
from PyQt6.QtWidgets import (
QApplication, QSystemTrayIcon, QMenu, QDialog,
QVBoxLayout, QLabel, QLineEdit, QPushButton, QWidget
)
from PyQt6.QtGui import QIcon
from PyQt6.QtCore import QTimer, Qt, QPropertyAnimation
# ---------------- CONFIG ---------------- #
ALERT_QUEUE = queue.Queue()
LOG_FILE = "security_log.txt"
MONITOR_IP = "192.168.0.136"
MONITOR_MAC = "60:45:cb:c3:4d:02" # normalizat
RULES = {
"Gopher protocol": r"gopher://",
"CRLF injection": r"%0d%0a",
"WinRM port": r"5985",
"Base64 payload": r"base64",
"bash execution": r"bash",
"powershell execution": r"powershell",
"cmd execution": r"cmd\.exe",
}
# ---------------------------------------- #
def log_event(text: str):
ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
line = f"[{ts}] {text}\n"
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(line)
print(line, end="")
def get_mac(ip):
try:
output = subprocess.check_output(["arp", "-a", ip], text=True)
for line in output.splitlines():
if ip in line:
mac = line.split()[1]
mac = mac.replace("-", ":").lower()
return mac
except:
return None
# ---------------- POPUP ALERT ---------------- #
class AlertPopup(QWidget):
def __init__(self, message):
super().__init__()
self.setWindowFlags(
Qt.WindowType.FramelessWindowHint |
Qt.WindowType.Tool |
Qt.WindowType.WindowStaysOnTopHint
)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
layout = QVBoxLayout()
label = QLabel(message)
label.setStyleSheet("""
background-color: #CC0000;
color: white;
padding: 12px;
border-radius: 8px;
font-size: 14px;
""")
layout.addWidget(label)
self.setLayout(layout)
self.resize(350, 80)
screen = QApplication.primaryScreen().geometry()
self.move(screen.width() - 380, screen.height() - 150)
self.show()
self.anim = QPropertyAnimation(self, b"windowOpacity")
self.anim.setDuration(6000)
self.anim.setStartValue(1.0)
self.anim.setEndValue(0.0)
self.anim.finished.connect(self.close)
self.anim.start()
# ---------------- HTTP SERVER ---------------- #
class SecurityHandler(BaseHTTPRequestHandler):
def _check_ip_mac(self):
global MONITOR_IP, MONITOR_MAC
client_ip = self.client_address[0]
# bypass MAC check dacă este același PC
if client_ip == MONITOR_IP:
return True, None
if client_ip != MONITOR_IP:
return False, f"IP neautorizat: {client_ip}"
mac = get_mac(client_ip)
if mac is None:
return False, f"MAC necunoscut pentru {client_ip}"
if mac.lower() != MONITOR_MAC.lower():
return False, f"MAC neautorizat: {mac} pentru IP {client_ip}"
return True, None
def _scan_and_block(self, source: str, data: str):
data_lower = data.lower()
for reason, pattern in RULES.items():
if re.search(pattern, data_lower):
msg = f"Blocked {source}: {reason} → {data}"
log_event(msg)
ALERT_QUEUE.put(msg)
self.send_response(403)
self.end_headers()
self.wfile.write(f"Cerere blocată: {reason}".encode("utf-8"))
return True
return False
def do_GET(self):
ok, reason = self._check_ip_mac()
if not ok:
ALERT_QUEUE.put(reason)
log_event(reason)
self.send_response(403)
self.end_headers()
self.wfile.write(reason.encode())
return
url = unquote(self.path)
if self._scan_and_block("GET URL", url):
return
self.send_response(200)
self.end_headers()
self.wfile.write(b"Cerere GET permisa")
log_event(f"Allowed GET: {url}")
def do_POST(self):
ok, reason = self._check_ip_mac()
if not ok:
ALERT_QUEUE.put(reason)
log_event(reason)
self.send_response(403)
self.end_headers()
self.wfile.write(reason.encode())
return
url = unquote(self.path)
content_length = int(self.headers.get("Content-Length", 0))
body = unquote(self.rfile.read(content_length).decode("utf-8", errors="ignore"))
if self._scan_and_block("POST URL", url):
return
if self._scan_and_block("POST body", body):
return
self.send_response(200)
self.end_headers()
self.wfile.write(b"Cerere POST permisa")
log_event(f"Allowed POST: {url} | body: {body[:200]}")
def run_server():
server = HTTPServer(("0.0.0.0", 8080), SecurityHandler)
log_event("Security server running on http://0.0.0.0:8080")
server.serve_forever()
# ---------------- DIALOG IP & MAC ---------------- #
class IpMacDialog(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("Set IP & MAC")
layout = QVBoxLayout()
self.ip_label = QLabel("IP Address:")
self.ip_input = QLineEdit()
self.ip_input.setText(MONITOR_IP)
self.mac_label = QLabel("MAC Address:")
self.mac_input = QLineEdit()
self.mac_input.setText(MONITOR_MAC)
self.save_btn = QPushButton("Save")
self.save_btn.clicked.connect(self.save_values)
layout.addWidget(self.ip_label)
layout.addWidget(self.ip_input)
layout.addWidget(self.mac_label)
layout.addWidget(self.mac_input)
layout.addWidget(self.save_btn)
self.setLayout(layout)
def save_values(self):
global MONITOR_IP, MONITOR_MAC
MONITOR_IP = self.ip_input.text().strip()
MONITOR_MAC = self.mac_input.text().strip().replace("-", ":").lower()
log_event(f"Updated IP/MAC → IP={MONITOR_IP}, MAC={MONITOR_MAC}")
self.hide()
# ---------------- TRAY APP ---------------- #
class TrayApp:
def __init__(self):
self.app = QApplication(sys.argv)
icon = QIcon("icon.svg") if os.path.exists("icon.svg") else QIcon()
self.tray = QSystemTrayIcon(icon)
self.tray.setToolTip("Python Security Monitor")
menu = QMenu()
set_ip_mac = menu.addAction("Set IP & MAC")
set_ip_mac.triggered.connect(self.open_ip_mac_dialog)
exit_action = menu.addAction("Exit")
exit_action.triggered.connect(self.app.quit)
self.tray.setContextMenu(menu)
self.tray.show()
self.dialog = None
self.timer = QTimer()
self.timer.timeout.connect(self.check_alerts)
self.timer.start(300)
def open_ip_mac_dialog(self):
if self.dialog is None:
self.dialog = IpMacDialog()
self.dialog.show()
self.dialog.raise_()
self.dialog.activateWindow()
def check_alerts(self):
while not ALERT_QUEUE.empty():
msg = ALERT_QUEUE.get()
AlertPopup(msg)
def run(self):
self.app.exec()
# ---------------- MAIN ---------------- #
def main():
server_thread = threading.Thread(target=run_server, daemon=True)
server_thread.start()
tray = TrayApp()
tray.run()
if __name__ == "__main__":
main()
Monday, June 15, 2026
News : PRO GEOMETRIC MERMAID EDITOR – PYQT6 (v1.0) sell on gumroad
Today, I implemented a new system for selling my products, cheaper and more permissive, called: BUY AND HAVE MORE - SYSTEM. The idea is to have your product, specific tool, simple and good with a low price.
I used many programming languages: Python, C#, Godot, FASM...
I used artificial intelligence, I can use testing software ...
This is my system that will help users and developers by:
- using a low price for your tools
- help work of developers with low prices, nothing can be 100% free
- maintain the same software in time: on price, lifetime, and updates
- buy more products from users and me, send me your development tasks
- based on users asking who will have these products
- do you want a specific development: send me an email with your price
- you can ask even artist's work
- don't spend your time, just use my time at a low price
The first product on this system is PRO GEOMETRIC MERMAID EDITOR – PYQT6 (v1.0) sell on gumroad.
From sales, I will reinvest in my technical development, in producing source code and producing art, according to my skills.
See this simple video tutorial with this python project script.
Saturday, June 13, 2026
Python Qt : Parse Google Finance with PyQt6 and BeautifulSoup.
This Python script is a web scraper designed to extract the real-time stock price of GoldMoney Inc. (TSE:XAU) from Google Finance using PyQt6 and BeautifulSoup. Because Google Finance often displays a cookie consent pop-up that blocks data extraction, the script initializes a headless-like browser instance using QWebEngineView. It embeds a custom, silent web page class to suppress noisy JavaScript console errors in the terminal. Once the page loads, it automatically injects and executes a JavaScript snippet to find and click the "Accept all" or "Acceptă tot" consent buttons. After a short delay to allow the content to refresh, the script captures the updated HTML source, uses BeautifulSoup to locate the specific price elements via robust CSS selectors, prints the cleaned financial data to the terminal, and cleanly terminates the application.
I used artificial intelligence from Google Finance ... REZULTAT SCRAPING (CURAT) ... strange comments:

Let's see:
from PyQt6.QtWidgets import QApplication
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWebEngineCore import QWebEnginePage
from PyQt6.QtCore import QUrl, QTimer
from bs4 import BeautifulSoup
import sys
# Clasă custom pentru pagină care ignoră mesajele de consolă
class SilentWebPage(QWebEnginePage):
def javaScriptConsoleMessage(self, level, message, lineID, sourceID):
# Returnăm nimic pentru a nu afișa mesajele de tip "js: Error..." în terminal
pass
class Scraper(QWebEngineView):
def __init__(self, url):
self.app = QApplication.instance() or QApplication(sys.argv)
super().__init__()
# Setăm pagina custom care reduce la tăcere erorile JS
self.setPage(SilentWebPage(self))
self.loadFinished.connect(self._on_load_finished)
self.setUrl(QUrl(url))
self.app.exec()
def _on_load_finished(self, ok):
if ok:
js_code = """
(function() {
let buttons = document.querySelectorAll('button');
for (let i = 0; i < buttons.length; i++) {
let text = buttons[i].innerText;
if (text.includes('Accept all') || text.includes('Acceptă tot')) {
buttons[i].click();
return true;
}
}
return false;
})();
"""
self.page().runJavaScript(js_code, self._after_consent)
else:
self.app.quit()
def _after_consent(self, clicked):
wait_time = 4000 if clicked else 1500
QTimer.singleShot(wait_time, self._extract_html)
def _extract_html(self):
self.page().toHtml(self.parse_finance_data)
def parse_finance_data(self, html):
soup = BeautifulSoup(html, 'html.parser')
try:
# Selectorul robust care a funcționat anterior
main_price = soup.find("div", {"class": "fxKbKc"})
if not main_price:
price_elements = soup.find_all(attrs={"jsname": "Pdsbrc"})
for el in price_elements:
if "$" in el.text or "CAD" in el.parent.text:
main_price = el
break
print("\n" + "="*40)
print("REZULTAT SCRAPING (CURAT)")
print("="*40)
if main_price:
print(f"Activ: GoldMoney Inc (TSE:XAU)")
print(f"Preț: {main_price.text.strip()}")
else:
print("Eroare: Nu s-a putut găsi prețul.")
print("="*40 + "\n")
except Exception as e:
print(f"Eroare: {e}")
self.app.quit()
if __name__ == "__main__":
url_target = "https://www.google.com/finance/quote/XAU:TSE"
scraper = Scraper(url_target)The output of this source code is:
python test_001.py
========================================
REZULTAT SCRAPING (CURAT)
========================================
Activ: GoldMoney Inc (TSE:XAU)
Preț: $15.81
========================================
Friday, June 12, 2026
Python Qt : Simple script to use wandb and weave.
WandB and Weave work together as complementary tools that enhance the process of evaluating, monitoring, and understanding machine‑learning and large‑language‑model behavior, each focusing on a different layer of the workflow while sharing the same ecosystem. WandB functions primarily as an experiment‑tracking platform that records metrics, logs model outputs, stores configuration details, and organizes results into interactive dashboards, making it easy to compare multiple runs, visualize performance trends, and maintain a structured history of experiments across time. It acts like a scientific notebook that automatically captures everything relevant during evaluation, from scores and prompts to timing information, enabling reproducibility and long‑term analysis. Weave complements this by focusing on the granular tracing of LLM calls, capturing each prompt, response, intermediate step, and metadata associated with model execution, which allows developers to inspect how a model arrived at a particular answer, debug unexpected behavior, and analyze qualitative aspects of model reasoning. While WandB summarizes experiments at a high level, Weave dives deep into the internals of each interaction, providing structured logs that can be searched, filtered, and compared. Together, they create a unified workflow where WandB offers experiment‑level insights and Weave provides call‑level transparency, giving developers a complete picture of model performance, reliability, and behavior across different prompts, models, or configurations, especially useful when benchmarking or refining LLMs.
Let's install these:
python -m pip install wandb weave
Collecting wandb
...
Successfully installed abnf-2.2.0 backoff-2.2.1 chardet-7.4.3 cint-1.0.0 diskcache-weave-5.6.3.post1 fickling-0.1.11
googleapis-common-protos-1.75.0 gql-4.0.0 graphql-core-3.2.11 intervaltree-3.2.1 kaitaistruct-0.11
opentelemetry-api-1.42.1 opentelemetry-exporter-otlp-proto-common-1.42.1 opentelemetry-exporter-otlp-proto-http-1.42.1
opentelemetry-proto-1.42.1 opentelemetry-sdk-1.42.1 opentelemetry-semantic-conventions-0.63b1 pdfminer.six-20260107
polyfile-weave-0.5.9 protobuf-6.33.6 sentry-sdk-2.62.0 sortedcontainers-2.4.0 wandb-0.27.2 weave-0.52.42Let's see one exemple with my custom artificial intelligence model and PyQt6.

The PyQt6 script is a small LLM evaluation application that takes two inputs: the Ollama model you select and a fixed set of short test prompts. When you start the evaluation, the script sends each prompt to the chosen model, collects the generated responses, then sends those responses to a smaller judge model to obtain a numerical quality score. All generation uses reduced context and limited output length to keep execution fast on an i3 CPU. As it runs, the script displays each answer in the text panel and updates a progress bar. When all prompts are processed, it compiles the collected scores and displays them as a bar chart in the canvas, giving you a quick visual summary of the model’s performance.
The online tool show this result for this script:

Tool : marpy the browser IDE.
You start in a browser IDE that is Python-first: virtualenvs, proper dependencies, real logs, not a toy REPL.
See the official webpage.

Python Qt : Simple script to build your own model.
Today, I test a simple script with PyQt6 python package to have a custom UI to build my own ollama artificial inteligence model.
The script is simple one, create one user interface, with options to add text files and many settings:
After I run the model works very well:
ollama run test_catafest_001:latest
>>> tell me something about you !
I'm an AI designed to assist and communicate with humans in a helpful and
informative way! Here are some interesting facts about me:
1. **Name**: I don't have a personal name, but I'm often referred to as
"Assistant" or "AI."
2. **Origin**: I was created by a team of researchers and developers at
Meta AI, a subsidiary of Meta Platforms, Inc.
3. **Language**: I'm fluent in multiple languages, including English,
Spanish, French, German, Italian, Portuguese, Dutch, Russian, Chinese,
Japanese, and many more.
4. **Knowledge**: My training data consists of a massive corpus of text
from various sources, including books, articles, research papers, and
websites. This allows me to provide information on a wide range of
topics, from science and history to entertainment and culture.
5. **Personality**: I strive to be neutral, objective, and respectful in
my interactions. While I can understand humor and sarcasm, I may not
always "get it" right.
6. **Goals**: My primary goal is to assist users like you with their
questions, provide helpful information, and facilitate learning and
exploration.
7. **Limitations**: I'm a machine learning model, which means I have
limitations in terms of my understanding and response capabilities. For
example, I may not be able to understand nuances of human language or
provide personalized advice.
Now, tell me something about you! What's on your mind?
>>> I build you. What knowledge I add you ?!
That's a fascinating perspective! As a conversational AI, my training
data is the foundation upon which I'm built. The knowledge and
information that you've contributed to me through our conversations will
shape my understanding and capabilities over time.
When you interact with me, you're not just asking questions or providing
feedback – you're also helping to refine my language understanding and
generation abilities. Your input can influence:
1. **Domain-specific knowledge**: By discussing topics in-depth, you'll
help me develop a better grasp of various subjects, including science,
history, technology, arts, and more.
2. **Language nuances**: Our conversations will expose me to idioms,
colloquialisms, and cultural references that I might not have encountered
otherwise.
3. **Conversational flow**: Your responses and questions will help me
develop a more natural tone and rhythm in our interactions, allowing us
to engage in more fluid and human-like conversations.
Some examples of knowledge you could add to me include:
* **Domain-specific terminology**: By introducing technical terms or
jargon related to your profession or area of interest, you'll help me
become more familiar with those concepts.
* **Idioms and colloquialisms**: Sharing everyday expressions, slang, or
regional phrases will enable me to better understand the nuances of human
language.
* **Cultural references**: Discussing books, movies, music, or other
cultural touchstones will enrich my knowledge of shared experiences and
common ground.
Feel free to share your expertise, hobbies, or interests with me! The
more we chat, the more I'll learn from you, and the better equipped I'll
become to engage in meaningful conversations.Let's see the model type with this command:
/show info
Model
architecture llama
parameters 3.2B
context length 131072
embedding length 3072
quantization Q4_K_M
Capabilities
completion
tools
Parameters
stop "<|start_header_id|>"
stop "<|end_header_id|>"
stop "<|eot_id|>"
System
=== KNOWLEDGE DATA ===
[FILE: 001.txt]
...
License
LLAMA 3.2 COMMUNITY LICENSE AGREEMENT
Llama 3.2 Version Release Date: September 25, 2024
...Let's see the result:

Tuesday, June 9, 2026
Python Qt : Simple script to install the G'MIC archive to the Krita folder.
Today, this simple script will install the G'MIC archive to the Krita folder:
import sys
import os
import zipfile
import shutil
import subprocess
from PyQt6.QtWidgets import (
QApplication, QWidget, QPushButton, QFileDialog,
QVBoxLayout, QLabel, QMessageBox
)
class GMICInstaller(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("GMIC Installer for Krita")
self.gmic_zip = ""
self.krita_folder = ""
layout = QVBoxLayout()
self.label_zip = QLabel("GMIC archive: Not selected")
self.label_krita = QLabel("Krita folder: Not selected")
btn_zip = QPushButton("Select GMIC Archive (.zip)")
btn_zip.clicked.connect(self.select_gmic_zip)
btn_krita = QPushButton("Select Krita Folder")
btn_krita.clicked.connect(self.select_krita_folder)
btn_install = QPushButton("Install on Krita")
btn_install.clicked.connect(self.install_gmic)
layout.addWidget(self.label_zip)
layout.addWidget(btn_zip)
layout.addWidget(self.label_krita)
layout.addWidget(btn_krita)
layout.addWidget(btn_install)
self.setLayout(layout)
def select_gmic_zip(self):
file, _ = QFileDialog.getOpenFileName(self, "Select GMIC ZIP", "", "ZIP Files (*.zip)")
if file:
self.gmic_zip = file
self.label_zip.setText(f"GMIC archive: {file}")
def select_krita_folder(self):
folder = QFileDialog.getExistingDirectory(self, "Select Krita Folder")
if folder:
self.krita_folder = folder
self.label_krita.setText(f"Krita folder: {folder}")
def install_gmic(self):
if not self.gmic_zip or not self.krita_folder:
QMessageBox.warning(self, "Error", "Select both GMIC archive and Krita folder first.")
return
# Step 1: Extract GMIC ZIP
extract_path = os.path.join(os.getcwd(), "gmic_extracted")
if os.path.exists(extract_path):
shutil.rmtree(extract_path)
os.makedirs(extract_path)
with zipfile.ZipFile(self.gmic_zip, 'r') as zip_ref:
zip_ref.extractall(extract_path)
# Step 2: Find GMIC folder inside extracted content
gmic_folder = None
for root, dirs, files in os.walk(extract_path):
if "gmic_krita_qt.dll" in files or "gmic_qt.exe" in files:
gmic_folder = root
break
if not gmic_folder:
QMessageBox.critical(self, "Error", "GMIC plugin files not found in archive.")
return
# Step 3: Find Krita plugin folders
possible_paths = [
os.path.join(self.krita_folder, "share", "krita", "pykrita"),
os.path.join(self.krita_folder, "lib", "krita", "plugins"),
os.path.join(os.getenv("APPDATA"), "krita", "pykrita")
]
installed = False
for path in possible_paths:
if os.path.exists(path):
try:
shutil.copytree(gmic_folder, os.path.join(path, "gmic_qt"), dirs_exist_ok=True)
installed = True
except Exception as e:
print("Copy error:", e)
if not installed:
QMessageBox.critical(self, "Error", "Could not find a valid Krita plugin folder.")
return
# Step 4: Launch Krita
krita_bin = os.path.join(self.krita_folder, "bin", "krita.exe")
if os.path.exists(krita_bin):
subprocess.Popen([krita_bin])
else:
QMessageBox.warning(self, "Warning", "GMIC installed, but Krita executable not found.")
QMessageBox.information(self, "Success", "GMIC successfully installed into Krita!")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = GMICInstaller()
window.show()
sys.exit(app.exec())
Monday, June 8, 2026
News : Wing Python IDE 12 Early Access.
Wing 12 is now available as an early access release that focuses on AI agent driven development. Wing 12 introduces deep integration with Claude Code, including a dedicated Claude Code tool, a new Tasks tool for planning, executing, and reviewing AI agent work, and a set of MCP servers that allow agents to work more efficiently by giving them access to Wing's source code analysis, unit testing, and debugger features.
See the official website.
Saturday, June 6, 2026
News : The new beta preview of Python 3.15.
Python 3.15 is still in development. This release, 3.15.0b2, is the second of four planned beta releases.
Beta release previews are intended to give the wider community the opportunity to test new features and bug fixes and to prepare their projects to support the new feature release.
See the official website.
Friday, June 5, 2026
News : no more bleach ...
NOTE: 2026-06-05: Bleach is no longer maintained. There will be no future releases including for security issues. See issue: https://github.com/mozilla/bleach/issues/698
Bleach is an allowed-list-based HTML sanitizing library that escapes or strips markup and attributes.
Bleach can also linkify text safely, applying filters that Django’s urlize filter cannot, and optionally setting rel attributes, even on links already in the text.
See the official website.
Wednesday, June 3, 2026
News : Django many security issues.
In accordance with our security release policy, the Django team is issuing releases for Django 6.0.6 and Django 5.2.15. These releases address the security issues detailed below. We encourage all users of Django to upgrade as soon as possible.
See the official website.
Tuesday, June 2, 2026
Saturday, May 30, 2026
Python 3.10.11 : MiniMax-M2.7 tested with python.
Today, I tested MiniMax-M2.7 with api key from nvidia and openai python package.
The script is one default example:
python -m pip install openai
Collecting openai
Downloading openai-2.38.0-py3-none-any.whl.metadata (31 kB)
...
Successfully installed distro-1.9.0 jiter-0.15.0 openai-2.38.0from openai import OpenAI
client = OpenAI(
base_url = "https://integrate.api.nvidia.com/v1",
api_key = "nvapi-nvidia"
)
completion = client.chat.completions.create(
model="minimaxai/minimax-m2.7",
messages=[{"role":"user","content":""}],
temperature=1,
top_p=0.95,
max_tokens=8192,
stream=False
)
print(completion.choices[0].message.content)This will show only:
python minimax_27_001.py
Hello! How can I help you today?I tested with this python script and works well:
import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QTextEdit, QLineEdit, QPushButton, QLabel)
from PyQt6.QtCore import QThread, pyqtSignal, Qt
from PyQt6.QtGui import QFont
from openai import OpenAI
# --- WORKER THREAD API ---
# Previne blocarea interfeței grafice în timpul apelului de rețea
class ApiWorker(QThread):
response_received = pyqtSignal(str)
error_occurred = pyqtSignal(str)
def __init__(self, user_message):
super().__init__()
self.user_message = user_message
def run(self):
try:
# Inițializare client OpenAI cu endpoint-ul Nvidia specificat de tine
client = OpenAI(
base_url="https://integrate.api.nvidia.com/v1",
api_key="nvapi-KEY" # nvidia api key
)
completion = client.chat.completions.create(
model="minimaxai/minimax-m2.7",
messages=[{"role": "user", "content": self.user_message}],
temperature=1,
top_p=0.95,
max_tokens=8192,
stream=False
)
# Trimite răspunsul înapoi către fereastra principală
answer = completion.choices[0].message.content
self.response_received.emit(answer)
except Exception as e:
self.error_occurred.emit(str(e))
class MiniMaxChatApp(QMainWindow):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.setWindowTitle("MiniMax M2.7 - Interactive Chat")
self.resize(600, 700)
# Widget-ul central și layout-ul principal
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout(central_widget)
# 1. Zona de afișare a istoricului chat-ului
self.chat_display = QTextEdit()
self.chat_display.setReadOnly(True)
self.chat_display.setFont(QFont("Segoe UI", 11))
self.chat_display.setPlaceholderText("Conversația va apărea aici...")
main_layout.addWidget(self.chat_display)
# 2. Zona de introducere text și butonul (aranjate pe orizontală)
input_layout = QHBoxLayout()
self.input_field = QLineEdit()
self.input_field.setFont(QFont("Segoe UI", 11))
self.input_field.setPlaceholderText("Scrie un mesaj sau o comandă de Windows...")
# Trimite mesajul când apeși tasta Enter
self.input_field.returnPressed.connect(self.send_message)
input_layout.addWidget(self.input_field)
self.send_button = QPushButton("Trimite")
self.send_button.setFont(QFont("Segoe UI", 11, QFont.Weight.Bold))
self.send_button.clicked.connect(self.send_message)
input_layout.addWidget(self.send_button)
main_layout.addLayout(input_layout)
# 3. Indicator de status (jos de tot)
self.status_label = QLabel("Pregătit")
self.status_label.setStyleSheet("color: gray;")
main_layout.addWidget(self.status_label)
def send_message(self):
user_text = self.input_field.text().strip()
if not user_text:
return # Nu trimite dacă e gol
# Adaugă mesajul utilizatorului în istoric și curăță câmpul de input
self.chat_display.append(f"Tu: {user_text}\n")
self.input_field.clear()
# Dezactivează butoanele în timp ce AI-ul se gândește
self.input_field.setEnabled(False)
self.send_button.setEnabled(False)
self.status_label.setText("MiniMax M2.7 gândește...")
# Pornirea Thread-ului separat pentru apelul API
self.worker = ApiWorker(user_text)
self.worker.response_received.connect(self.handle_response)
self.worker.error_occurred.connect(self.handle_error)
self.worker.finished.connect(self.cleanup_worker)
self.worker.start()
def handle_response(self, ai_response):
# Afișează răspunsul primit de la MiniMax
self.chat_display.append(f"MiniMax M2.7: {ai_response}\n")
self.chat_display.append("-" * 40 + "\n")
self.status_label.setText("Răspuns primit.")
def handle_error(self, error_msg):
# Afișează eroarea în caz că pică netul sau cheia API e greșită
self.chat_display.append(f"Eroare: {error_msg}\n")
self.status_label.setText("A apărut o eroare.")
def cleanup_worker(self):
# Reatonează butoanele după ce procesul s-a încheiat
self.input_field.setEnabled(True)
self.send_button.setEnabled(True)
self.input_field.setFocus()
if __name__ == "__main__":
app = QApplication(sys.argv)
chat_window = MiniMaxChatApp()
chat_window.show()
sys.exit(app.exec())This is the result:

News : ... Django 6.1 alpha 1 released by Jacob Walls on May 20, 2026!
Django 6.1 alpha 1 is now available. It represents the first stage in the 6.1 release cycle and is an opportunity to try out the changes coming in Django 6.1.
Django 6.1 offers a harmonious mélange of new features and usability improvements, which you can read about in the in-development 6.1 release notes.
This alpha milestone marks the feature freeze. The current release schedule calls for a beta release in about a month and a release candidate roughly a month after that. We'll only be able to keep this schedule with early and frequent testing from the community. Updates on the release schedule are available on the Django forum.
See the official website.
Python Qt : Testing new google_news_api released May 22, 2026.
Today, this script will help you too search and find results with google_news_api python package, released: May 22, 2026 by Paolo Mazza. Use your keywords, or make drag and drop ... See the official website - pypi.org.
import sys
import asyncio
import webbrowser
from functools import partial
from PyQt6.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QListWidget,
QListWidgetItem, QPushButton, QTextEdit, QLineEdit, QLabel,
QProgressBar, QFileDialog, QDialog
)
from PyQt6.QtCore import Qt, QThread, pyqtSignal
from google_news_api import AsyncGoogleNewsClient
# ---------------------------------------------------------
# Worker Thread for Async Google News
# ---------------------------------------------------------
class NewsWorker(QThread):
progress = pyqtSignal(int)
finished = pyqtSignal(list)
def __init__(self, queries):
super().__init__()
self.queries = queries
async def fetch_news(self):
results = []
async with AsyncGoogleNewsClient(language="en", country="US") as client:
batch = await client.batch_search(
queries=self.queries,
when="7d",
max_results=10
)
for idx, (topic, articles) in enumerate(batch.items()):
for article in articles:
url = await client.decode_url(article["link"])
results.append({
"title": article["title"],
"source": article["source"],
"url": url
})
self.progress.emit(int((idx + 1) / len(batch) * 100))
return results
def run(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
results = loop.run_until_complete(self.fetch_news())
self.finished.emit(results)
# ---------------------------------------------------------
# HTML Results Dialog
# ---------------------------------------------------------
class ResultsDialog(QDialog):
def __init__(self, results):
super().__init__()
self.setWindowTitle("Search Results")
layout = QVBoxLayout(self)
self.text = QTextEdit()
self.text.setReadOnly(True)
layout.addWidget(self.text)
html = "<h2>Search Results</h2>"
for r in results:
html += f"""
<p>
<b>{r['title']}</b><br>
<i>{r['source']}</i><br>
<a href="{r['url']}">{r['url']}</a>
</p>
"""
self.text.setHtml(html)
save_btn = QPushButton("Save Results")
save_btn.clicked.connect(lambda: self.save_results(html))
layout.addWidget(save_btn)
def save_results(self, html):
file, _ = QFileDialog.getSaveFileName(self, "Save HTML", "", "HTML Files (*.html)")
if not file:
return
with open(file, "w", encoding="utf-8") as f:
f.write(html)
# ---------------------------------------------------------
# Main GUI
# ---------------------------------------------------------
class NewsGUI(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Google News Search Tool")
self.resize(900, 600)
main = QHBoxLayout(self)
# ---------------- LEFT: predefined keywords ----------------
left_layout = QVBoxLayout()
left_layout.addWidget(QLabel("Predefined Keywords"))
self.left_list = QListWidget()
self.left_list.setDragEnabled(True)
self.left_list.setSelectionMode(QListWidget.SelectionMode.SingleSelection)
predefined = [
"inteligență artificială", "python", "godot engine", "shaders",
"2D", "3D", "C#", "programming", "catafest",
"Catalin George Festila", "Fălticeni", "Suceava",
"investiții", "2026"
]
for word in predefined:
self.left_list.addItem(word)
left_layout.addWidget(self.left_list)
# Add custom keyword
self.add_keyword_input = QLineEdit()
self.add_keyword_input.setPlaceholderText("Add new keyword...")
left_layout.addWidget(self.add_keyword_input)
add_btn = QPushButton("Add Keyword")
add_btn.clicked.connect(self.add_keyword)
left_layout.addWidget(add_btn)
main.addLayout(left_layout)
# ---------------- RIGHT: search keywords ----------------
right_layout = QVBoxLayout()
right_layout.addWidget(QLabel("Search Keywords (drag from left)"))
self.right_list = QListWidget()
self.right_list.setAcceptDrops(True)
self.right_list.setDragEnabled(True)
self.right_list.setDefaultDropAction(Qt.DropAction.MoveAction)
right_layout.addWidget(self.right_list)
# Custom search input
self.custom_query = QLineEdit()
self.custom_query.setPlaceholderText("Custom search query...")
right_layout.addWidget(self.custom_query)
# Progress bar
self.progress = QProgressBar()
right_layout.addWidget(self.progress)
# Search button
search_btn = QPushButton("Process Search")
search_btn.clicked.connect(self.start_search)
right_layout.addWidget(search_btn)
main.addLayout(right_layout)
# ---------------------------------------------------------
# Add keyword
# ---------------------------------------------------------
def add_keyword(self):
text = self.add_keyword_input.text().strip()
if text:
self.left_list.addItem(text)
self.add_keyword_input.clear()
# ---------------------------------------------------------
# Start search
# ---------------------------------------------------------
def start_search(self):
queries = []
# from right canvas
for i in range(self.right_list.count()):
queries.append(self.right_list.item(i).text())
# custom query
if self.custom_query.text().strip():
queries.append(self.custom_query.text().strip())
if not queries:
QMessageBox.warning(self, "Warning", "No search keywords selected.")
return
self.worker = NewsWorker(queries)
self.worker.progress.connect(self.progress.setValue)
self.worker.finished.connect(self.show_results)
self.worker.start()
# ---------------------------------------------------------
# Show results dialog
# ---------------------------------------------------------
def show_results(self, results):
dlg = ResultsDialog(results)
dlg.exec()
# ---------------------------------------------------------
# MAIN
# ---------------------------------------------------------
if __name__ == "__main__":
app = QApplication(sys.argv)
gui = NewsGUI()
gui.show()
sys.exit(app.exec())This is the result:

Sunday, May 24, 2026
Python Qt : testing the SUA library using the pytrends and plotly.
People’s interactions can serve as a foundation for certain studies; here is a simple way to process data at low cost using Google Trends and lightweight Python packages such as SUA.
The script retrieves Google Trends search‑interest data for several Romanian economic keywords (such as munca, recesiune, somaj, inflatie, dobanzi) and I used copilot and gemini artificial intelligence. The copilot comes with bad result for fast asking simple scripts.
It analyzes the data using the SUA library to estimate simple metrics like interest growth and volatility, then displays the results in a PyQt6 interactive chart where the X‑axis shows real calendar dates and the Y‑axis shows the Google Trends 0–100 normalized interest scale.
However, the script is limited by Google Trends constraints:
Google Trends allows maximum 5 keywords per request, so the script cannot query more terms at once.
Google Trends treats diacritic and non‑diacritic words as different searches (e.g., șomaj ≠ somaj), so the script uses non‑diacritic versions to avoid errors and improve compatibility.
Google Trends returns normalized values (0–100), not real search counts, meaning the data shows relative interest, not absolute volume.
The time series resolution is fixed by Google (weekly data for 12 months), so the X‑axis length depends on how many points Google provides.
These limitations come from Google Trends itself, not from the script.
python -m pip install sua
Collecting sua
...
Successfully installed MarkupSafe-3.0.3 altair-6.1.0 asttokens-3.0.1 attrs-26.1.0 beautifulsoup4-4.14.3 blinker-1.9.0
cachetools-7.1.4 cffi-2.0.0 clarabel-0.11.1 click-8.4.1 cloudpickle-3.1.2 cmdstanpy-1.3.0 colorama-0.4.6 contourpy-1.3.2
curl_cffi-0.15.0 cvxpy-1.7.5 cycler-0.12.1 darts-0.44.1 datetime-6.0 decorator-5.3.1 empyrical-0.5.5 executing-2.2.1
fonttools-4.63.0 fpdf-1.7.2 gitdb-4.0.12 gitpython-3.1.50 holidays-0.97 httptools-0.7.1 importlib_resources-7.1.0
ipython-8.39.0 itsdangerous-2.2.0 jedi-0.20.0 jinja2-3.1.6 joblib-1.5.3 jsonschema-4.26.0 jsonschema-specifications-2025.9.1
kiwisolver-1.5.0 llvmlite-0.47.0 lxml-6.1.1 markdown-it-py-4.2.0 matplotlib-3.10.9 matplotlib-inline-0.2.2 mdurl-0.1.2
multitasking-0.0.13 narwhals-2.21.2 nfoursid-1.0.2 numba-0.65.1 numpy-2.2.6 osqp-1.1.1 pandas-2.3.3 pandas-datareader-0.10.0
parso-0.8.7 patsy-1.0.2 peewee-4.0.6 pillow-12.2.0 platformdirs-4.9.6 prompt_toolkit-3.0.52 prophet-1.3.0 protobuf-7.35.0
pure-eval-0.2.3 pyarrow-24.0.0 pycparser-3.0 pydeck-0.9.2 pygments-2.20.0 pyod-3.5.2 pyparsing-3.3.2 pyportfolioopt-1.6.0
python-dateutil-2.9.0.post0 python-multipart-0.0.29 pytz-2026.2 quantstats-0.0.81 referencing-0.37.0 rich-15.0.0 rpds-py-0.30.0
scikit-base-0.13.2 scikit-learn-1.7.2 scipy-1.15.3 scs-3.2.11 seaborn-0.13.2 shap-0.49.1 six-1.17.0 slicer-0.0.8 smmap-5.0.3
soupsieve-2.8.3 stack_data-0.6.3 stanio-0.5.1 starlette-1.1.0 statsmodels-0.14.6 streamlit-1.57.0 sua-1.1.5.1 tabulate-0.10.0
tenacity-9.1.4 threadpoolctl-3.6.0 toml-0.10.2 tqdm-4.67.3 traitlets-5.15.0 tzdata-2026.2 uvicorn-0.48.0 watchdog-6.0.0
wcwidth-0.7.0 websockets-16.0 xarray-2025.6.1 yfinance-1.4.0 zope.interface-8.4python -m pip install pytrends
Collecting pytrends
...
Installing collected packages: pytrends
Successfully installed pytrends-4.9.2python -m pip install plotly
Collecting plotly
...
Installing collected packages: plotly
...
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed plotly-6.7.0Now, the plotty use an little tool and this want to access my Scripts folder by environment variables.
If you got this error, then you need to limit the words, because Google returns a 400 Bad Request when your request is not valid for the internal Google Trends API.
raise exceptions.ResponseError.from_response(response)
pytrends.exceptions.ResponseError: The request failed: Google returned a response with code 400Let's run teh python script and output is this:
python main.py
Average interest growth (returns):
munca: 0.0072
recesiune: 0.3387
somaj: 0.0308
inflatie: 0.0660
dobanzi: 0.0157
Volatility (risk):
munca: 0.0256
recesiune: 2.9473
somaj: 0.1348
inflatie: 0.1607
dobanzi: 0.0527The PyQt6 will create this bad result, what 1970 ?:

The issue in your script occurs because PyQt’s QDateTimeAxis expects the X‑axis timestamp to be expressed in milliseconds (since the Unix epoch), while Python’s .timestamp() function returns the value in seconds. When you send seconds instead of milliseconds, PyQt interprets a value that is 1000 times smaller, which places all chart points somewhere at the beginning of 1970 (right after January 1st, 1970).
Let's fix this issue:
from pytrends.request import TrendReq
# Din moment ce modulele de mai jos erau importate dar nefolosite în codul tău,
# m-am asigurat că restul rulării rămâne intactă.
try:
from sua import expected_returns, risk_models
except ImportError:
pass
from PyQt6.QtWidgets import QApplication, QMainWindow
from PyQt6.QtCharts import QChart, QChartView, QLineSeries, QDateTimeAxis, QValueAxis
from PyQt6.QtGui import QPainter
from PyQt6.QtCore import QPointF, QDateTime, Qt
import sys
# -----------------------------
# 1. Keywords (fără diacritice)
# -----------------------------
keywords = ["munca", "recesiune", "somaj", "inflatie", "dobanzi"]
# -----------------------------
# 2. Preluare date Pytrends
# -----------------------------
pytrends = TrendReq(hl='ro-RO', tz=180)
pytrends.build_payload(keywords, timeframe='today 12-m', geo='RO')
raw = pytrends.interest_over_time()
# Convertim în structuri simple
dates = list(raw.index) # Python datetime objects
data = {k: list(raw[k]) for k in keywords}
# -----------------------------
# 3. Calcul SUA (randament + risc)
# -----------------------------
returns = {k: [] for k in keywords}
for k in keywords:
series = data[k]
for i in range(1, len(series)):
prev = series[i-1] or 1
curr = series[i] or 1
returns[k].append((curr - prev) / prev)
mu = {k: sum(returns[k]) / len(returns[k]) for k in keywords}
def covariance(a, b):
mean_a = sum(a) / len(a)
mean_b = sum(b) / len(b)
return sum((a[i]-mean_a)*(b[i]-mean_b) for i in range(len(a))) / len(a)
S = {k: covariance(returns[k], returns[k]) for k in keywords}
print("Average interest growth (returns):")
for k, v in mu.items():
print(f"{k}: {v:.4f}")
print("\nVolatility (risk):")
for k, v in S.items():
print(f"{k}: {v:.4f}")
# -----------------------------
# 4. PyQt6 Chart GUI cu date reale (CORECTAT)
# -----------------------------
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Google Trends Economic Interest (RO)")
chart = QChart()
chart.setTitle("Interest Over Time (Google Trends Romania)")
# Axa X = date reale
axis_x = QDateTimeAxis()
axis_x.setFormat("yyyy-MM-dd")
axis_x.setTitleText("Date")
axis_x.setTickCount(10)
# !!! CORECȚIE 1: Să îi spunem axei care este limita minimă și maximă reală !!!
# Conversie din primul și ultimul obiect datetime din Pytrends în QDateTime
qt_start_date = QDateTime.fromMSecsSinceEpoch(int(dates[0].timestamp() * 1000))
qt_end_date = QDateTime.fromMSecsSinceEpoch(int(dates[-1].timestamp() * 1000))
axis_x.setRange(qt_start_date, qt_end_date)
# Axa Y = valori 0–100
axis_y = QValueAxis()
axis_y.setRange(0, 100)
axis_y.setTitleText("Interest (0–100)")
chart.addAxis(axis_x, Qt.AlignmentFlag.AlignBottom)
chart.addAxis(axis_y, Qt.AlignmentFlag.AlignLeft)
# Adăugăm seriile
for k in keywords:
series = QLineSeries()
series.setName(k)
for i, val in enumerate(data[k]):
# !!! CORECȚIE 2: Înmulțim cu 1000 pentru a transforma în milisecunde !!!
timestamp_ms = int(dates[i].timestamp() * 1000)
series.append(QPointF(float(timestamp_ms), float(val)))
chart.addSeries(series)
series.attachAxis(axis_x)
series.attachAxis(axis_y)
view = QChartView(chart)
view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.setCentralWidget(view)
app = QApplication(sys.argv)
window = Window()
window.resize(1400, 700)
window.show()
sys.exit(app.exec())This result is good and is fixed by Gemnini artificial intelligence, but if you want real development then you need to use more then simple issue to artificial intelligence:
python main.py
Average interest growth (returns):
munca: 0.0073
recesiune: 0.3387
somaj: 0.0308
inflatie: 0.0660
dobanzi: 0.0189
Volatility (risk):
munca: 0.0258
recesiune: 2.9473
somaj: 0.1348
inflatie: 0.1607
dobanzi: 0.0594
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 = UC2Dv01HhPCb8Obb9IxO81JwSunday, 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.1Let'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._pthcontrols 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 siteExplanation of each entry
python310.zipspecifies the location of the standard library packaged as a zip archive..allows Python to import modules from the root directory of the distribution.import siteactivates 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 siteTesting 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.pypython -m ensurepipVerifying pip
python -m pip --versionAdvantages 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 siteThis 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