analitics

Pages

Showing posts with label sys. Show all posts
Showing posts with label sys. Show all posts

Tuesday, June 16, 2026

Python Qt : Network intrusion detection tool with alerts and real‑time HTTP attack detection.

This Python script acts as a local security monitor that watches incoming HTTP requests and alerts the user when suspicious activity is detected. It runs a lightweight HTTP server and inspects every GET and POST request for signs of intrusion, such as dangerous protocols (gopher://), command‑execution keywords (bash, powershell, cmd.exe), encoded payloads, or other attack patterns. Before scanning the request, the script verifies that the incoming connection comes from a specific trusted IP and MAC address. This prevents unauthorized devices from interacting with the server. If the IP or MAC does not match the configured values, the request is immediately rejected.
When a malicious pattern is detected, the script logs the event and displays a popup alert window on the user’s desktop. This popup is implemented using PyQt6 and appears as a small, always‑on‑top notification in the corner of the screen. It fades out automatically after a few seconds, ensuring the user is visually warned without relying on Windows system notifications.
The script also includes a tray icon with a menu that allows the user to update the trusted IP and MAC address. All events are written to a log file for later review. Overall, it functions as a simple intrusion‑detection and alerting tool for local network traffic.
This is a groper test on my browser and result was:
Let's see the source code:
import re
import threading
import queue
import subprocess
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
from datetime import datetime
import sys
from urllib.parse import unquote

# FIX pentru Windows 10/11 – AppID
import ctypes
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("Python.SecurityMonitor")

from PyQt6.QtWidgets import (
    QApplication, QSystemTrayIcon, QMenu, QDialog,
    QVBoxLayout, QLabel, QLineEdit, QPushButton, QWidget
)
from PyQt6.QtGui import QIcon
from PyQt6.QtCore import QTimer, Qt, QPropertyAnimation

# ---------------- CONFIG ---------------- #

ALERT_QUEUE = queue.Queue()
LOG_FILE = "security_log.txt"

MONITOR_IP = "192.168.0.136"
MONITOR_MAC = "60:45:cb:c3:4d:02"   # normalizat

RULES = {
    "Gopher protocol": r"gopher://",
    "CRLF injection": r"%0d%0a",
    "WinRM port": r"5985",
    "Base64 payload": r"base64",
    "bash execution": r"bash",
    "powershell execution": r"powershell",
    "cmd execution": r"cmd\.exe",
}

# ---------------------------------------- #

def log_event(text: str):
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    line = f"[{ts}] {text}\n"
    with open(LOG_FILE, "a", encoding="utf-8") as f:
        f.write(line)
    print(line, end="")

def get_mac(ip):
    try:
        output = subprocess.check_output(["arp", "-a", ip], text=True)
        for line in output.splitlines():
            if ip in line:
                mac = line.split()[1]
                mac = mac.replace("-", ":").lower()
                return mac
    except:
        return None

# ---------------- POPUP ALERT ---------------- #

class AlertPopup(QWidget):
    def __init__(self, message):
        super().__init__()
        self.setWindowFlags(
            Qt.WindowType.FramelessWindowHint |
            Qt.WindowType.Tool |
            Qt.WindowType.WindowStaysOnTopHint
        )

        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)

        layout = QVBoxLayout()
        label = QLabel(message)
        label.setStyleSheet("""
            background-color: #CC0000;
            color: white;
            padding: 12px;
            border-radius: 8px;
            font-size: 14px;
        """)
        layout.addWidget(label)
        self.setLayout(layout)

        self.resize(350, 80)

        screen = QApplication.primaryScreen().geometry()
        self.move(screen.width() - 380, screen.height() - 150)

        self.show()

        self.anim = QPropertyAnimation(self, b"windowOpacity")
        self.anim.setDuration(6000)
        self.anim.setStartValue(1.0)
        self.anim.setEndValue(0.0)
        self.anim.finished.connect(self.close)
        self.anim.start()

# ---------------- HTTP SERVER ---------------- #

class SecurityHandler(BaseHTTPRequestHandler):

    def _check_ip_mac(self):
        global MONITOR_IP, MONITOR_MAC

        client_ip = self.client_address[0]
        # bypass MAC check dacă este același PC
        if client_ip == MONITOR_IP:
            return True, None

        if client_ip != MONITOR_IP:
            return False, f"IP neautorizat: {client_ip}"

        mac = get_mac(client_ip)
        if mac is None:
            return False, f"MAC necunoscut pentru {client_ip}"

        if mac.lower() != MONITOR_MAC.lower():
            return False, f"MAC neautorizat: {mac} pentru IP {client_ip}"

        return True, None

    def _scan_and_block(self, source: str, data: str):
        data_lower = data.lower()
        for reason, pattern in RULES.items():
            if re.search(pattern, data_lower):
                msg = f"Blocked {source}: {reason} → {data}"
                log_event(msg)
                ALERT_QUEUE.put(msg)

                self.send_response(403)
                self.end_headers()
                self.wfile.write(f"Cerere blocată: {reason}".encode("utf-8"))
                return True
        return False

    def do_GET(self):
        ok, reason = self._check_ip_mac()
        if not ok:
            ALERT_QUEUE.put(reason)
            log_event(reason)
            self.send_response(403)
            self.end_headers()
            self.wfile.write(reason.encode())
            return

        url = unquote(self.path)

        if self._scan_and_block("GET URL", url):
            return

        self.send_response(200)
        self.end_headers()
        self.wfile.write(b"Cerere GET permisa")
        log_event(f"Allowed GET: {url}")

    def do_POST(self):
        ok, reason = self._check_ip_mac()
        if not ok:
            ALERT_QUEUE.put(reason)
            log_event(reason)
            self.send_response(403)
            self.end_headers()
            self.wfile.write(reason.encode())
            return

        url = unquote(self.path)

        content_length = int(self.headers.get("Content-Length", 0))
        body = unquote(self.rfile.read(content_length).decode("utf-8", errors="ignore"))

        if self._scan_and_block("POST URL", url):
            return
        if self._scan_and_block("POST body", body):
            return

        self.send_response(200)
        self.end_headers()
        self.wfile.write(b"Cerere POST permisa")
        log_event(f"Allowed POST: {url} | body: {body[:200]}")

def run_server():
    server = HTTPServer(("0.0.0.0", 8080), SecurityHandler)
    log_event("Security server running on http://0.0.0.0:8080")
    server.serve_forever()

# ---------------- DIALOG IP & MAC ---------------- #

class IpMacDialog(QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Set IP & MAC")

        layout = QVBoxLayout()

        self.ip_label = QLabel("IP Address:")
        self.ip_input = QLineEdit()
        self.ip_input.setText(MONITOR_IP)

        self.mac_label = QLabel("MAC Address:")
        self.mac_input = QLineEdit()
        self.mac_input.setText(MONITOR_MAC)

        self.save_btn = QPushButton("Save")
        self.save_btn.clicked.connect(self.save_values)

        layout.addWidget(self.ip_label)
        layout.addWidget(self.ip_input)
        layout.addWidget(self.mac_label)
        layout.addWidget(self.mac_input)
        layout.addWidget(self.save_btn)

        self.setLayout(layout)

    def save_values(self):
        global MONITOR_IP, MONITOR_MAC
        MONITOR_IP = self.ip_input.text().strip()
        MONITOR_MAC = self.mac_input.text().strip().replace("-", ":").lower()
        log_event(f"Updated IP/MAC → IP={MONITOR_IP}, MAC={MONITOR_MAC}")
        self.hide()

# ---------------- TRAY APP ---------------- #

class TrayApp:
    def __init__(self):
        self.app = QApplication(sys.argv)

        icon = QIcon("icon.svg") if os.path.exists("icon.svg") else QIcon()
        self.tray = QSystemTrayIcon(icon)
        self.tray.setToolTip("Python Security Monitor")

        menu = QMenu()

        set_ip_mac = menu.addAction("Set IP & MAC")
        set_ip_mac.triggered.connect(self.open_ip_mac_dialog)

        exit_action = menu.addAction("Exit")
        exit_action.triggered.connect(self.app.quit)

        self.tray.setContextMenu(menu)
        self.tray.show()

        self.dialog = None

        self.timer = QTimer()
        self.timer.timeout.connect(self.check_alerts)
        self.timer.start(300)

    def open_ip_mac_dialog(self):
        if self.dialog is None:
            self.dialog = IpMacDialog()
        self.dialog.show()
        self.dialog.raise_()
        self.dialog.activateWindow()

    def check_alerts(self):
        while not ALERT_QUEUE.empty():
            msg = ALERT_QUEUE.get()
            AlertPopup(msg)

    def run(self):
        self.app.exec()

# ---------------- MAIN ---------------- #

def main():
    server_thread = threading.Thread(target=run_server, daemon=True)
    server_thread.start()

    tray = TrayApp()
    tray.run()

if __name__ == "__main__":
    main()

Saturday, June 13, 2026

Python Qt : Parse Google Finance with PyQt6 and BeautifulSoup.

This Python script is a web scraper designed to extract the real-time stock price of GoldMoney Inc. (TSE:XAU) from Google Finance using PyQt6 and BeautifulSoup. Because Google Finance often displays a cookie consent pop-up that blocks data extraction, the script initializes a headless-like browser instance using QWebEngineView. It embeds a custom, silent web page class to suppress noisy JavaScript console errors in the terminal. Once the page loads, it automatically injects and executes a JavaScript snippet to find and click the "Accept all" or "Acceptă tot" consent buttons. After a short delay to allow the content to refresh, the script captures the updated HTML source, uses BeautifulSoup to locate the specific price elements via robust CSS selectors, prints the cleaned financial data to the terminal, and cleanly terminates the application.
I used artificial intelligence from Google Finance ... REZULTAT SCRAPING (CURAT) ... strange comments:
Let's see:
from PyQt6.QtWidgets import QApplication
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWebEngineCore import QWebEnginePage
from PyQt6.QtCore import QUrl, QTimer
from bs4 import BeautifulSoup
import sys

# Clasă custom pentru pagină care ignoră mesajele de consolă
class SilentWebPage(QWebEnginePage):
    def javaScriptConsoleMessage(self, level, message, lineID, sourceID):
        # Returnăm nimic pentru a nu afișa mesajele de tip "js: Error..." în terminal
        pass

class Scraper(QWebEngineView):
    def __init__(self, url):
        self.app = QApplication.instance() or QApplication(sys.argv)
        super().__init__()
        
        # Setăm pagina custom care reduce la tăcere erorile JS
        self.setPage(SilentWebPage(self))
        
        self.loadFinished.connect(self._on_load_finished)
        self.setUrl(QUrl(url))
        self.app.exec()

    def _on_load_finished(self, ok):
        if ok:
            js_code = """
            (function() {
                let buttons = document.querySelectorAll('button');
                for (let i = 0; i < buttons.length; i++) {
                    let text = buttons[i].innerText;
                    if (text.includes('Accept all') || text.includes('Acceptă tot')) {
                        buttons[i].click();
                        return true;
                    }
                }
                return false;
            })();
            """
            self.page().runJavaScript(js_code, self._after_consent)
        else:
            self.app.quit()

    def _after_consent(self, clicked):
        wait_time = 4000 if clicked else 1500
        QTimer.singleShot(wait_time, self._extract_html)

    def _extract_html(self):
        self.page().toHtml(self.parse_finance_data)

    def parse_finance_data(self, html):
        soup = BeautifulSoup(html, 'html.parser')
        try:
            # Selectorul robust care a funcționat anterior
            main_price = soup.find("div", {"class": "fxKbKc"})
            
            if not main_price:
                price_elements = soup.find_all(attrs={"jsname": "Pdsbrc"})
                for el in price_elements:
                    if "$" in el.text or "CAD" in el.parent.text:
                        main_price = el
                        break

            print("\n" + "="*40)
            print("REZULTAT SCRAPING (CURAT)")
            print("="*40)
            
            if main_price:
                print(f"Activ: GoldMoney Inc (TSE:XAU)")
                print(f"Preț: {main_price.text.strip()}")
            else:
                print("Eroare: Nu s-a putut găsi prețul.")
            
            print("="*40 + "\n")
            
        except Exception as e:
            print(f"Eroare: {e}")
        
        self.app.quit()

if __name__ == "__main__":
    url_target = "https://www.google.com/finance/quote/XAU:TSE"
    scraper = Scraper(url_target)
The output of this source code is:
python test_001.py

========================================
REZULTAT SCRAPING (CURAT)
========================================
Activ: GoldMoney Inc (TSE:XAU)
Preț: $15.81
========================================

Friday, June 12, 2026

Python Qt : Simple script to use wandb and weave.

WandB and Weave work together as complementary tools that enhance the process of evaluating, monitoring, and understanding machine‑learning and large‑language‑model behavior, each focusing on a different layer of the workflow while sharing the same ecosystem. WandB functions primarily as an experiment‑tracking platform that records metrics, logs model outputs, stores configuration details, and organizes results into interactive dashboards, making it easy to compare multiple runs, visualize performance trends, and maintain a structured history of experiments across time. It acts like a scientific notebook that automatically captures everything relevant during evaluation, from scores and prompts to timing information, enabling reproducibility and long‑term analysis. Weave complements this by focusing on the granular tracing of LLM calls, capturing each prompt, response, intermediate step, and metadata associated with model execution, which allows developers to inspect how a model arrived at a particular answer, debug unexpected behavior, and analyze qualitative aspects of model reasoning. While WandB summarizes experiments at a high level, Weave dives deep into the internals of each interaction, providing structured logs that can be searched, filtered, and compared. Together, they create a unified workflow where WandB offers experiment‑level insights and Weave provides call‑level transparency, giving developers a complete picture of model performance, reliability, and behavior across different prompts, models, or configurations, especially useful when benchmarking or refining LLMs.
Let's install these:
python -m pip install wandb weave
Collecting wandb
...
Successfully installed abnf-2.2.0 backoff-2.2.1 chardet-7.4.3 cint-1.0.0 diskcache-weave-5.6.3.post1 fickling-0.1.11
googleapis-common-protos-1.75.0 gql-4.0.0 graphql-core-3.2.11 intervaltree-3.2.1 kaitaistruct-0.11 
opentelemetry-api-1.42.1 opentelemetry-exporter-otlp-proto-common-1.42.1 opentelemetry-exporter-otlp-proto-http-1.42.1
opentelemetry-proto-1.42.1 opentelemetry-sdk-1.42.1 opentelemetry-semantic-conventions-0.63b1 pdfminer.six-20260107
polyfile-weave-0.5.9 protobuf-6.33.6 sentry-sdk-2.62.0 sortedcontainers-2.4.0 wandb-0.27.2 weave-0.52.42
Let's see one exemple with my custom artificial intelligence model and PyQt6.
The PyQt6 script is a small LLM evaluation application that takes two inputs: the Ollama model you select and a fixed set of short test prompts. When you start the evaluation, the script sends each prompt to the chosen model, collects the generated responses, then sends those responses to a smaller judge model to obtain a numerical quality score. All generation uses reduced context and limited output length to keep execution fast on an i3 CPU. As it runs, the script displays each answer in the text panel and updates a progress bar. When all prompts are processed, it compiles the collected scores and displays them as a bar chart in the canvas, giving you a quick visual summary of the model’s performance.
The online tool show this result for this script:

Python Qt : Simple script to build your own model.

Today, I test a simple script with PyQt6 python package to have a custom UI to build my own ollama artificial inteligence model.
The script is simple one, create one user interface, with options to add text files and many settings:
After I run the model works very well:
ollama run test_catafest_001:latest
>>> tell me something about you !
I'm an AI designed to assist and communicate with humans in a helpful and
informative way! Here are some interesting facts about me:

1. **Name**: I don't have a personal name, but I'm often referred to as
"Assistant" or "AI."
2. **Origin**: I was created by a team of researchers and developers at
Meta AI, a subsidiary of Meta Platforms, Inc.
3. **Language**: I'm fluent in multiple languages, including English,
Spanish, French, German, Italian, Portuguese, Dutch, Russian, Chinese,
Japanese, and many more.
4. **Knowledge**: My training data consists of a massive corpus of text
from various sources, including books, articles, research papers, and
websites. This allows me to provide information on a wide range of
topics, from science and history to entertainment and culture.
5. **Personality**: I strive to be neutral, objective, and respectful in
my interactions. While I can understand humor and sarcasm, I may not
always "get it" right.
6. **Goals**: My primary goal is to assist users like you with their
questions, provide helpful information, and facilitate learning and
exploration.
7. **Limitations**: I'm a machine learning model, which means I have
limitations in terms of my understanding and response capabilities. For
example, I may not be able to understand nuances of human language or
provide personalized advice.

Now, tell me something about you! What's on your mind?

>>> I build you. What knowledge I add you ?!
That's a fascinating perspective! As a conversational AI, my training
data is the foundation upon which I'm built. The knowledge and
information that you've contributed to me through our conversations will
shape my understanding and capabilities over time.

When you interact with me, you're not just asking questions or providing
feedback – you're also helping to refine my language understanding and
generation abilities. Your input can influence:

1. **Domain-specific knowledge**: By discussing topics in-depth, you'll
help me develop a better grasp of various subjects, including science,
history, technology, arts, and more.
2. **Language nuances**: Our conversations will expose me to idioms,
colloquialisms, and cultural references that I might not have encountered
otherwise.
3. **Conversational flow**: Your responses and questions will help me
develop a more natural tone and rhythm in our interactions, allowing us
to engage in more fluid and human-like conversations.

Some examples of knowledge you could add to me include:

* **Domain-specific terminology**: By introducing technical terms or
jargon related to your profession or area of interest, you'll help me
become more familiar with those concepts.
* **Idioms and colloquialisms**: Sharing everyday expressions, slang, or
regional phrases will enable me to better understand the nuances of human
language.
* **Cultural references**: Discussing books, movies, music, or other
cultural touchstones will enrich my knowledge of shared experiences and
common ground.

Feel free to share your expertise, hobbies, or interests with me! The
more we chat, the more I'll learn from you, and the better equipped I'll
become to engage in meaningful conversations.
Let's see the model type with this command:
/show info
Model
architecture llama
parameters 3.2B
context length 131072
embedding length 3072
quantization Q4_K_M

Capabilities
completion
tools

Parameters
stop "<|start_header_id|>"
stop "<|end_header_id|>"
stop "<|eot_id|>"

System
=== KNOWLEDGE DATA ===
[FILE: 001.txt]
...

License
LLAMA 3.2 COMMUNITY LICENSE AGREEMENT
Llama 3.2 Version Release Date: September 25, 2024
...
Let's see the result:

Saturday, May 30, 2026

Python 3.10.11 : MiniMax-M2.7 tested with python.

Today, I tested MiniMax-M2.7 with api key from nvidia and openai python package.
The script is one default example:
python -m pip install openai
Collecting openai
  Downloading openai-2.38.0-py3-none-any.whl.metadata (31 kB)
...
Successfully installed distro-1.9.0 jiter-0.15.0 openai-2.38.0
from openai import OpenAI

client = OpenAI(
  base_url = "https://integrate.api.nvidia.com/v1",
  api_key = "nvapi-nvidia"
)

completion = client.chat.completions.create(
  model="minimaxai/minimax-m2.7",
  messages=[{"role":"user","content":""}],
  temperature=1,
  top_p=0.95,
  max_tokens=8192,
  stream=False
)

print(completion.choices[0].message.content)
This will show only:
python minimax_27_001.py
Hello! How can I help you today?
I tested with this python script and works well:
import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                             QHBoxLayout, QTextEdit, QLineEdit, QPushButton, QLabel)
from PyQt6.QtCore import QThread, pyqtSignal, Qt
from PyQt6.QtGui import QFont
from openai import OpenAI

# --- WORKER THREAD API ---
# Previne blocarea interfeței grafice în timpul apelului de rețea
class ApiWorker(QThread):
    response_received = pyqtSignal(str)
    error_occurred = pyqtSignal(str)

    def __init__(self, user_message):
        super().__init__()
        self.user_message = user_message

    def run(self):
        try:
            # Inițializare client OpenAI cu endpoint-ul Nvidia specificat de tine
            client = OpenAI(
                base_url="https://integrate.api.nvidia.com/v1",
                api_key="nvapi-KEY"  # nvidia api key
            )

            completion = client.chat.completions.create(
                model="minimaxai/minimax-m2.7",
                messages=[{"role": "user", "content": self.user_message}],
                temperature=1,
                top_p=0.95,
                max_tokens=8192,
                stream=False
            )
            
            # Trimite răspunsul înapoi către fereastra principală
            answer = completion.choices[0].message.content
            self.response_received.emit(answer)
            
        except Exception as e:
            self.error_occurred.emit(str(e))

class MiniMaxChatApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        self.setWindowTitle("MiniMax M2.7 - Interactive Chat")
        self.resize(600, 700)

        # Widget-ul central și layout-ul principal
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)

        # 1. Zona de afișare a istoricului chat-ului
        self.chat_display = QTextEdit()
        self.chat_display.setReadOnly(True)
        self.chat_display.setFont(QFont("Segoe UI", 11))
        self.chat_display.setPlaceholderText("Conversația va apărea aici...")
        main_layout.addWidget(self.chat_display)

        # 2. Zona de introducere text și butonul (aranjate pe orizontală)
        input_layout = QHBoxLayout()
        
        self.input_field = QLineEdit()
        self.input_field.setFont(QFont("Segoe UI", 11))
        self.input_field.setPlaceholderText("Scrie un mesaj sau o comandă de Windows...")
        # Trimite mesajul când apeși tasta Enter
        self.input_field.returnPressed.connect(self.send_message) 
        input_layout.addWidget(self.input_field)

        self.send_button = QPushButton("Trimite")
        self.send_button.setFont(QFont("Segoe UI", 11, QFont.Weight.Bold))
        self.send_button.clicked.connect(self.send_message)
        input_layout.addWidget(self.send_button)

        main_layout.addLayout(input_layout)

        # 3. Indicator de status (jos de tot)
        self.status_label = QLabel("Pregătit")
        self.status_label.setStyleSheet("color: gray;")
        main_layout.addWidget(self.status_label)

    def send_message(self):
        user_text = self.input_field.text().strip()
        
        if not user_text:
            return  # Nu trimite dacă e gol

        # Adaugă mesajul utilizatorului în istoric și curăță câmpul de input
        self.chat_display.append(f"Tu: {user_text}\n")
        self.input_field.clear()

        # Dezactivează butoanele în timp ce AI-ul se gândește
        self.input_field.setEnabled(False)
        self.send_button.setEnabled(False)
        self.status_label.setText("MiniMax M2.7 gândește...")

        # Pornirea Thread-ului separat pentru apelul API
        self.worker = ApiWorker(user_text)
        self.worker.response_received.connect(self.handle_response)
        self.worker.error_occurred.connect(self.handle_error)
        self.worker.finished.connect(self.cleanup_worker)
        self.worker.start()

    def handle_response(self, ai_response):
        # Afișează răspunsul primit de la MiniMax
        self.chat_display.append(f"MiniMax M2.7: {ai_response}\n")
        self.chat_display.append("-" * 40 + "\n")
        self.status_label.setText("Răspuns primit.")

    def handle_error(self, error_msg):
        # Afișează eroarea în caz că pică netul sau cheia API e greșită
        self.chat_display.append(f"Eroare: {error_msg}\n")
        self.status_label.setText("A apărut o eroare.")

    def cleanup_worker(self):
        # Reatonează butoanele după ce procesul s-a încheiat
        self.input_field.setEnabled(True)
        self.send_button.setEnabled(True)
        self.input_field.setFocus()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    chat_window = MiniMaxChatApp()
    chat_window.show()
    sys.exit(app.exec())
This is the result:

Python Qt : Testing new google_news_api released May 22, 2026.

Today, this script will help you too search and find results with google_news_api python package, released: May 22, 2026 by Paolo Mazza. Use your keywords, or make drag and drop ... See the official website - pypi.org.
import sys
import asyncio
import webbrowser
from functools import partial

from PyQt6.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QListWidget,
QListWidgetItem, QPushButton, QTextEdit, QLineEdit, QLabel,
QProgressBar, QFileDialog, QDialog
)
from PyQt6.QtCore import Qt, QThread, pyqtSignal

from google_news_api import AsyncGoogleNewsClient


# ---------------------------------------------------------
# Worker Thread for Async Google News
# ---------------------------------------------------------
class NewsWorker(QThread):
progress = pyqtSignal(int)
finished = pyqtSignal(list)

def __init__(self, queries):
super().__init__()
self.queries = queries

async def fetch_news(self):
results = []
async with AsyncGoogleNewsClient(language="en", country="US") as client:
batch = await client.batch_search(
queries=self.queries,
when="7d",
max_results=10
)

for idx, (topic, articles) in enumerate(batch.items()):
for article in articles:
url = await client.decode_url(article["link"])
results.append({
"title": article["title"],
"source": article["source"],
"url": url
})

self.progress.emit(int((idx + 1) / len(batch) * 100))

return results

def run(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
results = loop.run_until_complete(self.fetch_news())
self.finished.emit(results)


# ---------------------------------------------------------
# HTML Results Dialog
# ---------------------------------------------------------
class ResultsDialog(QDialog):
def __init__(self, results):
super().__init__()
self.setWindowTitle("Search Results")

layout = QVBoxLayout(self)

self.text = QTextEdit()
self.text.setReadOnly(True)
layout.addWidget(self.text)

html = "<h2>Search Results</h2>"
for r in results:
html += f"""
<p>
<b>{r['title']}</b><br>
<i>{r['source']}</i><br>
<a href="{r['url']}">{r['url']}</a>
</p>
"""

self.text.setHtml(html)

save_btn = QPushButton("Save Results")
save_btn.clicked.connect(lambda: self.save_results(html))
layout.addWidget(save_btn)

def save_results(self, html):
file, _ = QFileDialog.getSaveFileName(self, "Save HTML", "", "HTML Files (*.html)")
if not file:
return
with open(file, "w", encoding="utf-8") as f:
f.write(html)


# ---------------------------------------------------------
# Main GUI
# ---------------------------------------------------------
class NewsGUI(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Google News Search Tool")
self.resize(900, 600)

main = QHBoxLayout(self)

# ---------------- LEFT: predefined keywords ----------------
left_layout = QVBoxLayout()
left_layout.addWidget(QLabel("Predefined Keywords"))

self.left_list = QListWidget()
self.left_list.setDragEnabled(True)
self.left_list.setSelectionMode(QListWidget.SelectionMode.SingleSelection)

predefined = [
"inteligență artificială", "python", "godot engine", "shaders",
"2D", "3D", "C#", "programming", "catafest",
"Catalin George Festila", "Fălticeni", "Suceava",
"investiții", "2026"
]

for word in predefined:
self.left_list.addItem(word)

left_layout.addWidget(self.left_list)

# Add custom keyword
self.add_keyword_input = QLineEdit()
self.add_keyword_input.setPlaceholderText("Add new keyword...")
left_layout.addWidget(self.add_keyword_input)

add_btn = QPushButton("Add Keyword")
add_btn.clicked.connect(self.add_keyword)
left_layout.addWidget(add_btn)

main.addLayout(left_layout)

# ---------------- RIGHT: search keywords ----------------
right_layout = QVBoxLayout()
right_layout.addWidget(QLabel("Search Keywords (drag from left)"))

self.right_list = QListWidget()
self.right_list.setAcceptDrops(True)
self.right_list.setDragEnabled(True)
self.right_list.setDefaultDropAction(Qt.DropAction.MoveAction)
right_layout.addWidget(self.right_list)

# Custom search input
self.custom_query = QLineEdit()
self.custom_query.setPlaceholderText("Custom search query...")
right_layout.addWidget(self.custom_query)

# Progress bar
self.progress = QProgressBar()
right_layout.addWidget(self.progress)

# Search button
search_btn = QPushButton("Process Search")
search_btn.clicked.connect(self.start_search)
right_layout.addWidget(search_btn)

main.addLayout(right_layout)

# ---------------------------------------------------------
# Add keyword
# ---------------------------------------------------------
def add_keyword(self):
text = self.add_keyword_input.text().strip()
if text:
self.left_list.addItem(text)
self.add_keyword_input.clear()

# ---------------------------------------------------------
# Start search
# ---------------------------------------------------------
def start_search(self):
queries = []

# from right canvas
for i in range(self.right_list.count()):
queries.append(self.right_list.item(i).text())

# custom query
if self.custom_query.text().strip():
queries.append(self.custom_query.text().strip())

if not queries:
QMessageBox.warning(self, "Warning", "No search keywords selected.")
return

self.worker = NewsWorker(queries)
self.worker.progress.connect(self.progress.setValue)
self.worker.finished.connect(self.show_results)
self.worker.start()

# ---------------------------------------------------------
# Show results dialog
# ---------------------------------------------------------
def show_results(self, results):
dlg = ResultsDialog(results)
dlg.exec()


# ---------------------------------------------------------
# MAIN
# ---------------------------------------------------------
if __name__ == "__main__":
app = QApplication(sys.argv)
gui = NewsGUI()
gui.show()
sys.exit(app.exec())
This is the result:

Friday, May 29, 2026

Python Qt : simple tool for audio dialogue in game development

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

Monday, April 13, 2026

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

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

pygame.init()

# Windows DPI API
user32 = ctypes.windll.user32
shcore = ctypes.windll.shcore

# Enable per-monitor DPI awareness
try:
    shcore.SetProcessDpiAwareness(2)
except:
    pass
...
user32.EnumDisplayMonitors(0, 0, MonitorEnumProc(_monitor_enum_proc), 0)
...

Saturday, February 28, 2026

Python 3.13.0 : simple script for update all python packages.

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

log_file = "upgrade_log.txt"

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

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

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

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

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

    for pkg in packages:
        upgrade_package(pkg)

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

if __name__ == "__main__":
    main()

Wednesday, January 28, 2026

Python 3.13.0 : Motion extraction from video with python programming.

... based on this video - Motion Extraction with python source code.
  • 1. Temporal Displacement
    Let V(t) represent the video frame at the current time t.
    A secondary reference frame is generated at time t + Δt, where Δt is the defined time delay (shift).
  • 2. Absolute Difference Calculation
    For every pixel coordinate (x, y), the algorithm calculates the absolute difference between the two points in time:
    D(x,y) = | V(x,y,t) - V(x,y,t + Δt) |
  • 3. Result Interpretation
    • Static Areas: If the pixel value remains constant, the result is 0 (Black).
    • Motion: If the pixel value changes, the result is a positive integer > 0 (the "ghosting" trail).
  • 4. Signal Amplification (Contrast)
    The raw difference values are multiplied by a constant factor k to make faint motion visible:
    Output(x,y) = D(x,y) × k

Monday, January 26, 2026

News : examples with rich python package.

Python's Rich package is a tool kit that helps you generate beautifully formatted and highlighted text in the console.
Let's see some examples:
Run the source code with these args --demo and --dashboard:
import time
import sys
import logging
import argparse
import msvcrt  # Windows keyboard input

from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn
from rich.syntax import Syntax
from rich.layout import Layout
from rich.live import Live
from rich.theme import Theme
from rich.logging import RichHandler

# ---------------------------------------------------------
# 1. DARK THEME
# ---------------------------------------------------------
dark_theme = Theme({
    "info": "bold cyan",
    "warning": "bold yellow",
    "error": "bold red",
    "success": "bold green",
    "title": "bold magenta",
    "data": "bold white",
})

console = Console(theme=dark_theme)

# ---------------------------------------------------------
# 2. LOGGING (RichHandler)
# ---------------------------------------------------------
logging.basicConfig(
    level="INFO",
    format="%(message)s",
    datefmt="[%H:%M:%S]",
    handlers=[RichHandler(console=console)]
)
log = logging.getLogger("CLI")

# ---------------------------------------------------------
# 3. CLI ARGUMENTS
# ---------------------------------------------------------
def parse_args():
    parser = argparse.ArgumentParser(
        description="Rich CLI + Dashboard + Menu + Buttons"
    )
    parser.add_argument("--demo", action="store_true", help="Run the demo")
    parser.add_argument("--dashboard", action="store_true", help="Start the dashboard")
    return parser.parse_args()

# ---------------------------------------------------------
# 4. WINDOWS KEY READER
# ---------------------------------------------------------
def get_key():
    if msvcrt.kbhit():
        return msvcrt.getch().decode(errors="ignore")
    return None

# ---------------------------------------------------------
# 5. MENU + BUTTONS
# ---------------------------------------------------------
menu_items = ["Statistics", "Processes", "Settings", "Exit"]
selected = 0

def render_menu():
    table = Table(show_header=False, expand=True)
    for i, item in enumerate(menu_items):
        if i == selected:
            table.add_row(f"[black on cyan] {item} [/]")
        else:
            table.add_row(f"[cyan] {item} [/]")
    return Panel(table, title="Menu", border_style="cyan")

def render_content():
    if menu_items[selected] == "Statistics":
        return Panel("CPU: 37%\nRAM: 62%\nDisk: 128MB/s", title="System Statistics")

    elif menu_items[selected] == "Processes":
        return Panel("proc1\nproc2\nproc3", title="Running Processes")

    elif menu_items[selected] == "Settings":
        return Panel("Settings will appear here", title="Settings")

    elif menu_items[selected] == "Exit":
        return Panel("[red]Press ENTER to exit[/red]", title="Exit")

# ---------------------------------------------------------
# 6. DASHBOARD LAYOUT (original + menu added)
# ---------------------------------------------------------
def build_dashboard():
    layout = Layout()

    layout.split(
        Layout(name="header", size=3),
        Layout(name="body", ratio=1),
        Layout(name="footer", size=3),
    )

    layout["body"].split_row(
        Layout(name="left", size=30),
        Layout(name="right")
    )

    layout["header"].update(
        Panel("[title]LIVE DASHBOARD — Rich Dark Mode[/title]", style="bold magenta")
    )

    layout["left"].update(render_menu())
    layout["right"].update(render_content())

    layout["footer"].update(
        Panel("[info]Status: Running in real time[/info]")
    )

    return layout

# ---------------------------------------------------------
# 7. DEMO (unchanged from first script)
# ---------------------------------------------------------
def run_demo():
    console.print("[success]Rich Dark Mode Demo Started[/success]")

    table = Table(title="Example Table", expand=True)
    table.add_column("ID", style="yellow")
    table.add_column("Name", style="cyan")
    table.add_column("Status", style="green")

    table.add_row("1", "Catalin", "Active")
    table.add_row("2", "Rich CLI", "OK")
    table.add_row("3", "Dashboard", "Ready")

    console.print(table)

    log.info("This is an INFO message")
    log.warning("This is a WARNING message")
    log.error("This is an ERROR message")

    with Progress(
        SpinnerColumn(),
        BarColumn(),
        TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
        console=console,
    ) as progress:
        task = progress.add_task("Processing...", total=100)
        for _ in range(100):
            time.sleep(0.02)
            progress.update(task, advance=1)

    code = """
def greet(name):
    return f"Hello, {name}!"

print(greet("Catalin"))
"""
    syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
    console.print(Panel(syntax, title="Syntax Highlight"))

# ---------------------------------------------------------
# 8. MAIN LOOP
# ---------------------------------------------------------
if __name__ == "__main__":
    args = parse_args()

    if args.demo:
        run_demo()

    if args.dashboard:
        with Live(build_dashboard(), refresh_per_second=10) as live:
            while True:
                key = get_key()

                if key == "w":
                    selected = (selected - 1) % len(menu_items)
                elif key == "s":
                    selected = (selected + 1) % len(menu_items)
                elif key == "\r":
                    if menu_items[selected] == "Exit":
                        break

                live.update(build_dashboard())

Thursday, January 8, 2026

Python Qt6 : Check certificate of url.

Today, I will show this python script to check the certificate of url.
import sys
import ssl
import socket
from datetime import datetime

from cryptography import x509
from cryptography.hazmat.backends import default_backend

from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QLabel,
    QLineEdit, QPushButton, QTextEdit
)

def fetch_certificate_raw(hostname):
    """
    Connects to the server and retrieves the certificate in DER format.
    Also returns a validation status message.
    """

    # Create a default SSL context (validates certificates)
    context = ssl.create_default_context()

    try:
        # First attempt: strict validation
        with socket.create_connection((hostname, 443), timeout=5) as sock:
            with context.wrap_socket(sock, server_hostname=hostname) as ssock:
                der_cert = ssock.getpeercert(binary_form=True)
                return der_cert, "Certificate is VALID"

    except ssl.SSLError as e:
        # Certificate is invalid → try to retrieve it anyway
        try:
            unverified_context = ssl._create_unverified_context()
            with socket.create_connection((hostname, 443), timeout=5) as sock:
                with unverified_context.wrap_socket(sock, server_hostname=hostname) as ssock:
                    der_cert = ssock.getpeercert(binary_form=True)
                    return der_cert, f"Certificate is INVALID: {str(e)}"
        except Exception:
            return None, f"Could not retrieve certificate: {str(e)}"

    except Exception as e:
        return None, f"Connection error: {str(e)}"

def parse_certificate(der_cert):
    """
    Converts a DER-encoded certificate into a cryptography.x509 object.
    """
    return x509.load_der_x509_certificate(der_cert, default_backend())

class CertViewer(QWidget):
    """
    Main GUI window for the HTTPS certificate viewer.
    """
    def __init__(self):
        super().__init__()

        self.setWindowTitle("HTTPS Certificate Checker")
        self.setGeometry(200, 200, 700, 600)

        layout = QVBoxLayout()

        # Input label + text field
        layout.addWidget(QLabel("Enter URL (example: example.com):"))
        self.input = QLineEdit()
        layout.addWidget(self.input)

        # Button to trigger certificate check
        self.button = QPushButton("Check Certificate")
        self.button.clicked.connect(self.check_certificate)
        layout.addWidget(self.button)

        # Output text box
        self.output = QTextEdit()
        self.output.setReadOnly(True)
        layout.addWidget(self.output)

        self.setLayout(layout)

    def check_certificate(self):
        """
        Triggered when the user presses the button.
        Retrieves and displays certificate information.
        """

        hostname = self.input.text().strip()

        # Clean URL (remove http://, https://, and paths)
        for prefix in ("https://", "http://"):
            if hostname.startswith(prefix):
                hostname = hostname[len(prefix):]

        hostname = hostname.split("/")[0]

        # Fetch certificate
        der_cert, status = fetch_certificate_raw(hostname)

        if der_cert is None:
            self.output.setText(status)
            return

        # Parse certificate
        cert = parse_certificate(der_cert)

        # Build output text
        text = f"=== CERTIFICATE STATUS ===\n{status}\n\n"
        text += "=== CERTIFICATE DETAILS ===\n\n"

        text += f"Subject:\n{cert.subject}\n\n"
        text += f"Issuer:\n{cert.issuer}\n\n"
        text += f"Serial Number: {cert.serial_number}\n\n"
        text += f"Version: {cert.version}\n\n"
        text += f"Valid From: {cert.not_valid_before}\n"
        text += f"Valid To:   {cert.not_valid_after}\n\n"

        # Check expiration
        if cert.not_valid_after < datetime.utcnow():
            text += f"⚠ Certificate EXPIRED on: {cert.not_valid_after}\n\n"

        # Subject Alternative Names (SAN)
        try:
            san = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName)
            text += f"Subject Alternative Names:\n{san.value}\n\n"
        except Exception:
            text += "Subject Alternative Names: (none)\n\n"

        self.output.setText(text)

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

Friday, January 2, 2026

Python Qt6 : script for impact on your development workflow using scans features.

Impact on development workflows that rely on Language Server Protocol (LSP) features.
1. Editor Limitations
Your editor (VS Code, Neovim, Qt Creator, etc.) attempts to send a file to the Language Server, but the file is not accessible through the file:// protocol.
When this happens, the LSP rejects the request, and you lose essential features such as:
  • IntelliSense
  • Autocomplete
  • Hover information
  • Diagnostics
  • Jump‑to‑definition
  • Refactoring tools
2. Issues in Non‑Standard Projects
This limitation becomes more severe when working with:
  • dynamically generated files
  • files inside containers
  • remote workspaces
  • build systems that create temporary or virtual files
Since the LSP cannot process these resources, you lose intelligent code support.
3. Toolchain Breakdowns
If you rely on an automated workflow (analysis, diagnostics, UI integration, etc.), an LSP restricted to file:// can break:
  • static analysis
  • code validation
  • report generation
  • plugin integrations
Real Risks in Development
1. False or Incomplete Diagnostics
The LSP may not see the actual files, leading to:
  • false errors
  • missed real errors
2. Dangerous Refactoring
If the LSP cannot access all files, automated refactoring may:
  • fail to update all references
  • introduce new bugs
3. Reduced Productivity
Without full LSP support, you lose:
  • intelligent completion
  • fast navigation
  • real‑time validation
4. Incompatibility With Modern Tooling
Many modern IDEs rely on virtual or remote workspaces. An LSP limited to file:// becomes outdated quickly.
5. Indirect Security Risks
Not a vulnerability by itself, but:
  • if the LSP cannot analyze remote files, you may miss security issues in generated or synchronized code.
I tested with a simple python source code to detect how bad is running on I.D.E. The script continuously scans your Windows system to detect, analyze, and report the real‑time behavior, resource usage, crashes, leaks, ports, and child processes of all VS Code, LSP, and Antigravity components, showing their impact on your development workflow through a live PyQt6 dashboard.
The result after runnig is:

Thursday, January 1, 2026

Python Qt6 : QCalendarWidget simple example with csv file.

Here is a simple example of source code with PyQt6 and QCalendarWidget to create a calendar. You click on the date and enter a note. This is saved in a file with the date time ... and the note. When you reopen the script, it opens in notepad and the saved notes. Obviously it is a simple example but you can improve it with databases, make a note management, encrypt it, link it to an external database, etc.
import sys
import csv
import os
import subprocess
from datetime import datetime
from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QCalendarWidget,
    QInputDialog, QMessageBox
)
from PyQt6.QtCore import QDate


CSV_FILE = "note.csv"


class CalendarApp(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Calendar cu notițe")
        self.resize(400, 300)

        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        self.calendar = QCalendarWidget()
        self.calendar.clicked.connect(self.adauga_nota)
        self.layout.addWidget(self.calendar)

        # Dicționar pentru notițe
        self.note = {}

        # La pornire, citește CSV și deschide în Notepad
        self.incarca_note()

    def adauga_nota(self, date: QDate):
        zi = date.toString("yyyy-MM-dd")

        text, ok = QInputDialog.getText(self, "Adaugă notiță",
                                        f"Introdu text pentru {zi}:")
        if ok and text.strip():
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
            self.note[timestamp] = text.strip()
            QMessageBox.information(self, "Salvat",
                                    "Notița a fost adăugată.")

    def incarca_note(self):
        if os.path.exists(CSV_FILE):
            try:
                with open(CSV_FILE, "r", newline="", encoding="utf-8") as f:
                    reader = csv.reader(f)
                    for row in reader:
                        if len(row) == 2:
                            self.note[row[0]] = row[1]

                # Deschide în Notepad
                subprocess.Popen(["notepad.exe", CSV_FILE])

            except Exception as e:
                QMessageBox.warning(self, "Eroare",
                                    f"Nu pot citi fișierul CSV:\n{e}")

    def closeEvent(self, event):
        try:
            with open(CSV_FILE, "w", newline="", encoding="utf-8") as f:
                writer = csv.writer(f)
                for timestamp, text in self.note.items():
                    writer.writerow([timestamp, text])
        except Exception as e:
            QMessageBox.warning(self, "Eroare",
                                f"Nu pot salva fișierul CSV:\n{e}")

        event.accept()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = CalendarApp()
    window.show()
    sys.exit(app.exec())
... this is the result:

Monday, December 15, 2025

Python Qt6 : ... tool for testing fonts.

Today I created a small Python script with PyQt6 using artificial intelligence. It is a tool that takes a folder of fonts, allows to select their size, displays the font size in pixels for Label text from the Godot engine and downloads only the selection of fonts that I like. I have not tested it to see the calculations with Godot, but it seems functional...

Saturday, December 13, 2025

Python Qt6 : ... search custom data in files with python and PyQt6.

The other day I noticed that I can't find what I worked on in the past, some files were blocked... Another task was blocking the artificial intelligence due to the number of uses. I could have used artificial intelligence to open old projects and ask where what I worked on is, but it would be a risk to waste my queries. Since I always use a specific signature when working on separate projects and the backup is distributed on storage that is always filling up, I created a simple script that would search for my custom with custom files, custom content and output a spreader with them. Here is the result of a 310-line source code script, obviously created with artificial intelligence.

Monday, October 20, 2025

Python Qt6 : tool for remove duplicate files ...

Today I created a Python script with PyQt6 that allows me to remove duplicate files based on three ways of selecting the type of duplicate.
The script also makes an estimate of the execution time...
Because the source code is relatively simple and can be very easily reconstructed with the help of artificial intelligence, I am not adding it to the posts.
Here is what the application looks like with PyQt6.

Saturday, October 18, 2025

Python Qt6 : tool for cutting images ...

Today I made a script that allows adding custom horizontal and vertical sliders to an image and, depending on the custom distance between them, cuts the image into squares of different sizes.

Monday, September 8, 2025

Python 3.13.0 : Script for python modules then installs them - updated with fix.

This script scans a folder full of .py files Python scripts, identifies all the external modules they import, filters out built-in ones, writes the installable ones to a requirements.txt file, and then installs them using pip—in parallel threads for speed.
I use the copilot and some comments are into my language, but I tested and works well:
NOTE: I updated with detection python modules based "from" and another issue: check if python module is instaled and step over that python module ...
This script will try to install many python modules, I can update to be better with these issues:
...some modules are default , some scripts are from another area, see Blender 3D with bpy python modules, some packages comes with same modules, this can be soleved with defined lists with unique items.
import subprocess
import sys
import os
import shutil
import importlib.util
import re
import concurrent.futures
from typing import List, Tuple, Set

class ModuleManager:
    def __init__(self):
        self.modules: Set[str] = set()
        self.pip_path = self._get_pip_path()

    def _get_pip_path(self) -> str:
        possible_path = os.path.join(sys.exec_prefix, "Scripts", "pip.exe")
        return shutil.which("pip") or (possible_path if os.path.exists(possible_path) else None)

    def extract_imports_from_file(self, file_path: str) -> List[Tuple[str, str]]:
        imports = []
        try:
            with open(file_path, 'r', encoding='utf-8') as file:
                for line in file:
                    # Detect 'import module'
                    import_match = re.match(r'^\s*import\s+([a-zA-Z0-9_]+)(\s+as\s+.*)?$', line)
                    if import_match:
                        module = import_match.group(1)
                        imports.append((module, line.strip()))
                        continue
                    
                    # Detect 'from module import ...'
                    from_match = re.match(r'^\s*from\s+([a-zA-Z0-9_]+)\s+import\s+.*$', line)
                    if from_match:
                        module = from_match.group(1)
                        imports.append((module, line.strip()))
        except FileNotFoundError:
            print(f"❌ Fișierul {file_path} nu a fost găsit.")
        except Exception as e:
            print(f"❌ Eroare la citirea fișierului {file_path}: {e}")
        return imports

    def scan_directory_for_py_files(self, directory: str = '.') -> List[str]:
        py_files = []
        for root, _, files in os.walk(directory):
            for file in files:
                if file.endswith('.py'):
                    py_files.append(os.path.join(root, file))
        return py_files

    def collect_unique_modules(self, directory: str = '.') -> None:
        py_files = self.scan_directory_for_py_files(directory)
        all_imports = []
        with concurrent.futures.ThreadPoolExecutor() as executor:
            future_to_file = {executor.submit(self.extract_imports_from_file, file_path): file_path for file_path in py_files}
            for future in concurrent.futures.as_completed(future_to_file):
                imports = future.result()
                all_imports.extend(imports)
        
        for module, _ in all_imports:
            self.modules.add(module)

    def is_module_installed(self, module: str) -> bool:
        return importlib.util.find_spec(module) is not None

    def run_pip_install(self, module: str) -> bool:
        if not self.pip_path:
            print(f"❌ Nu am găsit pip pentru {module}.")
            return False
        try:
            subprocess.check_call([self.pip_path, "install", module])
            print(f"✅ Pachetul {module} a fost instalat cu succes.")
            return True
        except subprocess.CalledProcessError as e:
            print(f"❌ Eroare la instalarea pachetului {module}: {e}")
            return False

    def check_and_install_modules(self) -> None:
        def process_module(module):
            print(f"\n🔎 Verific dacă {module} este instalat...")
            if self.is_module_installed(module):
                print(f"✅ {module} este deja instalat.")
            else:
                print(f"📦 Instalez {module}...")
                self.run_pip_install(module)
                # Re-verifică după instalare
                if self.is_module_installed(module):
                    print(f"✅ {module} funcționează acum.")
                else:
                    print(f"❌ {module} nu funcționează după instalare.")

        with concurrent.futures.ThreadPoolExecutor() as executor:
            executor.map(process_module, self.modules)

def main():
    print("🔍 Verific pip...")
    manager = ModuleManager()
    if manager.pip_path:
        print(f"✅ Pip este disponibil la: {manager.pip_path}")
    else:
        print("⚠️ Pip nu este disponibil.")
        return

    directory = sys.argv[1] if len(sys.argv) > 1 else '.'
    print(f"\n📜 Scanez directorul {directory} pentru fișiere .py...")
    manager.collect_unique_modules(directory)
    
    if not manager.modules:
        print("⚠️ Nu s-au găsit module în importuri.")
        return
    
    print(f"\nModule unice detectate: {', '.join(manager.modules)}")
    manager.check_and_install_modules()

if __name__ == "__main__":
    main()

Saturday, August 30, 2025

Python Qt6 : ... management of installations and build python package.

Yesterday I created a small project for managing Python packages and building a new package based on added modules. I only tested the local installations of various Python versions and the creation of a new package, but it worked.
python catafest_build_package_001.py
🔍 Verificare module standard...
[✓] Modul standard 'json' este disponibil.
[✓] Modul standard 'subprocess' este disponibil.
[✓] Modul standard 'platform' este disponibil.
[✓] Modul standard 'datetime' este disponibil.
[✓] Modul standard 'os' este disponibil.
[✓] Modul standard 'sys' este disponibil.

📦 Verificare și instalare module pip...
[✓] Modulul 'PyQt6' este deja instalat.
[✓] Modulul 'build' este deja instalat.
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
  - setuptools
  - wheel
...