analitics

Pages

Thursday, October 8, 2026

News : Python 3.15 PEP's.

Python 3.15 brings significant improvements across performance, core features, and developer tools. The startup time is now much faster thanks to explicit lazy imports, and the experimental JIT compiler provides a 7 to 12 percent speed boost depending on your operating system. The update introduces new built-in types like frozendict and sentinel, and officially makes UTF-8 the default text encoding. For developers, the standard library now includes better error messages, more console colors, and a new high-frequency profiling tool called Tachyon to help measure and optimize code performance. The type hinting system is more flexible, and there are major updates to the C API. Most notably, it includes a stable ABI for free-threaded builds, which is a massive step forward for running efficient multi-threaded Python code without the Global Interpreter Lock. Additionally, official macOS installers now include free-threading support by default, Windows 64-bit binaries use a more optimized tail-calling interpreter, and frame pointers are enabled by default for easier system-level debugging. This specific Release Candidate 3 is a surprise extra release added to fix a few last-minute bugs related to the new lazy imports before the final version is launched.
  • PEP 810: Explicit lazy imports for faster startup times
  • PEP 814: Add frozendict built-in type
  • PEP 661: Add sentinel built-in type
  • PEP 798: Unpacking in comprehensions
  • PEP 686: Python now uses UTF-8 as the default encoding
  • PEP 829: Package startup configuration files
  • PEP 799: Tachyon: High frequency statistical sampling profiler / A dedicated profiling package
  • PEP 728: TypedDict with typed extra items
  • PEP 747: Annotating type forms with TypeForm
  • PEP 800: Disjoint bases in the type system
  • PEP 803, 820, 793: Stable ABI for free-threaded builds and related C API
  • PEP 782: A new PyBytesWriter C API to create a Python bytes object
  • PEP 788: Protection against finalization in the C API
  • PEP 831: Frame pointers are enabled by default for improved system-level observability
  • PEP 790: 3.15 release schedule

tkinter : analyze stock market data for a chosen ticker symbol

This Python script creates a mobile-friendly graphical application using Tkinter to analyze stock market data for a chosen ticker symbol, such as NVDA. When you launch the app, it presents a user interface with input fields to type a stock symbol and select a time frame, like one month, six months, or one year. Upon clicking the Load button, the script sends a direct web request to Yahoo Finance. It uses a custom user browser agent header to fetch accurate raw historical market price data, avoiding network blocking or API failures on mobile Python environments. Once the raw data is downloaded, the script converts the response into a structured Pandas dataframe containing daily open, high, low, close, and volume values. It then automatically computes several key technical market indicators. It calculates 20-day and 50-day Simple Moving Averages to show price trends over time. It computes Bollinger Bands to illustrate price volatility boundaries, and it calculates the Relative Strength Index over 14 days to identify overbought or oversold market conditions. At the top of the interface, the app displays text cards showing the latest stock price, current RSI value, and moving averages. Below these metrics, it renders two interactive charts using Matplotlib embedded directly into Tkinter. The upper chart plots the stock closing price alongside the moving averages and Bollinger Bands shading. The lower chart plots the RSI line with dashed horizontal benchmark lines at values 30 and 70. In short, this script serves as a lightweight, real-time stock dashboard that fetches financial market data, calculates essential technical indicators, and visualizes price action clearly on mobile screens.
Let's see the source code:
import tkinter as tk
from tkinter import ttk, messagebox
import pandas as pd
import numpy as np
import requests
from datetime import datetime, timedelta

import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure

class FinancialDashboardApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Market Indicators Dashboard")
        self.root.geometry("480x800")

        # --- Controls Frame (Top) ---
        control_frame = ttk.LabelFrame(root, text=" Parametrii Simbol ")
        control_frame.pack(fill="x", padx=10, pady=5)

        ttk.Label(control_frame, text="Ticker:").grid(row=0, column=0, padx=5, pady=5)
        self.ticker_entry = ttk.Entry(control_frame, width=8)
        self.ticker_entry.insert(0, "NVDA")
        self.ticker_entry.grid(row=0, column=1, padx=5, pady=5)

        ttk.Label(control_frame, text="Perioadă:").grid(row=0, column=2, padx=5, pady=5)
        self.period_cb = ttk.Combobox(control_frame, values=["1mo", "3mo", "6mo", "1y"], width=6)
        self.period_cb.set("6mo")
        self.period_cb.grid(row=0, column=3, padx=5, pady=5)

        fetch_btn = ttk.Button(control_frame, text="Încarcă", command=self.load_data)
        fetch_btn.grid(row=0, column=4, padx=5, pady=5)

        # --- Info Cards Frame (Middle) ---
        self.info_frame = ttk.LabelFrame(root, text=" Date & Indicatori ")
        self.info_frame.pack(fill="x", padx=10, pady=5)

        self.lbl_price = ttk.Label(self.info_frame, text="Preț: -", font=("Helvetica", 10, "bold"))
        self.lbl_price.grid(row=0, column=0, sticky="w", padx=10, pady=2)

        self.lbl_rsi = ttk.Label(self.info_frame, text="RSI (14): -")
        self.lbl_rsi.grid(row=0, column=1, sticky="w", padx=10, pady=2)

        self.lbl_sma20 = ttk.Label(self.info_frame, text="SMA 20: -")
        self.lbl_sma20.grid(row=1, column=0, sticky="w", padx=10, pady=2)

        self.lbl_sma50 = ttk.Label(self.info_frame, text="SMA 50: -")
        self.lbl_sma50.grid(row=1, column=1, sticky="w", padx=10, pady=2)

        # --- Matplotlib Canvas Frame (Bottom) ---
        self.plot_frame = ttk.Frame(root)
        self.plot_frame.pack(fill="both", expand=True, padx=10, pady=5)

        self.fig = Figure(figsize=(5, 6), dpi=90)
        self.ax1 = self.fig.add_subplot(211) # Subplot Preț + SMA / Bollinger
        self.ax2 = self.fig.add_subplot(212, sharex=self.ax1) # Subplot RSI

        self.canvas = FigureCanvasTkAgg(self.fig, master=self.plot_frame)
        self.canvas.get_tk_widget().pack(fill="both", expand=True)

        self.load_data()

    def fetch_yahoo_data(self, symbol, range_str):
        """Descarcă date brute din Yahoo v8 API simulând un browser desktop."""
        url = f"https://query1.finance.yahoo.com/v8/finance/chart/{symbol}?range={range_str}&interval=1d"
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
        }
        
        response = requests.get(url, headers=headers, timeout=10)
        if response.status_code != 200:
            return pd.DataFrame()

        data = response.json()
        result = data['chart']['result'][0]
        timestamps = result['timestamp']
        quote = result['indicators']['quote'][0]

        df = pd.DataFrame({
            'Open': quote['open'],
            'High': quote['high'],
            'Low': quote['low'],
            'Close': quote['close'],
            'Volume': quote['volume']
        }, index=pd.to_datetime(timestamps, unit='s'))

        df.dropna(subset=['Close'], inplace=True)
        return df

    def calculate_indicators(self, df):
        # 1. Simple Moving Averages (SMA)
        df['SMA_20'] = df['Close'].rolling(window=20).mean()
        df['SMA_50'] = df['Close'].rolling(window=50).mean()

        # 2. Bollinger Bands
        std20 = df['Close'].rolling(window=20).std()
        df['BB_Upper'] = df['SMA_20'] + (std20 * 2)
        df['BB_Lower'] = df['SMA_20'] - (std20 * 2)

        # 3. RSI (14 zile)
        delta = df['Close'].diff()
        gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
        rs = gain / loss
        df['RSI'] = 100 - (100 / (1 + rs))

        return df

    def load_data(self):
        ticker_symbol = self.ticker_entry.get().strip().upper()
        period = self.period_cb.get()

        if not ticker_symbol:
            messagebox.showerror("Eroare", "Introduceți un ticker valid.")
            return

        try:
            # Preluare date prin request HTTP direct
            df = self.fetch_yahoo_data(ticker_symbol, period)

            if df.empty:
                messagebox.showwarning("Atenție", f"Nu s-au găsit date pentru tickerul '{ticker_symbol}'. Verificați conexiunea la internet sau simbolul introduse.")
                return

            df = self.calculate_indicators(df)

            curr_price = df['Close'].iloc[-1]
            last_rsi = df['RSI'].iloc[-1] if not pd.isna(df['RSI'].iloc[-1]) else 0.0
            last_sma20 = df['SMA_20'].iloc[-1] if not pd.isna(df['SMA_20'].iloc[-1]) else 0.0
            last_sma50 = df['SMA_50'].iloc[-1] if not pd.isna(df['SMA_50'].iloc[-1]) else 0.0

            # Actualizare etichete UI
            self.lbl_price.config(text=f"Preț: {curr_price:.2f} USD")
            self.lbl_rsi.config(text=f"RSI (14): {last_rsi:.2f}")
            self.lbl_sma20.config(text=f"SMA 20: {last_sma20:.2f}")
            self.lbl_sma50.config(text=f"SMA 50: {last_sma50:.2f}")

            # Generare grafice
            self.plot_charts(df, ticker_symbol)

        except Exception as e:
            messagebox.showerror("Eroare", f"A apărut o eroare la procesare:\n{e}")

    def plot_charts(self, df, ticker_symbol):
        self.ax1.clear()
        self.ax2.clear()

        # Grafic 1: Preț, SMA 20, SMA 50 și Benzi Bollinger
        self.ax1.plot(df.index, df['Close'], label='Close', color='black', linewidth=1.2)
        self.ax1.plot(df.index, df['SMA_20'], label='SMA 20', color='blue', linestyle='--', alpha=0.7)
        self.ax1.plot(df.index, df['SMA_50'], label='SMA 50', color='orange', linestyle='--', alpha=0.7)
        self.ax1.plot(df.index, df['BB_Upper'], label='BB Upper', color='gray', linestyle=':', alpha=0.5)
        self.ax1.plot(df.index, df['BB_Lower'], label='BB Lower', color='gray', linestyle=':', alpha=0.5)
        self.ax1.fill_between(df.index, df['BB_Lower'], df['BB_Upper'], color='gray', alpha=0.1)

        self.ax1.set_title(f"{ticker_symbol} - Preț & Indicatori", fontsize=10)
        self.ax1.legend(loc="upper left", fontsize=7)
        self.ax1.grid(True, linestyle=':', alpha=0.6)

        # Grafic 2: RSI cu nivelurile 30 și 70
        self.ax2.plot(df.index, df['RSI'], label='RSI (14)', color='purple')
        self.ax2.axhline(70, color='red', linestyle='--', alpha=0.6)   # Overbought
        self.ax2.axhline(30, color='green', linestyle='--', alpha=0.6) # Oversold
        self.ax2.set_ylim(0, 100)
        self.ax2.set_title("RSI (14)", fontsize=9)
        self.ax2.grid(True, linestyle=':', alpha=0.6)

        self.fig.autofmt_xdate(rotation=30)
        self.fig.tight_layout()
        self.canvas.draw()

if __name__ == "__main__":
    root = tk.Tk()
    app = FinancialDashboardApp(root)
    root.mainloop()

Tuesday, October 6, 2026

News : How fast is Python 3.15? by Miguel Grinberg.

The article “How fast is Python 3.15?” provides a detailed performance analysis of the upcoming CPython release, focusing on benchmark‑driven comparisons across standard CPython, the new JIT compiler, and the free‑threaded (no‑GIL) build. Using two deterministic workloads—recursive Fibonacci and bubble sort—the author evaluates Python 3.15 in both single‑threaded and multi‑threaded execution modes, generating a clear performance profile relevant for developers, performance engineers, and technical SEO audiences researching Python speed improvements.
In single‑threaded Fibonacci, Python 3.15 shows incremental gains over Python 3.14, continuing the trend of small but consistent optimizations introduced after the major jump in Python 3.11. The standout result is the Python 3.15 JIT, which delivers a 1.20× speedup over standard CPython. This marks the first release where the JIT consistently outperforms the interpreter in this benchmark. PyPy remains significantly faster, achieving 5.5× the speed of CPython 3.15, while Rust demonstrates extreme performance at 77× faster, reinforcing the gap between dynamic and compiled languages.
The single‑threaded bubble‑sort benchmark confirms the same pattern: Python 3.15 is slightly faster than 3.14, but the JIT build provides a substantial 1.28× improvement. Node.js surpasses PyPy in this specific workload, highlighting how algorithmic characteristics influence interpreter performance. Free‑threaded CPython remains slower in single‑thread mode due to concurrency‑related overhead, which is expected.
The multi‑threaded benchmarks reveal the most important performance evolution. Standard CPython continues to scale poorly because of the Global Interpreter Lock (GIL), and Python 3.15 is marginally slower than 3.14 in multi‑threaded Fibonacci. PyPy also suffers from GIL‑related limitations.
However, free‑threaded Python 3.15 demonstrates a major breakthrough: in multi‑threaded Fibonacci, it achieves a 4.5× speedup compared to standard CPython, matching and stabilizing the gains introduced in Python 3.14 FT. This confirms that the no‑GIL architecture is maturing and becoming a viable option for CPU‑bound parallel workloads. The JIT build maintains its advantage even under multi‑threading, showing consistent performance improvements across all categories.
From a technical SEO perspective, the article positions Python 3.15 as a release focused on predictable single‑threaded optimizations, significant JIT compiler progress, and high‑impact concurrency improvements through the free‑threaded runtime. These advancements make Python 3.15 a strategically important version for developers targeting performance‑critical applications, parallel computing, and future no‑GIL migration paths.
See the full article on the official blog.

thinter : manage python scripts !!!

Today, this simple manager for python scripts with autodetect python modules and add to description for each script with Claude AI.
See the source code:
# -*- coding: utf-8 -*-
import os
import re
import ast
import sys
import json
import hashlib
import subprocess
import tkinter as tk
from tkinter import ttk, messagebox
import tkinter.font as tkfont
DEFAULT_ROOT = "/storage/emulated/0/Download"
CATALOG_NAME = ".scripts_catalog.json"
FONT_SIZE = 10
try:
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))
except NameError:
    BASE_DIR = os.getcwd()
STD = getattr(sys, "stdlib_module_names", None)
if STD is None:
    STD = set((
        "__future__ abc argparse array ast asyncio base64 bisect bz2 calendar cmd code codecs collections "
        "configparser contextlib copy csv ctypes curses dataclasses datetime decimal difflib email enum "
        "fnmatch fractions ftplib functools gc getopt getpass gettext glob gzip hashlib heapq hmac html http "
        "imaplib importlib inspect io itertools json locale logging lzma math mimetypes msvcrt multiprocessing "
        "operator optparse os pathlib pdb pickle platform pprint profile queue random re readline sched secrets "
        "select shelve shutil signal smtplib socket sqlite3 ssl stat statistics string struct subprocess sys "
        "tarfile telnetlib tempfile textwrap threading time timeit tkinter traceback turtle types typing "
        "unittest urllib uuid warnings weakref webbrowser winreg xml zipfile zlib"
    ).split())
ALIASES = {
    "PIL": "Pillow", "cv2": "opencv-python", "yt_dlp": "yt-dlp", "bs4": "beautifulsoup4",
    "sklearn": "scikit-learn", "yaml": "PyYAML", "dotenv": "python-dotenv", "serial": "pyserial",
    "win32api": "pywin32", "win32com": "pywin32", "Crypto": "pycryptodome", "skimage": "scikit-image",
    "dateutil": "python-dateutil", "OpenGL": "PyOpenGL", "bpy": "bpy (Blender)",
}
def safe(text):
    return str(text).encode("utf-8", "replace").decode("utf-8")
def read_json(path):
    try:
        with open(path, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception:
        return None
def is_local(mod, full, root_folder):
    root_n = os.path.normpath(root_folder)
    cur = os.path.normpath(os.path.dirname(full))
    while True:
        is_root = (cur == root_n)
        if os.path.isfile(os.path.join(cur, mod + ".py")):
            return True
        if not is_root and os.path.isdir(os.path.join(cur, mod)):
            return True
        if is_root or len(cur) <= len(root_n):
            return False
        cur = os.path.dirname(cur)
def analyze(full, root_folder):
    try:
        with open(full, "r", encoding="utf-8", errors="replace") as f:
            text = f.read(300000)
    except OSError:
        return "Libs: nu pot citi fișierul"
    mods = []
    try:
        for node in ast.walk(ast.parse(text)):
            if isinstance(node, ast.Import):
                for a in node.names:
                    mods.append(a.name.split(".")[0])
            elif isinstance(node, ast.ImportFrom):
                if node.module and node.level == 0:
                    mods.append(node.module.split(".")[0])
    except (SyntaxError, ValueError):
        pattern = r"^\s*(?:from\s+([A-Za-z_]\w*)[\w.]*\s+import\s|import\s+([A-Za-z_]\w*))"
        for m in re.finditer(pattern, text, re.M):
            mods.append(m.group(1) or m.group(2))
    libs = []
    for m in mods:
        if m in STD or is_local(m, full, root_folder):
            continue
        name = ALIASES.get(m, m)
        if name not in libs:
            libs.append(name)
    libs.sort(key=str.lower)
    return "Libs: " + (", ".join(libs) if libs else "doar biblioteca standard")
class App:
    def __init__(self, root):
        self.root = root
        root.title("Python Scripts Manager")
        root.geometry("900x600")
        self.scripts = []
        self.files = {}
        self.root_folder = ""
        self.font = tkfont.Font(family="Arial", size=FONT_SIZE)
        style = ttk.Style()
        style.configure("Treeview", font=self.font, rowheight=self.font.metrics("linespace") + 14)
        style.configure("Treeview.Heading", font=(self.font.actual("family"), FONT_SIZE, "bold"))
        top = tk.Frame(root)
        top.pack(fill="x", padx=6, pady=4)
        tk.Label(top, text="Folder:").pack(side="left")
        self.path_var = tk.StringVar(value=DEFAULT_ROOT)
        tk.Entry(top, textvariable=self.path_var, font=self.font).pack(side="left", fill="x", expand=True, padx=4)
        tk.Button(top, text="Scan", command=self.load_scripts).pack(side="left")
        search = tk.Frame(root)
        search.pack(fill="x", padx=6)
        tk.Label(search, text="Caută:").pack(side="left")
        self.search_var = tk.StringVar()
        self.search_var.trace_add("write", lambda *a: self.refresh_table())
        tk.Entry(search, textvariable=self.search_var, font=self.font).pack(side="left", fill="x", expand=True, padx=4)
        frame = tk.Frame(root)
        frame.pack(fill="both", expand=True, padx=6, pady=4)
        self.tree = ttk.Treeview(frame, columns=("name", "rel", "desc"), show="headings", selectmode="browse")
        self.tree.heading("name", text="Script")
        self.tree.heading("rel", text="Path relativ")
        self.tree.heading("desc", text="Descriere")
        self.tree.column("name", width=180)
        self.tree.column("rel", width=300)
        self.tree.column("desc", width=300)
        self.tree.tag_configure("auto", foreground="gray")
        vsb = ttk.Scrollbar(frame, orient="vertical", command=self.tree.yview)
        hsb = ttk.Scrollbar(frame, orient="horizontal", command=self.tree.xview)
        self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
        self.tree.grid(row=0, column=0, sticky="nsew")
        vsb.grid(row=0, column=1, sticky="ns")
        hsb.grid(row=1, column=0, sticky="ew")
        frame.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)
        self.tree.bind("", lambda e: self.open_detail())
        btns = tk.Frame(root)
        btns.pack(fill="x", padx=6, pady=4)
        tk.Button(btns, text="Run", command=self.run_script, bg="#2196F3", fg="white", width=8).pack(side="left", padx=2)
        tk.Button(btns, text="Copy path", command=self.copy_path, width=8).pack(side="left", padx=2)
        tk.Button(btns, text="Detalii", command=self.open_detail, width=8).pack(side="left", padx=2)
        tk.Button(btns, text="Auto toate", command=self.auto_all, width=8).pack(side="left", padx=2)
        self.status = tk.Label(root, text="Ready", anchor="w")
        self.status.pack(fill="x", padx=6, pady=2)
        self.load_scripts()
    def primary_file(self):
        return os.path.join(self.root_folder, CATALOG_NAME)
    def fallback_file(self):
        h = hashlib.md5(self.root_folder.encode("utf-8", "replace")).hexdigest()[:8]
        return os.path.join(BASE_DIR, "catalog_" + h + ".json")
    def save_catalog(self):
        payload = {"version": 1, "root": self.root_folder, "files": self.files}
        for path in (self.primary_file(), self.fallback_file()):
            try:
                with open(path, "w", encoding="utf-8") as f:
                    json.dump(payload, f, indent=2, ensure_ascii=False)
                return path
            except OSError:
                continue
        messagebox.showerror("Eroare", "Nu pot salva catalogul.")
        return None
    def load_scripts(self):
        folder = self.path_var.get().strip()
        if not os.path.isdir(folder):
            messagebox.showerror("Eroare", "Folder inexistent:\n" + safe(folder))
            return
        self.root_folder = folder
        data = read_json(self.primary_file()) or read_json(self.fallback_file()) or {}
        old = data.get("files", {})
        found = []
        for dirpath, dirs, files in os.walk(folder):
            dirs[:] = [d for d in dirs if d != "__pycache__"]
            for file in files:
                if file.endswith(".py"):
                    full = os.path.join(dirpath, file)
                    rel = os.path.relpath(full, folder).replace("\\", "/")
                    found.append((file, rel, full))
        found.sort(key=lambda s: s[1].lower())
        found_rels = set(s[1] for s in found)
        orphans = {r: e for r, e in old.items() if r not in found_rels and e.get("desc")}
        new_files = {}
        for name, rel, full in found:
            try:
                st = os.stat(full)
                size, mtime = st.st_size, int(st.st_mtime)
            except OSError:
                size, mtime = 0, 0
            entry = old.get(rel)
            if entry is None:
                entry = {"desc": ""}
                for orel, oe in list(orphans.items()):
                    if os.path.basename(orel) == name and oe.get("size") == size:
                        entry = {"desc": oe.get("desc", ""), "auto": oe.get("auto", "")}
                        del orphans[orel]
                        break
            entry = dict(entry)
            entry["size"] = size
            entry["mtime"] = mtime
            new_files[rel] = entry
        new_files.update(orphans)
        self.files = new_files
        self.scripts = found
        saved = self.save_catalog()
        self.refresh_table()
        if saved:
            self.status.config(text=str(len(found)) + " scripturi | catalog: " + safe(saved))
    def refresh_table(self):
        q = self.search_var.get().strip().lower()
        self.tree.delete(*self.tree.get_children())
        shown = 0
        for i, (name, rel, full) in enumerate(self.scripts):
            entry = self.files.get(rel, {})
            desc = entry.get("desc", "")
            auto = entry.get("auto", "")
            text = desc if desc else ("~ " + auto if auto else "")
            if q and q not in name.lower() and q not in rel.lower() and q not in text.lower():
                continue
            tags = ("auto",) if (not desc and auto) else ()
            self.tree.insert("", "end", iid=str(i), values=(safe(name), safe(rel), safe(text)), tags=tags)
            shown += 1
        if q:
            self.status.config(text=str(shown) + " din " + str(len(self.scripts)) + " scripturi")
    def selected(self):
        sel = self.tree.selection()
        if not sel:
            messagebox.showinfo("Info", "Selectează un script din listă.")
            return None
        return int(sel[0])
    def open_detail(self):
        idx = self.selected()
        if idx is None:
            return
        name, rel, full = self.scripts[idx]
        entry = self.files.setdefault(rel, {})
        win = tk.Toplevel(self.root)
        win.title("Detalii")
        win.transient(self.root)
        w = max(self.root.winfo_width(), 320)
        win.geometry(str(w) + "x" + str(max(self.root.winfo_height() // 2, 280)))
        tk.Label(win, text=safe(rel), wraplength=w - 20, justify="left", anchor="w").pack(fill="x", padx=8, pady=4)
        bar = tk.Frame(win)
        bar.pack(fill="x", padx=8)
        box = tk.Text(win, wrap="word", height=8, font=self.font)
        box.pack(fill="both", expand=True, padx=8, pady=6)
        box.insert("1.0", entry.get("desc") or entry.get("auto", ""))
        def do_auto():
            if box.get("1.0", "end").strip():
                if not messagebox.askyesno("Auto detect", "Înlocuiesc textul din câmp?", parent=win):
                    return
            info = analyze(full, self.root_folder)
            entry["auto"] = info
            box.delete("1.0", "end")
            box.insert("1.0", info)
        def do_update():
            entry["desc"] = box.get("1.0", "end").strip()
            self.save_catalog()
            self.refresh_table()
            win.destroy()
        tk.Button(bar, text="Auto detect", command=do_auto, width=10).pack(side="left", padx=2)
        tk.Button(bar, text="Update", command=do_update, bg="#4CAF50", fg="white", width=8).pack(side="left", padx=2)
        tk.Button(bar, text="Închide", command=win.destroy, width=8).pack(side="left", padx=2)
        win.grab_set()
    def auto_all(self):
        total = len(self.scripts)
        if not total:
            return
        if not messagebox.askyesno("Auto toate", "Regenerez Libs pentru " + str(total) + " scripturi?\nDescrierile tale nu se modifică."):
            return
        for n, (name, rel, full) in enumerate(self.scripts, 1):
            self.files.setdefault(rel, {})["auto"] = analyze(full, self.root_folder)
            if n % 5 == 0:
                self.status.config(text="Analiză " + str(n) + "/" + str(total))
                self.root.update_idletasks()
        self.save_catalog()
        self.refresh_table()
        self.status.config(text="Libs generat pentru " + str(total) + " scripturi")
    def run_script(self):
        idx = self.selected()
        if idx is None:
            return
        full = self.scripts[idx][2]
        try:
            subprocess.Popen([sys.executable, full], cwd=os.path.dirname(full))
            self.status.config(text="Rulat: " + safe(os.path.basename(full)))
        except Exception as e:
            messagebox.showerror("Eroare", "Eroare la rulare:\n" + safe(e))
    def copy_path(self):
        idx = self.selected()
        if idx is None:
            return
        full = self.scripts[idx][2]
        self.root.clipboard_clear()
        self.root.clipboard_append(full)
        self.status.config(text="Copiat: " + safe(full))
if __name__ == "__main__":
    root = tk.Tk()
    App(root)
    root.mainloop()

Python 3.13.13 : Exemple with random and string.

See this source code:
import random
import string

# Generate a random password of length 10
def generate_password(length):
    characters = string.ascii_letters + string.digits + string.punctuation
    return ''.join(random.choices(characters, k=length))


password = generate_password(10)
print(password)

Sunday, October 4, 2026

tkinter : PyTorch and Keras on pydroid3.

Today, simple python script with pytorch and keras on android pydroid application.
This script is a Tkinter graphical application designed to test and verify PyTorch and Keras packages in Pydroid 3. Right at launch, it tracks execution time down to milliseconds and displays the installed package versions in a status panel. All diagnostics run on a background thread to keep the user interface responsive. For PyTorch, the script tests matrix inversion, automatic gradient calculations using Autograd, data processing through a neural network linear layer, and eigenvalue decomposition. For Keras, the script verifies building a Sequential model with dense layers, compiling and training it on synthetic data for 3 epochs, and running a final prediction to confirm end-to-end execution.
This is the result of the running python script:
Let's see the source code:
import os
import time
import threading
import tkinter as tk
from tkinter import ttk, scrolledtext

# Set Keras backend to torch
os.environ["KERAS_BACKEND"] = "torch"

class AppCheck(tk.Tk):
    def __init__(self):
        self.start_time = time.time()
        
        super().__init__()
        self.title("PyTorch & Keras Inspector")
        self.geometry("480x600")
        
        self.style = ttk.Style(self)
        self.style.theme_use('clam')
        
        self.create_widgets()
        
        # Log UI startup time
        ui_ready = self.get_elapsed_str()
        self.log(f"{ui_ready} Tkinter UI initialized")
        
        # Initial package check
        self.check_initial_imports()

    def get_elapsed_str(self):
        """Returns elapsed time in MM:SS.mmm format without brackets."""
        elapsed = time.time() - self.start_time
        minutes = int(elapsed // 60)
        seconds = int(elapsed % 60)
        millis = int((elapsed - int(elapsed)) * 1000)
        return f"+{minutes:02d}:{seconds:02d}.{millis:03d}"

    def create_widgets(self):
        # Status Frame
        self.info_frame = ttk.LabelFrame(self, text=" Package Status ")
        self.info_frame.pack(fill="x", padx=8, pady=4)

        self.lbl_torch = ttk.Label(self.info_frame, text="PyTorch: Checking...", font=("Helvetica", 9))
        self.lbl_torch.pack(anchor="w", padx=8, pady=2)

        self.lbl_keras = ttk.Label(self.info_frame, text="Keras: Checking...", font=("Helvetica", 9))
        self.lbl_keras.pack(anchor="w", padx=8, pady=2)

        # Run Tests Button
        self.btn_run = ttk.Button(self, text="Run Full Test Suite", command=self.start_tests_thread)
        self.btn_run.pack(pady=6)

        # Console Log Header
        ttk.Label(self, text="Execution Log:", font=("Helvetica", 9, "bold")).pack(anchor="w", padx=8)
        
        # Font size reduced to 7 to fit ~10% more content line-by-line
        self.txt_log = scrolledtext.ScrolledText(self, height=25, font=("Courier", 7))
        self.txt_log.pack(fill="both", expand=True, padx=8, pady=4)

    def log(self, message):
        """Helper to append text to the log window."""
        self.txt_log.insert(tk.END, message + "\n")
        self.txt_log.see(tk.END)

    def check_initial_imports(self):
        t_str = self.get_elapsed_str()
        self.log(f"{t_str} Checking imports...")
        
        # Check PyTorch
        try:
            import torch
            self.lbl_torch.config(text=f"PyTorch: v{torch.__version__}", foreground="green")
            self.log(f"{self.get_elapsed_str()} PyTorch v{torch.__version__} loaded successfully")
        except ImportError as e:
            self.lbl_torch.config(text="PyTorch: Not installed", foreground="red")
            self.log(f"{self.get_elapsed_str()} PyTorch import error: {e}")

        # Check Keras
        try:
            import keras
            self.lbl_keras.config(text=f"Keras: v{keras.__version__}", foreground="green")
            self.log(f"{self.get_elapsed_str()} Keras v{keras.__version__} loaded successfully")
        except ImportError as e:
            self.lbl_keras.config(text="Keras: Not installed", foreground="red")
            self.log(f"{self.get_elapsed_str()} Keras import error: {e}")

    def start_tests_thread(self):
        self.btn_run.config(state="disabled")
        threading.Thread(target=self.run_tests, daemon=True).start()

    def run_tests(self):
        self.log(f"\n{self.get_elapsed_str()} STARTING FULL TEST SUITE")

        # PYTORCH TESTS
        try:
            import torch
            
            # Test 1: Matrix Inverse
            self.log(f"{self.get_elapsed_str()} PyTorch Test 1/4 Matrix Operations")
            A = torch.tensor([[2.0, 1.0], [1.0, 4.0]])
            A_inv = torch.inverse(A)
            self.log(f"   Inverted matrix output:")
            for row in A_inv.numpy():
                self.log(f"   {row}")

            # Test 2: Autograd
            self.log(f"{self.get_elapsed_str()} PyTorch Test 2/4 Autograd Gradient Calculation")
            x = torch.tensor([3.0], requires_grad=True)
            y = x**3 + 2*x
            y.backward()
            self.log(f"   dy/dx at x=3 (expected 29.0): {x.grad.item()}")

            # Test 3: Linear Layer
            self.log(f"{self.get_elapsed_str()} PyTorch Test 3/4 Neural Network Linear Layer")
            lin = torch.nn.Linear(3, 1)
            inp = torch.randn(2, 3)
            out = lin(inp)
            self.log(f"   Output tensor shape: {out.shape}")

            # Test 4: Eigenvalues
            self.log(f"{self.get_elapsed_str()} PyTorch Test 4/4 Eigenvalue Decomposition")
            e_vals = torch.linalg.eigvals(A)
            self.log(f"   Eigenvalues: {e_vals.numpy()}")

        except Exception as e:
            self.log(f"{self.get_elapsed_str()} PyTorch Test Failed: {str(e)}")
            
        # KERAS TESTS
        try:
            import keras
            import numpy as np
            
            # Test 1: Sequential Model Creation
            self.log(f"{self.get_elapsed_str()} Keras Test 1/3 Building Sequential Model")
            model = keras.Sequential([
                keras.layers.Input(shape=(4,)),
                keras.layers.Dense(8, activation="relu"),
                keras.layers.Dense(1, activation="sigmoid")
            ])
            self.log(f"   Model created successfully with {model.count_params()} parameters")

            # Test 2: Model Compilation & Training
            self.log(f"{self.get_elapsed_str()} Keras Test 2/3 Model Compilation and Training")
            model.compile(optimizer="adam", loss="binary_crossentropy")
            
            X_train = np.random.random((32, 4))
            y_train = np.random.randint(2, size=(32, 1))
            
            history = model.fit(X_train, y_train, epochs=3, batch_size=16, verbose=0)
            final_loss = history.history['loss'][-1]
            self.log(f"   3 epochs training completed. Final loss: {final_loss:.4f}")

            # Test 3: Inference / Prediction
            self.log(f"{self.get_elapsed_str()} Keras Test 3/3 Model Prediction")
            X_test = np.random.random((2, 4))
            preds = model.predict(X_test, verbose=0)
            self.log(f"   Prediction results shape: {preds.shape}")
            for pred in preds:
                self.log(f"   Output probabilities: {pred}")

        except Exception as e:
            self.log(f"{self.get_elapsed_str()} Keras Test Failed: {str(e)}")

        self.log(f"\n{self.get_elapsed_str()} TEST SUITE COMPLETED SUCCESSFULLY")
        self.btn_run.config(state="normal")

if __name__ == "__main__":
    app = AppCheck()
    app.mainloop()

Friday, October 2, 2026

Python 3.13.16 : React frontend and python on Reflex Cloud.

Today, this simple test with artificial intelligence from reflex chat bot show how good and fast can be with one simple task.
Reflex compiles to a React frontend and a Starlette backend—the ASGI server that powers FastAPI. A familiar web stack, accessible to every Python developer. pandas, SQLAlchemy, scikit-learn, ...
After I ask to create a simple G.U.I. interface with all features from reflex, the chat bot result is this:
I built a one-page interactive playground that shows how a React-powered interface responds to Python state. Visitors can try five demos: a counter with event history, live preview controls, form validation, a searchable collection, and a dialog and toast.
Size: about 1,150 lines of Python across the page, four component modules, and four state modules. The integration section describes possibilities; it does not claim that external services are connected.
The interaction tests passed, and the page renders in the browser. I don’t have a reliable elapsed-time measurement for the build, so I can’t give an accurate creation time.
... the time of build was 4 minutes ...
Reflex comes with free features:
  • Publish 1 app on Reflex Cloud
  • Limited free usage for AI generation and hosting
  • Refills periodically
  • Public apps and public GitHub repos
  • App code download and file editing
  • Public sign-in with Reflex Auth
  • Managed database
See the result online : this webpage.

Tuesday, September 29, 2026

Python 3.13.13 : using webz.io with python.

The webz.io can be used with python:
import requests

response = requests.get(
    "https://api.webz.io/api/news?token=YOUR-TOKEN&q=%28category%3A%22economy%2C+business+and+finance%22+OR+category
    %3A%22war%2C+conflict+and+unrest%22+OR+category%3A%22science+and+technology%22+OR+category%3A%22disaster+and+accident%22
    +OR+category%3A%22environment%22+
    OR+category%3A%22labor%22%29&sort=crawled&ts=1790410006113&format=json&size=10&
    webz_reporter=true&includeSyndicated=false&allowNewsHistory=false",
    headers={"Accept": "application/json"},
)
print(response.json())

Monday, September 28, 2026

tkinter : testing RapidFuzz.

RapidFuzz is a fast and powerful Python library used for fuzzy string matching and similarity scoring. It is designed as a drop-in, high-performance alternative to the popular fuzzywuzzy library, offering significantly faster execution speeds because its core algorithms are implemented in C++ (via pybind11).
​What Can RapidFuzz Do?
​Calculate String Similarity:
It measures how similar two strings are using various mathematical distances (such as Levenshtein distance, Hamming distance, Jaro-Winkler, and Normalized Similarity metrics), returning a score typically ranging from 0 (completely different) to 100 (exact match).
​Smart Search & Extraction (process module):
You can query a list of items (such as filenames, database records, or user inputs) and instantly extract the best matches, even if the user query contains typos, misspellings, or partial words.
​Data Cleaning & Deduplication:
It helps clean messy datasets by grouping entries that represent the same entity despite slight spelling variations (e.g., matching "John Smith" with "Jon Smith").
​Lightweight & Efficient:
Unlike heavy deep learning frameworks, RapidFuzz has minimal dependencies and runs smoothly across different environments—making it ideal for desktop applications, servers, and mobile development environments like Pydroid 3.

Sunday, September 27, 2026

tkinter : fast image tool on pydroid3.

Today, one simple example fast tool for images:
Let's see this start source code:
# -*- coding: utf-8 -*-
"""Image Editor - consistent GUI sizes (Pydroid / Android)"""
import os
import shutil
import zipfile
from datetime import datetime
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, font as tkfont
from PIL import Image, ImageTk, ImageOps, ImageEnhance
VALID_EXTS = (".png", ".jpg", ".jpeg", ".bmp", ".webp", ".gif", ".tiff", ".tif")
def get_android_start_dir():
    candidates = [
        "/storage/emulated/0", "/sdcard",
        "/storage/emulated/0/Download", "/sdcard/Download",
        "/storage/emulated/0/DCIM", "/sdcard/DCIM",
        "/storage/emulated/0/Pictures", "/sdcard/Pictures",
        os.path.expanduser("\~"), "/",
    ]
    for path in candidates:
        if os.path.isdir(path) and os.access(path, os.R_OK):
            return path
    return os.getcwd()
...

Saturday, September 26, 2026

tkinter : basic portfolio for market with simulations ...

Today, one simple script tool for market. this allow to set one portofolio and use with simulations.
Let'see some screenshots:
The basic intro source code ...
import datetime
import json
import threading
import urllib.request
import tkinter as tk
from tkinter import ttk, messagebox

import matplotlib
matplotlib.use("TkAgg")
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg


def fetch_yahoo_history(symbol, period_years=1):
    """Descarcă istoricul zilnic de pe Yahoo Finance."""
    end_date = int(datetime.datetime.now().timestamp())
    start_date = int((datetime.datetime.now() - datetime.timedelta(days=365 * period_years)).timestamp())
    
    url = f"https://query1.finance.yahoo.com/v8/finance/chart/{symbol}?period1={start_date}&period2={end_date}&interval=1d"
    req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
    
    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read().decode("utf-8"))
            result = data["chart"]["result"][0]
            timestamps = result["timestamp"]
            closes = result["indicators"]["quote"][0]["close"]
            
            history = []
            for ts, cl in zip(timestamps, closes):
                if cl is not None:
                    dt = datetime.datetime.fromtimestamp(ts)
                    history.append((dt, float(cl)))
            return history
    except Exception as e:
        print(f"Eroare la descărcare {symbol}: {e}")
        return []
        ...

Thursday, September 24, 2026

News : OpenAI python library released.

The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.10+ application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by HTTPX2.

Tuesday, September 22, 2026

News : PyPy new 8.0.0 version.

The PyPy team is proud to release version 8.0.0 of PyPy after the previous release on May 26, 2026. This is a major new version, hence the bump to 8.0.0. It is our first release of Python 3.12, which may still have some bugs so we are calling it "beta" quality.
PyPy is written in RPython, and has code generation to translate RPython into C as part of the VM build process. We have made some improvements to code generation in attempts to speed up the base interpreter.

Monday, September 21, 2026

tkinter : simple marshmallow demo.

About the marshmallow package
  • It checks if your data has the correct fields.
  • It verifies that each field has the right type.
  • It helps you validate JSON before using it.
  • It can convert Python data into clean JSON.
About your this source code:
  • It loads the marshmallow package when the app starts.
  • It reads the JSON you type in the input box.
  • It tries to convert that text into Python data.
  • It validates the data using your marshmallow schema.
  • It shows the result or the error directly in the output box.
  • It does not use print or message boxes.
Why this tool is useful
  • You can test JSON visually.
  • You can see validation errors instantly.
  • You avoid console output.
  • You keep everything inside the GUI.
Let's see the sourve code:
import json
import tkinter as tk
from tkinter import ttk

# ------------------ Package Load Test ------------------

try:
    from marshmallow import Schema, fields, ValidationError
    package_status = "Package marshmallow loaded successfully."
except Exception as e:
    package_status = "PACKAGE LOAD ERROR: " + str(e)


# ------------------ Marshmallow Schema ------------------

class PersonSchema(Schema):
    full_name = fields.Str(required=True)
    city = fields.Str(required=True)
    birth_date = fields.Date(required=True)
    nicknames = fields.List(fields.Str(), required=True)


schema = PersonSchema()


# ------------------ Tkinter UI ------------------

def process_json():
    raw = editor.get("1.0", tk.END).strip()

    output.delete("1.0", tk.END)

    # Step 1: package status
    output.insert(tk.END, package_status + "\n\n")

    # Step 2: show raw JSON
    output.insert(tk.END, "RAW JSON RECEIVED:\n" + raw + "\n\n")

    # Step 3: JSON parsing
    try:
        data = json.loads(raw)
        output.insert(tk.END, "JSON PARSED OK.\n\n")
    except Exception as e:
        output.insert(tk.END, "JSON ERROR:\n" + str(e))
        return

    # Step 4: Marshmallow validation
    try:
        result = schema.dump(data)
        output.insert(tk.END, "SCHEMA VALIDATION OK.\n\n")
    except ValidationError as e:
        output.insert(tk.END, "SCHEMA ERROR:\n" + json.dumps(e.messages, indent=2))
        return

    # Step 5: Final result
    output.insert(tk.END, "FINAL RESULT:\n" + json.dumps(result, indent=2))


root = tk.Tk()
root.title("Marshmallow Diagnostic Full Output")
root.geometry("750x550")

frm = ttk.Frame(root, padding=10)
frm.pack(fill="both", expand=True)

ttk.Label(frm, text="JSON Input:").pack(anchor="w")

editor = tk.Text(frm, height=14, width=90)
editor.pack(fill="x")

editor.insert(
    tk.END,
    json.dumps(
        {
            "full_name": "Catalin George Festila",
            "city": "Falticeni",
            "birth_date": "1976-03-07",
            "nicknames": ["catafest", "mythcat"]
        },
        indent=2
    )
)

ttk.Button(frm, text="Run", command=process_json).pack(pady=10)

ttk.Label(frm, text="Output:").pack(anchor="w")

output = tk.Text(frm, height=14, width=90)
output.pack(fill="both", expand=True)

root.mainloop()

Saturday, September 12, 2026

News : Real-Time Stock & ETF Analyzer in using tvscreener and Tkinter and pydroid3.

The tvscreener is a lightweight Python library designed to extract and analyze real-time financial data directly from TradingView's API without requiring paid API keys or complex authentication. It provides quick access to technical indicators, fundamental metrics, historical performance, and volume data for international stocks and ETFs. Explore the source code and documentation on the official tvscreener GitHub repository. The script provides a desktop interface (Tkinter GUI) powered by tvscreener logic to query market data on demand. This allow to choose a ticker symbol from the dropdown list (such as GOOGL, NVDA, AAPL, or ETFs like VUAA). Clicking Check triggers a background HTTP request without freezing the user interface, while displaying real-time progress via the progress bar. The script formats the API response into four core metric categories: Performance, Technical Indicators, Fundamentals, and Volume and Liquidity. Clicking Copy transfers the complete output directly to your system clipboard for quick sharing or note-taking.

Tuesday, September 1, 2026

News : Inkscape MCP coding agent python module.

With Inkscape MCP, your coding agent can do everything a designer does in Inkscape — sketch and reshape paths, apply effects and filters, render LaTeX equations, generate barcodes and QR codes, convert between formats, query and rewrite document structure — through plain conversation instead of menu clicks.

Tuesday, August 25, 2026

News : win a Golden Ticket with Google Cloud: All-Access Pass to NVIDIA GTC Berlin 2026..

The Google Cloud and NVIDIA teams are excited to announce an opportunity for you to win the ultimate developer experience! We are sending one lucky member from our developer community to NVIDIA GTC Berlin 2026 in Berlin, Germany (Oct 20-22). This is a unique opportunity for our developer community, and we hope you will participate in the contest! Be sure to read all the details below to learn how to enter your submission before the deadline on September 10, 2026.
The "Golden Ticket" winner will receive a premier experience:
✈️ Travel & Pass: One complimentary NVIDIA GTC Berlin Conference Pass to attend in person, including a round-trip travel to Berlin and accommodations. Winners must be able to travel to this event in person to accept the prize.
💻 NVIDIA merchandise gift bag.
⭐ VIP Access: VIP seating for NVIDIA CEO Jensen Huang’s keynote on October 21, 2026.
🧠 Access to NVIDIA community special events.

tkinter : three algorithms for yahoo market with pydroid 3.

The script retrieves historical closing-price data for NVDA and VUAA.AS directly from Yahoo Finance.
First tab displays a candlestick chart for the selected time range. Two Simple Moving Averages are calculated automatically; their periods adapt to the chosen interval (for example SMA 5/10 on short ranges and SMA 50/200 on longer ones) and are shown in the legend. Clear dates appear on the horizontal axis.
Its second tab, ML Predictions, focuses on statistical modelling. The downloaded series is fitted with three algorithms: ordinary linear regression, polynomial regression (degree adjustable by the user), and a Random Forest regressor. All four lines—the original prices plus the three model outputs—are plotted on a single chart. Clear calendar dates appear on the horizontal axis, and an enlarged legend identifies each curve. Loading feedback is shown while data are fetched. The tab therefore converts raw Yahoo price history into immediate visual comparisons of linear, non-linear and ensemble trend estimates.

Monday, August 24, 2026

tkinter : python manager with pydroid 3.

The script is a lightweight package manager for Pydroid 3, built with Tkinter, designed to help you inspect, back up, and restore your Python environment on Android. It reads all installed packages using importlib.metadata, which works reliably in Pydroid 3, and displays them in a scrollable text area. A Check button scans the environment and lists every package with its version, while a Copy button places that list into the clipboard for quick sharing or documentation. The Save function opens a dialog and writes all detected packages into a Requirements.txt file, typically stored in the Download folder, allowing you to preserve the exact state of your environment. The Load function opens a file dialog, reads a requirements file, compares each entry with the currently installed packages, and installs only the missing or outdated ones using pip. Every operation updates a progress bar so you can see how many items have been processed. Overall, the script acts as a practical tool for backing up, restoring, and synchronizing Python packages on Pydroid 3, where traditional virtual environments and desktop-style package managers are not available.

Sunday, August 16, 2026

tkinter : test colorchooser script on pydroid 3.

Learn how to use the Python Tkinter colorchooser module to create interactive graphical interfaces. This simple script opens a native color picker dialog and instantly updates your application interface based on user selection.
How the Python script functions:
The colorchooser module is imported to launch the native system palette window.
The askcolor function runs when triggered and waits for user input.
The script extracts the returned HEX color code from the selection.
The main window background and label text update immediately with the chosen color.
This lightweight code provides a simple way to add dynamic customization to desktop applications without installing external libraries.
Let's see the source code:
import tkinter as tk
from tkinter import colorchooser

def choose_color():
    # Opens the color picker dialog and returns a tuple: ((R, G, B), "#HEX")
    color = colorchooser.askcolor(title="Choose a Background Color")
    
    # Check if the user selected a color (didn't click Cancel)
    if color[1]: 
        hex_code = color[1]
        root.config(bg=hex_code)
        label_color.config(text=f"HEX Code: {hex_code}", bg=hex_code)

# Create the main window
root = tk.Tk()
root.title("Tkinter Colorchooser Example")
root.geometry("300x200")

# Button to trigger the color chooser dialog
btn_choose = tk.Button(root, text="Select Color", command=choose_color)
btn_choose.pack(pady=30)

# Label to display the chosen HEX code
label_color = tk.Label(root, text="No color selected", font=("Arial", 12))
label_color.pack(pady=10)

root.mainloop()

News : Django is moving to an annual release cycle.

Django's Steering Council has accepted the Django Enhancement Proposal DEP 20 to move Django to an annual release cycle. From January 2028, Django will make one feature release a year, giving every feature release the LTS-level three years of support, and version numbers will carry the feature release year: Django 2028, then Django 2029, and so on.

Thursday, August 13, 2026

tkinter : simple tool for converting source code for blogger.

This tool helps you safely prepare source code for publishing on Blogger. You paste your code, press the convert button, and the app instantly transforms it into a format that displays correctly without being interpreted or altered by the platform. It keeps your code clean, readable, and protected. After conversion, you can copy the final result and insert it directly into your blog. It’s a fast and reliable solution for developers who want to share code snippets online.
import html
import tkinter as tk
from tkinter import ttk, messagebox

def paste_from_clipboard():
    try:
        text_input.insert(tk.INSERT, root.clipboard_get())
    except Exception:
        try:
            text_input.focus_set()
            text_input.event_generate("<<Paste>>")
        except Exception:
            pass

def process_code():
    raw_code = text_input.get("1.0", tk.END).strip()
    if not raw_code:
        return

    try:
        # 1. Escapam caracterele speciale HTML (<, >, &, ")
        escaped_code = html.escape(raw_code)

        # 2. Includem codul convertit in structura div -> pre -> code
        formatted_result = f"<div><pre><code>{escaped_code}</code></pre></div>"

        # 3. Afisam rezultatul
        text_output.config(state=tk.NORMAL)
        text_output.delete("1.0", tk.END)
        text_output.insert(tk.END, formatted_result)
        text_output.config(state=tk.DISABLED)
    except Exception as e:
        messagebox.showerror("Eroare", f"A aparut o eroare la conversie: {e}")

def copy_to_clipboard():
    cleaned_text = text_output.get("1.0", tk.END).strip()
    if cleaned_text:
        try:
            root.clipboard_clear()
            root.clipboard_append(cleaned_text)
            messagebox.showinfo("OK", "Copiat in clipboard!")
        except Exception as e:
            messagebox.showerror("Eroare", f"Nu s-a putut copia: {e}")

def clear_all():
    text_input.delete("1.0", tk.END)
    text_output.config(state=tk.NORMAL)
    text_output.delete("1.0", tk.END)
    text_output.config(state=tk.DISABLED)

# Constructie Interfata
root = tk.Tk()
root.title("Code Escaper for Blogger")
root.geometry("400x600")

# Input
lbl1 = ttk.Label(root, text="1. Cod Sursa de Convertit:")
lbl1.pack(anchor="w", padx=10, pady=(10, 0))

frame_btns = ttk.Frame(root)
frame_btns.pack(fill=tk.X, padx=10, pady=5)

btn_paste = ttk.Button(frame_btns, text="Lipeste (Paste)", command=paste_from_clipboard)
btn_paste.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))

btn_clear = ttk.Button(frame_btns, text="Sterge", command=clear_all)
btn_clear.pack(side=tk.RIGHT)

text_input = tk.Text(root, height=8)
text_input.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

# Procesare
btn_process = ttk.Button(root, text="Converteste pentru Blogger", command=process_code)
btn_process.pack(fill=tk.X, padx=10, pady=5)

# Output
lbl2 = ttk.Label(root, text="2. Rezultat de pus in Blogger:")
lbl2.pack(anchor="w", padx=10, pady=(5, 0))

text_output = tk.Text(root, height=8, state=tk.DISABLED)
text_output.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

btn_copy = ttk.Button(root, text="Copiaza Rezultatul", command=copy_to_clipboard)
btn_copy.pack(fill=tk.X, padx=10, pady=(5, 10))

root.mainloop()

Monday, August 10, 2026

tkinter : clean html source code with pydroid 3.

This script is an automated HTML cleaner designed to optimize web content for SEO, specifically tailored for HTML code generated by Blogger image uploads. By removing inline style attributes, CSS style blocks, and class parameters, it cleans clutter to improve the code-to-text ratio, enhancing page loading speed-a key ranking factor.
It strips anchor tags while preserving their internal content, eliminating unwanted external links or internal link equity leaks.
By keeping only structural tags like div and img, it produces lightweight, clean HTML markup that search engine crawlers can index and parse easily.
Let's see the source code:
import tkinter as tk
from tkinter import ttk, messagebox
from bs4 import BeautifulSoup

def paste_from_clipboard():
    try:
        text_input.insert(tk.INSERT, root.clipboard_get())
    except Exception:
        try:
            text_input.focus_set()
            text_input.event_generate("<<Paste>>")
        except Exception:
            pass

def process_html():
    raw_html = text_input.get("1.0", tk.END).strip()
    if not raw_html:
        return

    try:
        soup = BeautifulSoup(raw_html, "html.parser")

        # 1. Eliminare completă tag-uri style și script
        for tag in soup.find_all(["style", "script"]):
            tag.decompose()

        # 2. Eliminare ancore (<a>) și păstrare conținut
        for a_tag in soup.find_all("a"):
            a_tag.unwrap()

        # 3. Eliminare atribute 'style' și 'class' de pe TOATE elementele
        attributes_to_remove = ["style", "class"]
        for tag in soup.find_all(True):
            for attr in attributes_to_remove:
                if attr in tag.attrs:
                    del tag.attrs[attr]

        cleaned_html = str(soup)

        text_output.config(state=tk.NORMAL)
        text_output.delete("1.0", tk.END)
        text_output.insert(tk.END, cleaned_html)
        text_output.config(state=tk.DISABLED)
    except Exception as e:
        messagebox.showerror("Eroare", f"A aparut o eroare la procesare: {e}")

def copy_to_clipboard():
    cleaned_text = text_output.get("1.0", tk.END).strip()
    if cleaned_text:
        try:
            root.clipboard_clear()
            root.clipboard_append(cleaned_text)
            messagebox.showinfo("OK", "Copiat in clipboard!")
        except Exception as e:
            messagebox.showerror("Eroare", f"Nu s-a putut copia: {e}")

def clear_all():
    text_input.delete("1.0", tk.END)
    text_output.config(state=tk.NORMAL)
    text_output.delete("1.0", tk.END)
    text_output.config(state=tk.DISABLED)

# Constructie Interfata
root = tk.Tk()
root.title("HTML Cleaner")
root.geometry("400x600")

# Input
lbl1 = ttk.Label(root, text="1. Sursa HTML:")
lbl1.pack(anchor="w", padx=10, pady=(10, 0))

frame_btns = ttk.Frame(root)
frame_btns.pack(fill=tk.X, padx=10, pady=5)

btn_paste = ttk.Button(frame_btns, text="Lipeste (Paste)", command=paste_from_clipboard)
btn_paste.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))

btn_clear = ttk.Button(frame_btns, text="Sterge", command=clear_all)
btn_clear.pack(side=tk.RIGHT)

text_input = tk.Text(root, height=8)
text_input.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

# Procesare
btn_process = ttk.Button(root, text="Proceseaza HTML", command=process_html)
btn_process.pack(fill=tk.X, padx=10, pady=5)

# Output
lbl2 = ttk.Label(root, text="2. Rezultat Curatat:")
lbl2.pack(anchor="w", padx=10, pady=(5, 0))

text_output = tk.Text(root, height=8, state=tk.DISABLED)
text_output.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

btn_copy = ttk.Button(root, text="Copiaza Rezultatul", command=copy_to_clipboard)
btn_copy.pack(fill=tk.X, padx=10, pady=(5, 10))

root.mainloop()

Sunday, August 9, 2026

tkinter : Acode.log Viewer and Clipboard Tool on Pydroid 3

This Python script creates a clean graphical user interface (GUI) using Tkinter, specifically designed to run inside Pydroid 3 on Android devices. Its main purpose is to automatically locate, open, and display the content of the Acode.log file generated by the popular Acode code editor app.
The program checks several common storage paths used by both the free and paid versions of Acode. Once the log file is found, the full content is loaded into a scrollable text area with horizontal and vertical scrollbars for easy reading of long log files. The complete file path, file size in KB, and total number of lines are shown clearly in the status bar at the bottom of the window.
Users can interact with the application through a simple File menu that includes three essential options: Reload (to refresh the log content), Copy All (to copy the entire log text to the system clipboard with one click), and Exit. Keyboard shortcuts (Ctrl+R, Ctrl+C, Ctrl+Q) are also supported for faster operation. The script handles missing files and read errors gracefully, displaying clear status messages and helpful guidance when the log cannot be located or accessed. It uses UTF-8 encoding with error replacement to ensure special characters do not break the display.
Ideal for Android developers and Acode users who need a fast, lightweight way to inspect application logs directly on their device without leaving the Pydroid 3 environment. The pure Tkinter interface requires no external libraries, making it fully compatible with Pydroid 3 out of the box.
Let's see the source code:
import tkinter as tk
from tkinter import scrolledtext, messagebox, ttk
import os

# Possible paths for Acode.log (paid + free version)
POSSIBLE_PATHS = [
    "/storage/emulated/0/Android/data/com.foxdebug.acode/files/Acode.log",
    "/storage/emulated/0/Android/data/com.foxdebug.acode.free/files/Acode.log",
    "/storage/emulated/0/Android/data/com.foxdebug.acode/files/logs/Acode.log",
    "/storage/emulated/0/Android/data/com.foxdebug.acode.free/files/logs/Acode.log",
    "/sdcard/Android/data/com.foxdebug.acode/files/Acode.log",
    "/sdcard/Android/data/com.foxdebug.acode.free/files/Acode.log",
]

class AcodeLogViewer:
    def __init__(self, root):
        self.root = root
        self.root.title("Acode.log Viewer - Pydroid 3")
        self.root.geometry("900x650")
        self.root.minsize(600, 400)

        self.found_path = None
        self.file_content = ""

        self.create_menu()
        self.create_widgets()
        self.search_and_load()

    def create_menu(self):
        """Create the main application menu."""
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)

        # File menu
        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="File", menu=file_menu)
        file_menu.add_command(label="Reload", command=self.search_and_load, accelerator="Ctrl+R")
        file_menu.add_command(label="Copy All", command=self.copy_to_clipboard, accelerator="Ctrl+C")
        file_menu.add_separator()
        file_menu.add_command(label="Exit", command=self.root.quit, accelerator="Ctrl+Q")

        # Keyboard shortcuts
        self.root.bind("", lambda e: self.search_and_load())
        self.root.bind("", lambda e: self.copy_to_clipboard())
        self.root.bind("", lambda e: self.root.quit())

    def create_widgets(self):
        # Text area with scrollbars
        text_frame = ttk.Frame(self.root, padding=8)
        text_frame.pack(fill=tk.BOTH, expand=True)

        self.text_area = scrolledtext.ScrolledText(
            text_frame,
            wrap=tk.NONE,
            font=("Courier New", 10),
            state=tk.DISABLED
        )
        self.text_area.pack(fill=tk.BOTH, expand=True)

        # Horizontal scrollbar
        h_scroll = ttk.Scrollbar(text_frame, orient=tk.HORIZONTAL, command=self.text_area.xview)
        h_scroll.pack(fill=tk.X)
        self.text_area.configure(xscrollcommand=h_scroll.set)

        # Status bar – displays the found file path
        self.status_var = tk.StringVar(value="Ready.")
        status_bar = ttk.Label(
            self.root,
            textvariable=self.status_var,
            relief=tk.SUNKEN,
            anchor=tk.W,
            padding=(6, 3)
        )
        status_bar.pack(fill=tk.X, side=tk.BOTTOM)

    def search_and_load(self):
        self.found_path = None
        self.file_content = ""

        for path in POSSIBLE_PATHS:
            if os.path.isfile(path):
                self.found_path = path
                break

        if self.found_path is None:
            self.status_var.set("Acode.log file not found in the known locations.")
            self.display_text(
                "The Acode.log file could not be located.\n\n"
                "Please verify that the Acode application has already generated the log "
                "and that Pydroid 3 has storage access permissions."
            )
            return

        try:
            with open(self.found_path, "r", encoding="utf-8", errors="replace") as f:
                self.file_content = f.read()

            size_kb = len(self.file_content.encode("utf-8")) / 1024
            lines = len(self.file_content.splitlines())

            # Full path + additional information in the status bar
            self.status_var.set(
                f"{self.found_path}  •  {size_kb:.1f} KB  •  {lines} lines"
            )
            self.display_text(self.file_content)

        except Exception as e:
            self.status_var.set(f"Error reading file: {self.found_path}")
            self.display_text(f"Unable to read the file:\n{str(e)}")

    def display_text(self, content):
        self.text_area.config(state=tk.NORMAL)
        self.text_area.delete("1.0", tk.END)
        self.text_area.insert(tk.END, content)
        self.text_area.config(state=tk.DISABLED)
        self.text_area.see("1.0")

    def copy_to_clipboard(self):
        if not self.file_content:
            messagebox.showwarning("Clipboard", "There is no content to copy.")
            return

        try:
            self.root.clipboard_clear()
            self.root.clipboard_append(self.file_content)
            self.root.update()
            self.status_var.set(f"Content copied • {self.found_path or 'N/A'}")
            messagebox.showinfo("Clipboard", "The entire log content has been copied to the clipboard.")
        except Exception as e:
            messagebox.showerror("Clipboard Error", f"Unable to copy to clipboard:\n{str(e)}")


if __name__ == "__main__":
    root = tk.Tk()
    app = AcodeLogViewer(root)
    root.mainloop()

Friday, August 7, 2026

News : Django 6.1 release ...

These release notes cover the new features, as well as some backwards incompatible changes you’ll want to be aware of when upgrading from Django 6.0 or earlier. We’ve begun the deprecation process for some features.

Tuesday, August 4, 2026

News : Python 3.15.0 candidate ...

This is a candidate preview of Python 3.15
This release, 3.15.0rc1, is the first of two planned candidate releases. Entering the release candidate phase, only reviewed code changes which are clear bug fixes are allowed between this release candidate and the final release.
The next release of Python 3.15 will be 3.15.0rc2, scheduled for 2026-09-01.

Monday, August 3, 2026

tkinter : simple rss feeds reader on pydroid 3.

Today, this is a simple rss feeds reader with tkinter on pydroid I.D.E. android. The source code is very simple and get feeds from web. See the result:
import tkinter as tk
from tkinter import messagebox, ttk
import webbrowser
import feedparser

RSS_URL = "https://blog.python.org/feeds/posts/default"

article_links = []


def clean_text(text):
    """Converts text to basic ASCII to strip non-supported Android Tkinter characters."""
    if not text:
        return ""
    # Encodes to ASCII ignoring emojis, special quotes, and unsupported byte sequences
    clean = text.encode("ascii", "ignore").decode("ascii")
    # Removes extra whitespace and linebreaks
    return " ".join(clean.split())


def load_feed():
    global article_links

    listbox.delete(0, tk.END)
    article_links.clear()

    status_label.config(text="Fetching articles...", foreground="blue")
    root.update()

    try:
        feed = feedparser.parse(RSS_URL)

        if not feed.entries:
            status_label.config(text="No articles found.", foreground="red")
            return

        for entry in feed.entries:
            title_raw = entry.get("title", "Untitled")
            title = clean_text(title_raw)
            link = entry.get("link", "")

            listbox.insert(tk.END, title)
            article_links.append(link)

        status_label.config(
            text=f"Loaded {len(feed.entries)} articles!", foreground="green"
        )

    except Exception as e:
        status_label.config(text="Connection error!", foreground="red")
        messagebox.showerror("Error", f"Failed to load feed:\n{e}")


def open_link(event):
    try:
        index = listbox.curselection()[0]
        url = article_links[index]
        if url:
            webbrowser.open(url)
    except IndexError:
        pass


# --- GUI Setup ---
root = tk.Tk()
root.title("Python Foundation RSS Reader")
root.geometry("600x800")

title_label = ttk.Label(
    root, text="Python Software Foundation Blog", font=("Helvetica", 14, "bold")
)
title_label.pack(pady=10)

refresh_btn = ttk.Button(root, text="Refresh", command=load_feed)
refresh_btn.pack(pady=5)

status_label = ttk.Label(
    root, text="Connecting...", font=("Helvetica", 10)
)
status_label.pack(pady=5)

frame = ttk.Frame(root)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL)

listbox = tk.Listbox(
    frame,
    font=("Helvetica", 11),
    selectbackground="#007ACC",
    selectforeground="white",
    activestyle="none",
    yscrollcommand=scrollbar.set,
    bd=1,
    relief="solid",
)

scrollbar.config(command=listbox.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

listbox.bind("<>", open_link)

root.after(500, load_feed)
root.mainloop()

Saturday, August 1, 2026

tkinter : tau package on pydroid 3

Today, I test the tau python package with tkinter on pydroid3 on my phone.
/div>
Let's see the source code;
div>
import os
import platform
import subprocess
import sys
import tkinter as tk
from tkinter import ttk

def get_android_prop(prop_name):
    """Citește o proprietate de sistem Android folosind comanda getprop."""
    try:
        val = (
            subprocess.check_output(f"getprop {prop_name}", shell=True, text=True)
            .strip()
        )
        return val if val else "N/A"
    except Exception:
        return "Indisponibil"


def get_system_info():
    """Colectează informații extinse despre telefon și modulul Tau."""
    # 1. Verificare modul Tau
    tau_info = "Neinstalat"
    try:
        import tau

        tau_info = getattr(tau, "__version__", "Instalat (fără __version__)")
    except ImportError:
        tau_info = "Pachetul 'tau-ai' nu este instalat"

    # 2. Preluare date extinse Android
    model = get_android_prop("ro.product.model")
    brand = get_android_prop("ro.product.brand")
    manufacturer = get_android_prop("ro.product.manufacturer")
    android_version = get_android_prop("ro.build.version.release")
    sdk_version = get_android_prop("ro.build.version.sdk")
    device_board = get_android_prop("ro.product.board")
    hardware = get_android_prop("ro.hardware")

    info = {
        "Versiune Tau": tau_info,
        "Producător": f"{manufacturer.capitalize()} ({brand.capitalize()})",
        "Model Telefon": model,
        "Versiune Android": f"Android {android_version} (API {sdk_version})",
        "Placă / Hardware": f"{device_board} / {hardware}",
        "Arhitectură CPU": platform.machine(),
        "Sistem Python": f"{platform.system()} {platform.release()}",
        "Versiune Python": sys.version.split()[0],
    }
    return info


def main():
    root = tk.Tk()
    root.title("Tau & Detalii Android")
    root.geometry("420x560")
    root.configure(bg="#212121")

    title = tk.Label(
        root,
        text="Informații Dispozitiv & Tau",
        font=("Helvetica", 15, "bold"),
        fg="#00E676",
        bg="#212121",
        pady=12,
    )
    title.pack()

    # Zonă de text pentru date
    text_area = tk.Text(
        root,
        font=("Courier", 10),
        bg="#303030",
        fg="#FFFFFF",
        padx=10,
        pady=10,
        relief=tk.FLAT,
    )
    text_area.pack(fill=tk.BOTH, expand=True, padx=15, pady=5)

    # Inserare date în fereastră
    info_data = get_system_info()
    text_content = "=== SPECIFICAȚII TELEFON & APP ===\n\n"
    for key, value in info_data.items():
        text_content += f"• {key}:\n  {value}\n\n"

    text_area.insert(tk.END, text_content)
    text_area.config(state=tk.DISABLED)

    # Buton de închidere
    btn_close = tk.Button(
        root,
        text="Închide",
        font=("Helvetica", 11, "bold"),
        bg="#FF5252",
        fg="white",
        activebackground="#FF1744",
        activeforeground="white",
        command=root.destroy,
        pady=8,
    )
    btn_close.pack(fill=tk.X, padx=15, pady=15)
    root.mainloop()
if __name__ == "__main__":
    main()

Thursday, July 30, 2026

tkinter : mini rss for NVDA

Today, I started new post with tkinter label. This is a demo real with pydroid3 android application to get data from rss and show me NVDA and more.
The tkinter package (“Tk interface”) is the standard Python interface to the Tcl/Tk GUI toolkit. Both Tk and tkinter are available on most Unix platforms, including macOS, as well as on Windows systems.

Tuesday, July 28, 2026

Python : djxi Django library .

djxi is a Django library that eliminates the HTMX scattering problem: it bundles URL patterns, view logic, and HTML template sections for a feature into a single DXEndpointBattery class, keeping all related code in one place.

Monday, July 27, 2026

Python : KivyMD on pydroid android I.D.E.

This Python script is a cross-platform mobile application built with KivyMD, a Material Design framework extension for Python's Kivy library. It combines a declarative user interface layout defined via Kivy Language (KV string) with Python event handling to create an interactive task management dashboard tailored for Android environments like Pydroid 3.

Saturday, July 25, 2026

Python : simple educational planisphere

This Python script functions as an educational planisphere designed for telescope observation. Instead of taking a live snapshot of the current sky (where most events would be hidden below the horizon during the day or off-season), it calculates and displays the expected positions of major astronomical events through late 2026—such as meteor shower radiants and NEO asteroid flybys—relative to Fălticeni, Romania (47.46^\circ\text{ N}, 26.30^\circ\text{ E}).
Oriented with North at the top, the code converts equatorial coordinates (Right Ascension and Declination) into local horizon coordinates (Azimuth and Altitude). It then projects these targets onto a 2D polar plot, indicating the cardinal direction (N, E, S, W) to point your telescope and labeling each event with its peak date and estimated rise time, before automatically exporting the chart as a high-resolution PNG image to your device's Download folder.
Let's see source code:
import os
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime, timezone

def draw_and_save_sky_map():
    # Coordonate Fălticeni, România
    LAT = 47.46
    LON = 26.30

    # Calendarul evenimentelor până la sfârșitul anului 2026
    events = [
        {
            'name': 'Perseide (Vârf)',
            'date_str': '12 Aug',
            'rise_time': '22:00',
            'ra': 3.07, 'dec': 58.0,
            'color': '#00ffcc', 'marker': '*'
        },
        {
            'name': 'Orionide (Vârf)',
            'date_str': '21 Oct',
            'rise_time': '23:30',
            'ra': 6.34, 'dec': 16.0,
            'color': '#ff9900', 'marker': '*'
        },
        {
            'name': 'Leonide (Vârf)',
            'date_str': '17 Noi',
            'rise_time': '00:15',
            'ra': 10.2, 'dec': 22.0,
            'color': '#ff3366', 'marker': '*'
        },
        {
            'name': 'Geminide (Vârf)',
            'date_str': '13 Dec',
            'rise_time': '20:30',
            'ra': 7.46, 'dec': 33.0,
            'color': '#ffff00', 'marker': '*'
        },
        {
            'name': 'Asteroid NEO 2026',
            'date_str': 'Toamnă 2026',
            'rise_time': '21:00',
            'ra': 14.5, 'dec': 10.0,
            'color': '#ff00ff', 'marker': 's'
        }
    ]

    # Calcul Timp Sidereal Local (LST)
    now_utc = datetime.now(timezone.utc)
    j2000_epoch = datetime(2000, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
    days_since_j2000 = (now_utc - j2000_epoch).total_seconds() / 86400.0
    lst = (18.697374558 + 24.06570982441908 * days_since_j2000 + LON / 15.0) % 24

    # Creare grafic Matplotlib (Polar)
    fig = plt.figure(figsize=(8, 8), facecolor='#0b0d17')
    ax = fig.add_subplot(111, polar=True, facecolor='#05070f')

    # Orientare fixă: NORDUL SUS (0°)
    ax.set_theta_zero_location('N') 
    ax.set_theta_direction(-1)      
    ax.set_rlim(0, 90)              
    ax.set_yticklabels([])

    lat_rad = np.radians(LAT)

    for item in events:
        ha = (lst - item['ra']) * 15.0
        ha_rad = np.radians(ha)
        dec_rad = np.radians(item['dec'])

        sin_alt = np.sin(dec_rad) * np.sin(lat_rad) + np.cos(dec_rad) * np.cos(lat_rad) * np.cos(ha_rad)
        alt = np.degrees(np.arcsin(np.clip(sin_alt, -1.0, 1.0)))

        if alt <= 5:
            alt = 35.0  

        cos_az = (np.sin(dec_rad) - np.sin(lat_rad) * sin_alt) / (np.cos(lat_rad) * np.sin(np.arccos(sin_alt)))
        az = np.degrees(np.arccos(np.clip(cos_az, -1.0, 1.0)))
        if np.sin(ha_rad) > 0:
            az = 360 - az

        theta = np.radians(az)
        r = 90 - alt

        ax.plot(theta, r, marker=item['marker'], markersize=12, color=item['color'])
        label_text = f"{item['name']}\n[{item['date_str']}]\nRăsare ~{item['rise_time']}"
        ax.text(theta, r + 7, label_text, color=item['color'], 
                fontsize=7.5, ha='center', fontweight='bold')

    cardinals = [('NORD (0°)', 0), ('EST (90°)', 90), ('SUD (180°)', 180), ('VEST (270°)', 270)]
    for label, angle in cardinals:
        ax.text(np.radians(angle), 102, label, color='white', fontsize=9, fontweight='bold', ha='center', va='center')

    ax.set_title("HARTĂ EVENIMENTE ASTRONOMICE 2026 (Aug - Dec)\nLocație: Fălticeni | Orientare: Nordul Sus\n", 
                 color='white', fontsize=9, pad=15)

    ax.grid(color='#1a233a', linestyle=':', linewidth=0.8)
    plt.tight_layout()

    # --- SALVARE IMAGINE ÎN FOLDERUL DOWNLOAD ---
    download_path = "/sdcard/Download"
    
    # Alternativă în caz că calea /sdcard nu este mapată la fel pe unele versiuni de Android
    if not os.path.exists(download_path):
        download_path = os.path.expanduser("~/storage/downloads")

    file_name = f"harta_astronomica_2026_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
    full_path = os.path.join(download_path, file_name)

    try:
        plt.savefig(full_path, dpi=300, facecolor=fig.get_facecolor(), edgecolor='none')
        print(f" Imaginea a fost salvată cu succes la:\n{full_path}")
    except Exception as e:
        print(f" Eroare la salvarea fișierului: {e}")

    plt.show()

if __name__ == "__main__":
    draw_and_save_sky_map()

Tuesday, July 21, 2026

News : Quantum Computing 101: How to Get Started

Python 3.10.11 : testing default example with the pyui-0.1.0 python module.

A declarative, cross-platform GUI framework (ala SwiftUI) written in Python using SDL2. It provides tools and libraries that enable developers to create and manage user interfaces for desktop apps.
You need to install the pyui python module and the SDK with the pip tool.
python -m pip install pyui
Collecting pyui
  Downloading pyui-0.1.0-py3-none-any.whl.metadata (893 bytes)
Collecting PySDL2>=0.9.7 (from pyui)
  Downloading PySDL2-0.9.17-py3-none-any.whl.metadata (3.8 kB)
Downloading pyui-0.1.0-py3-none-any.whl (6.0 MB)
   ---------------------------------------- 6.0/6.0 MB 6.0 MB/s  0:00:01
Downloading PySDL2-0.9.17-py3-none-any.whl (583 kB)
   ---------------------------------------- 583.1/583.1 kB 2.1 MB/s  0:00:00
Installing collected packages: PySDL2, pyui
Successfully installed PySDL2-0.9.17 pyui-0.1.0
python -m pip install pysdl2-dll
Collecting pysdl2-dll
  Downloading pysdl2_dll-2.32.10-py2.py3-none-win_amd64.whl.metadata (4.7 kB)
Downloading pysdl2_dll-2.32.10-py2.py3-none-win_amd64.whl (4.1 MB)
   ---------------------------------------- 4.1/4.1 MB 4.1 MB/s  0:00:01
Installing collected packages: pysdl2-dll
Successfully installed pysdl2-dll-2.32.10
Let's see the default example with one grid.
Let's see the source code:
import pyui

class ItemGridView(pyui.View):
    def content(self):
        yield pyui.ScrollView(axis=self.axis)(
            pyui.Grid(num=self.num, size=self.size, axis=self.axis, flex=self.flex)(
                pyui.ForEach(
                    range(self.item_count),
                    lambda num: (
                        pyui.Rectangle()(pyui.Text(num + 1).color(255, 255, 255))
                        .background(120, 120, 120)
                        .radius(5)
                        .animate()
                    ),
                )
            )
        )

class GridTest(pyui.View):
    axis = pyui.State(default=1)
    item_count = pyui.State(int, default=50)
    size = pyui.State(default=100)
    num = pyui.State(default=4)
    size_or_num = pyui.State(default=0)
    flex = pyui.State(default=False)

    def content(self):
        if self.size_or_num.value == 0:
            size = None
            num = self.num.value
        else:
            size = self.size.value
            num = None
        yield pyui.HStack(alignment=pyui.Alignment.LEADING)(
            pyui.VStack(alignment=pyui.Alignment.LEADING)(
                pyui.Text("Axis"),
                pyui.SegmentedButton(self.axis)(
                    pyui.Text(pyui.Axis.HORIZONTAL.name),
                    pyui.Text(pyui.Axis.VERTICAL.name),
                ),
                pyui.HStack(
                    pyui.Text("Number of items"),
                    pyui.Spacer(),
                    pyui.Text(self.item_count.value)
                    .color(128, 128, 128)
                    .priority("high"),
                ),
                pyui.Slider(self.item_count, maximum=200),
                pyui.Text("Fill rows/columns by"),
                pyui.SegmentedButton(self.size_or_num)(
                    pyui.Text("Number"),
                    pyui.Text("Size"),
                ),
                pyui.HStack(
                    pyui.Text("Items per row/column"),
                    pyui.Spacer(),
                    pyui.Text(self.num.value).color(128, 128, 128).priority("high"),
                ),
                pyui.Slider(self.num, minimum=1, maximum=10).disable(
                    self.size_or_num.value == 1
                ),
                pyui.HStack(
                    pyui.Text("Item size"),
                    pyui.Spacer(),
                    pyui.Text(self.size.value).color(128, 128, 128).priority("high"),
                ),
                pyui.Slider(self.size, minimum=50, maximum=200).disable(
                    self.size_or_num.value == 0
                ),
                pyui.Toggle(self.flex, label="Adjust size to fit").disable(
                    self.size_or_num.value == 0
                ),
            )
            .padding(10)
            .size(width=300),
            ItemGridView(
                item_count=self.item_count.value,
                size=size,
                num=num,
                axis=self.axis.value,
                flex=self.flex.value,
            ),
        )

if __name__ == "__main__":
    app = pyui.Application("io.temp.GridTest")
    app.window("Grid Tester", GridTest())
    app.run()

Saturday, July 18, 2026

Python Qt : testing Browserless.io feature ...

The provided PyQt6 application is a desktop tool that allows a user to input any website address and instantly obtain a full‑page screenshot of that site using the Browserless.io cloud rendering service; the program begins by creating a simple graphical interface composed of a text field for entering a URL, a button that triggers the screenshot process, and a display area where the resulting image is shown, all arranged vertically for clarity and ease of use. When the user presses the button, the application reads the typed URL, constructs a request to the Browserless screenshot API using the user’s authentication token, and sends a JSON payload instructing Browserless to load the page and capture it in full, not just the visible viewport. Browserless processes the request by launching a headless Chromium instance, navigating to the specified website, rendering the page completely—including dynamic content—and returning a PNG image representing the entire scrollable webpage. The application receives this binary PNG data through an HTTP response, converts it into a QPixmap object, and displays it directly inside the PyQt window, effectively turning the GUI into a live screenshot viewer. This design allows developers to visually inspect websites without embedding a browser engine locally, relying instead on Browserless’s remote infrastructure to handle rendering, JavaScript execution, and screenshot generation. The code is intentionally minimal, focusing on clarity and functionality: it uses only standard PyQt widgets, avoids unnecessary complexity, and demonstrates how to integrate cloud‑based browser automation into a Python desktop application. The result is a lightweight tool that can preview websites, capture full‑page screenshots, and serve as a foundation for more advanced features such as zooming, saving images, asynchronous loading, or automated website testing workflows.
This script was created with copilot artificial intelligence, and tested by me.
import sys
import requests
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QPushButton, QLabel
from PyQt6.QtGui import QPixmap
from PyQt6.QtCore import Qt

# PUNE TOKEN-UL TAU AICI, CA STRING
BROWSERLESS_TOKEN = "fill_TOKEN_official__website"

class BrowserlessViewer(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Browserless Web Screenshot Viewer")
        self.resize(900, 700)

        layout = QVBoxLayout(self)

        self.url_edit = QLineEdit()
        self.url_edit.setPlaceholderText("Introdu adresa web...")
        layout.addWidget(self.url_edit)

        self.btn = QPushButton("Generează Screenshot")
        self.btn.clicked.connect(self.take_screenshot)
        layout.addWidget(self.btn)

        self.canvas = QLabel()
        self.canvas.setAlignment(Qt.AlignmentFlag.AlignCenter)
        layout.addWidget(self.canvas)

    def take_screenshot(self):
        url = self.url_edit.text().strip()
        if not url:
            return

        api_url = f"https://chrome.browserless.io/screenshot?token={BROWSERLESS_TOKEN}"

        payload = {
            "url": url,
            "options": {
                "fullPage": True
            }
        }

        try:
            r = requests.post(api_url, json=payload)
            r.raise_for_status()

            pixmap = QPixmap()
            pixmap.loadFromData(r.content)
            self.canvas.setPixmap(pixmap)

        except Exception as e:
            print("Eroare:", e)

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

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