analitics

Pages

Showing posts with label os. Show all posts
Showing posts with label os. Show all posts

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:

Wednesday, December 17, 2025

Python Qt6 : Run Visual Studio Code directly from RAM using ImDisk and python.

I built a small tool using Python + PyQt6 that lets me run Visual Studio Code directly from a RAM Disk for maximum speed and minimal SSD wear.
The setup uses ImDisk Toolkit to automatically create a 6 GB RAM Disk (drive O:).
The tool then copies VS Code into RAM and launches it instantly through a simple graphical interface with three buttons:
  • Create RAM Disk
  • Copy VS Code to RAM
  • Launch VS Code from RAM
This method provides much faster startup times and a smoother workflow, making it a great performance boost for Windows users.

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.

Wednesday, December 10, 2025

Python 3.13.0 : ... simple script for copilot history.

NOTES: I have been using artificial intelligence since it appeared, it is obviously faster and more accurate, I recommend using testing on larger projects.
This python script will take the database and use to get copilot history and save to file copilot_conversations.txt :
import os
import sqlite3
import datetime
import requests
from bs4 import BeautifulSoup
import shutil

# Locația tipică pentru istoricul Edge (Windows)
edge_history_path = os.path.expanduser(
    r"~\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\History"
)

# Copiem fișierul History în folderul curent
def copy_history_file(src_path, dst_name="edge_history_copy.db"):
    if not os.path.exists(src_path):
        print("Nu am găsit istoricul Edge.")
        return None
    dst_path = os.path.join(os.getcwd(), dst_name)
    try:
        shutil.copy(src_path, dst_path)
        print(f"Am copiat baza de date în {dst_path}")
        return dst_path
    except Exception as e:
        print(f"Eroare la copiere: {e}")
        return None

def extract_copilot_links(db_path):
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()

    cursor.execute("""
        SELECT url, title, last_visit_time
        FROM urls
        WHERE url LIKE '%copilot%'
    """)

    results = []
    for url, title, last_visit_time in cursor.fetchall():
        ts = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=last_visit_time)
        results.append({
            "url": url,
            "title": title,
            "last_visit": ts.strftime("%Y-%m-%d %H:%M:%S")
        })

    conn.close()
    return results

def fetch_conversation(url):
    try:
        resp = requests.get(url)
        if resp.status_code == 200:
            soup = BeautifulSoup(resp.text, "html.parser")
            texts = soup.get_text(separator="\n", strip=True)
            return texts
        else:
            return f"Eroare acces {url}: {resp.status_code}"
    except Exception as e:
        return f"Eroare acces {url}: {e}"

if __name__ == "__main__":
    copy_path = copy_history_file(edge_history_path)
    if copy_path:
        chats = extract_copilot_links(copy_path)
        if chats:
            with open("copilot_conversations.txt", "w", encoding="utf-8") as f:
                for chat in chats:
                    content = fetch_conversation(chat["url"])
                    f.write(f"=== Conversație: {chat['title']} ({chat['last_visit']}) ===\n")
                    f.write(content)
                    f.write("\n\n")
            print("Am salvat conversațiile în copilot_conversations.txt")
        else:
            print("Nu am găsit conversații Copilot în istoricul Edge.")

Thursday, October 30, 2025

Python 3.13.0 : xAI A.P.I. with regional endpoint on xai_sdk python package.

Grok is a family of Large Language Models (LLMs) developed by xAI.
Inspired by the Hitchhiker's Guide to the Galaxy, Grok is a maximally truth-seeking AI that provides insightful, unfiltered truths about the universe.
xAI offers an API for developers to programmatically interact with our Grok models. The same models power our consumer facing services such as Grok.com, the iOS and Android apps, as well as Grok in X experience.
If you want to use a regional endpoint, you need to specify the endpoint url when making request with SDK. In xAI SDK, this is specified through the api_host parameter.
Is not free models available for xAI A.P.I.
See this example from the official website:
import os

from xai_sdk import Client
from xai_sdk.chat import user

client = Client(
api_key=os.getenv("XAI_API_KEY"),
api_host="us-east-1.api.x.ai" # Without the https://
)

chat = client.chat.create(model="grok-4")
chat.append(user("What is the meaning of life?"))

completion = chat.sample()

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.

Python Qt6 : tool for renaming files with creation date .

Since this hacking and the crashes... I've always taken screenshots... Today I created a small script that takes files from a folder and renames them with the creation date in this format...yyyyMMdd_HHmmss .
... obviously artificial intelligence helped me.
This is the source code :
import sys
import os
import shutil
from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QFileDialog, QMessageBox
from PyQt6.QtCore import QDateTime

class FileRenamer(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Redenumire fișiere cu dată și index")
        self.setGeometry(100, 100, 400, 150)

        layout = QVBoxLayout()

        self.button = QPushButton("Selectează folderul și redenumește fișierele")
        self.button.clicked.connect(self.rename_files)
        layout.addWidget(self.button)

        self.setLayout(layout)

    def rename_files(self):
        folder = QFileDialog.getExistingDirectory(self, "Selectează folderul")
        if not folder:
            return

        files = [f for f in os.listdir(folder) if os.path.isfile(os.path.join(folder, f))]
        files.sort()  # Sortează pentru consistență

        for index, filename in enumerate(files, start=1):
            old_path = os.path.join(folder, filename)
            try:
                creation_time = os.path.getctime(old_path)
                dt = QDateTime.fromSecsSinceEpoch(int(creation_time))
                date_str = dt.toString("yyyyMMdd_HHmmss")
                ext = os.path.splitext(filename)[1]
                new_name = f"{date_str}_{index:03d}{ext}"
                new_path = os.path.join(folder, new_name)

                # Evită suprascrierea fișierelor existente
                if not os.path.exists(new_path):
                    shutil.move(old_path, new_path)
            except Exception as e:
                QMessageBox.critical(self, "Eroare", f"Eroare la fișierul {filename}:\n{str(e)}")
                continue

        QMessageBox.information(self, "Succes", "Fișierele au fost redenumite cu succes!")

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

Friday, September 26, 2025

Python Qt6 : tool for game development with PNG images.

Today, I worked with art artificial intelligence, to create tool for my game development.
I used python and PyQt6 and this tool help me to remove border, resize, split, rename and save images as PNG file type for Godot game engine.

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
...

Thursday, August 21, 2025

Python Qt6 : Use python with ffmpeg tool ...

If you download a video from youtube with high resolution using a tool as yt-dlp then you can get two files with video and audio content:
... one with be with .f401.mp4 and another with .f251-9.webm and using the ffmpeg tool you can create one .mp4 file with both audio and video content.
Let's see a source code with python and PyQt6 module to search into D:\Software folder and create the mp4 file.

import os
import subprocess
from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QPushButton,
    QListWidget, QMessageBox
)
# fisier download : yt-dlp.exe -vU https://www.youtube.com/watch?v=xxxxxx -f bestvideo*+bestaudio/best
FOLDER_PATH = r"D:\Software"

class FFmpegMerger(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Combinare Video + Audio cu FFmpeg")
        self.resize(600, 400)

        self.layout = QVBoxLayout()
        self.file_list = QListWidget()
        self.process_button = QPushButton("Prelucrează în MP4")

        self.layout.addWidget(self.file_list)
        self.layout.addWidget(self.process_button)
        self.setLayout(self.layout)

        self.process_button.clicked.connect(self.process_files)

        self.populate_file_list()

    def populate_file_list(self):
        files = os.listdir(FOLDER_PATH)
        video_files = [f for f in files if f.endswith(".f401.mp4")]
        audio_files = [f for f in files if f.endswith(".f251-9.webm")]

        base_names = set(f.split(".f401.mp4")[0] for f in video_files)
        candidates = []

        for base in base_names:
            audio_name = f"{base}.f251-9.webm"
            output_name = f"{base}.mp4"
            if audio_name in audio_files and output_name not in files:
                candidates.append(base)

        for name in candidates:
            self.file_list.addItem(name)

    def process_files(self):
        for i in range(self.file_list.count()):
            base = self.file_list.item(i).text()
            video_path = os.path.join(FOLDER_PATH, f"{base}.f401.mp4")
            audio_path = os.path.join(FOLDER_PATH, f"{base}.f251-9.webm")
            output_path = os.path.join(FOLDER_PATH, f"{base}.mp4")

            cmd = [
                "ffmpeg",
                "-i", video_path,
                "-i", audio_path,
                "-c:v", "copy",
                "-c:a", "aac",
                "-strict", "experimental",
                output_path
            ]

            try:
                subprocess.run(cmd, check=True)
            except subprocess.CalledProcessError as e:
                QMessageBox.critical(self, "Eroare", f"Eroare la procesarea {base}: {e}")
                return

        QMessageBox.information(self, "Succes", "Toate fișierele au fost prelucrate cu succes!")

if __name__ == "__main__":
    app = QApplication([])
    window = FFmpegMerger()
    window.show()
    app.exec()

Sunday, July 13, 2025

Python Qt6 : simple celtic knots tool with SVG file format.

Today, I try to create SVG file with an celtic knot design tool.
I used random values from -360 up to 360 for for Twist 1, Twist 2, and Twist 3 sliders.
The basic function is this, is created by artificial intelligence and not works very well.
        # Generate star polygon vertices
        points_cw = []
        points_ccw = []
        for i in range(steps):
            t = 2 * math.pi * i / steps
            r = outer_radius if i % 2 == 0 else inner_radius
            x_cw = center[0] + r * math.cos(t)
            y_cw = center[1] + r * math.sin(t)
            x_ccw = center[0] + r * math.cos(-t + math.pi / max(lobes, 1))
            y_ccw = center[1] + r * math.sin(-t + math.pi / max(lobes, 1))
            points_cw.append((x_cw, y_cw))
            points_ccw.append((x_ccw, y_ccw))
See one random example with this tool:

Saturday, July 12, 2025

Python Qt6 : simple merge sprites images with unittest feature.

Today, I created one python tool script with the artificial intelligence to merge sprites images.
I used the artificial intelligence to add unittest to create default images with PIL to test the result.
You can select your folder , select the align of merge features or test with unittest button.
This is the result and works well:
import os
import unittest
from PIL import Image, ImageDraw, ImageFont, ImageQt
import shutil
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QFileDialog, QLabel, QVBoxLayout, QWidget, QComboBox
from PyQt6.QtGui import QPixmap
from PyQt6.QtCore import Qt
import sys

def create_test_image(path, size, number):
    img = Image.new('RGBA', size, (255, 255, 255, 255))
    draw = ImageDraw.Draw(img)
    # Simplified to just draw the number with a basic color background
    draw.rectangle((0, 0, size[0], size[1]), fill=(0, 100 * number % 255, 0, 255))
    try:
        font = ImageFont.load_default()
    except:
        font = None
    draw.text((size[0]//2-5, size[1]//2-5), str(number), fill=(255, 255, 255, 255), font=font)
    img.save(path, 'PNG')

def merge_sprites(folder_path, output_horizontal, output_vertical):
    images = [Image.open(os.path.join(folder_path, f)) for f in os.listdir(folder_path) if f.endswith(('.png', '.jpg', '.jpeg'))]
    
    if not images:
        return None, None
    
    width, height = images[0].size
    
    # Horizontal merge
    total_width = width * len(images)
    horizontal_image = Image.new('RGBA', (total_width, height))
    for i, img in enumerate(images):
        horizontal_image.paste(img, (i * width, 0))
    horizontal_image.save(output_horizontal, 'PNG')
    
    # Vertical merge
    total_height = height * len(images)
    vertical_image = Image.new('RGBA', (width, total_height))
    for i, img in enumerate(images):
        vertical_image.paste(img, (0, i * height))
    vertical_image.save(output_vertical, 'PNG')
    
    return horizontal_image, vertical_image

class TestSpriteMerger(unittest.TestCase):
    def setUp(self):
        self.test_folder = 'test_images'
        self.size = (50, 20)
        os.makedirs(self.test_folder, exist_ok=True)
        for i in range(3):
            create_test_image(os.path.join(self.test_folder, f'test_{i+1}.png'), self.size, i+1)
    
    def test_merge_horizontal(self):
        output_h = 'test_merged_horizontal.png'
        output_v = 'test_merged_vertical.png'
        h_img, _ = merge_sprites(self.test_folder, output_h, output_v)
        
        self.assertIsNotNone(h_img, "Horizontal merge failed")
        self.assertEqual(h_img.size, (self.size[0] * 3, self.size[1]))
    
    def test_merge_vertical(self):
        output_h = 'test_merged_horizontal.png'
        output_v = 'test_merged_vertical.png'
        _, v_img = merge_sprites(self.test_folder, output_h, output_v)
        
        self.assertIsNotNone(v_img, "Vertical merge failed")
        self.assertEqual(v_img.size, (self.size[0], self.size[1] * 3))
    
    def tearDown(self):
        if os.path.exists(self.test_folder):
            shutil.rmtree(self.test_folder)
        for f in ['test_merged_horizontal.png', 'test_merged_vertical.png']:
            if os.path.exists(f):
                os.remove(f)

class SpriteMergerApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Sprite Merger")
        self.setGeometry(100, 100, 800, 600)
        
        self.folder_path = ""
        
        layout = QVBoxLayout()
        
        self.select_button = QPushButton("Select Folder")
        self.select_button.clicked.connect(self.select_folder)
        layout.addWidget(self.select_button)
        
        self.merge_type = QComboBox()
        self.merge_type.addItems(["Horizontal", "Vertical"])
        layout.addWidget(self.merge_type)
        
        self.process_button = QPushButton("Process Selected Folder")
        self.process_button.clicked.connect(self.process_folder)
        layout.addWidget(self.process_button)
        
        self.test_button = QPushButton("Run Unit Test")
        self.test_button.clicked.connect(self.run_unit_test)
        layout.addWidget(self.test_button)
        
        self.result_label = QLabel("No image processed")
        layout.addWidget(self.result_label)
        
        self.image_label = QLabel()
        layout.addWidget(self.image_label)
        
        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)
    
    def select_folder(self):
        self.folder_path = QFileDialog.getExistingDirectory(self, "Select Sprite Folder")
        self.result_label.setText(f"Selected: {self.folder_path}")
    
    def process_folder(self):
        if not self.folder_path:
            self.result_label.setText("Please select a folder first")
            return
        h_img, v_img = merge_sprites(self.folder_path, 'merged_horizontal.png', 'merged_vertical.png')
        selected_type = self.merge_type.currentText()
        
        img = h_img if selected_type == "Horizontal" else v_img
        if img:
            pixmap = QPixmap.fromImage(ImageQt.ImageQt(img))
            self.image_label.setPixmap(pixmap.scaled(700, 500, aspectRatioMode=Qt.AspectRatioMode.KeepAspectRatio))
            self.result_label.setText(f"{selected_type} merge completed")
        else:
            self.result_label.setText(f"{selected_type} merge failed")
    
    def run_unit_test(self):
        suite = unittest.TestLoader().loadTestsFromTestCase(TestSpriteMerger)
        result = unittest.TextTestRunner().run(suite)
        
        test_folder = 'test_images'
        os.makedirs(test_folder, exist_ok=True)
        for i in range(3):
            create_test_image(os.path.join(test_folder, f'test_{i+1}.png'), (50, 20), i+1)
        
        h_img, v_img = merge_sprites(test_folder, 'test_merged_horizontal.png', 'test_merged_vertical.png')
        selected_type = self.merge_type.currentText()
        
        img = h_img if selected_type == "Horizontal" else v_img
        if img:
            pixmap = QPixmap.fromImage(ImageQt.ImageQt(img))
            self.image_label.setPixmap(pixmap.scaled(700, 500, aspectRatioMode=Qt.AspectRatioMode.KeepAspectRatio))
            self.result_label.setText(f"Unit tests: {result.testsRun} run, {len(result.failures)} failed, showing {selected_type.lower()} merge")
        else:
            self.result_label.setText(f"Unit tests: {result.testsRun} run, {len(result.failures)} failed, merge failed")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = SpriteMergerApp()
    window.show()
    sys.exit(app.exec())

Wednesday, July 2, 2025

Python Qt6 : ... simple resize image files.

I like the combination of Python development and the inclusion of the PyQt6 module. It is very fast and stable and allows me to create all sorts of tools to use.
Today I will show you another handy script that allows you to read all the image files from a folder and, depending on the selections: height, length, and/or aspect ratio, resize them and then place them in a folder created specifically for the resulting images.
Here is how the script looks, I clearly used artificial intelligence and it didn't take more than a few minutes, my evaluation, testing, and rearranging the interface took longer ...
import sys
import os
from datetime import datetime
from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton, QFileDialog, QLineEdit, QCheckBox, QLabel, QMessageBox
from PyQt6.QtCore import Qt
from PIL import Image

class ResizeApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Image Resizer")
        self.setGeometry(100, 100, 400, 200)

        layout = QVBoxLayout()
        central_widget = QWidget()
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

        self.folder_button = QPushButton("Select Folder")
        self.folder_button.clicked.connect(self.select_folder)
        layout.addWidget(self.folder_button)

        self.width_edit = QLineEdit("800")
        self.width_edit.setPlaceholderText("Width (px)")
        layout.addWidget(QLabel("Width:"))
        layout.addWidget(self.width_edit)

        self.height_edit = QLineEdit("600")
        self.height_edit.setPlaceholderText("Height (px)")
        layout.addWidget(QLabel("Height:"))
        layout.addWidget(self.height_edit)

        self.aspect_ratio = QCheckBox("Maintain Aspect Ratio")
        self.aspect_ratio.setChecked(True)
        layout.addWidget(self.aspect_ratio)

        self.resize_button = QPushButton("Resize Images")
        self.resize_button.clicked.connect(self.resize_images)
        layout.addWidget(self.resize_button)

        self.folder_path = ""

    def select_folder(self):
        self.folder_path = QFileDialog.getExistingDirectory(self, "Select Image Folder")
        if self.folder_path:
            self.folder_button.setText(f"Selected: {os.path.basename(self.folder_path)}")

    def resize_images(self):
        if not self.folder_path:
            QMessageBox.warning(self, "Error", "Please select a folder.")
            return

        try:
            width = int(self.width_edit.text())
            height = int(self.height_edit.text())
        except ValueError:
            QMessageBox.warning(self, "Error", "Please enter valid width and height.")
            return

        if width <= 0 or height <= 0:
            QMessageBox.warning(self, "Error", "Width and height must be positive.")
            return

        date_str = datetime.now().strftime("%d%m%y_%H%M")
        aspect_str = "asp_on" if self.aspect_ratio.isChecked() else "asp_off"
        output_folder = os.path.join(self.folder_path, f"resized_{date_str}_{height}_{aspect_str}")
        os.makedirs(output_folder, exist_ok=True)

        for file_name in os.listdir(self.folder_path):
            if file_name.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')):
                image_path = os.path.join(self.folder_path, file_name)
                try:
                    with Image.open(image_path) as img:
                        if self.aspect_ratio.isChecked():
                            img.thumbnail((width, height), Image.Resampling.LANCZOS)
                        else:
                            img = img.resize((width, height), Image.Resampling.LANCZOS)
                        output_path = os.path.join(output_folder, f"resized_{date_str}_{height}_{aspect_str}_{file_name}")
                        img.save(output_path)
                except Exception as e:
                    QMessageBox.warning(self, "Error", f"Failed to process {file_name}: {str(e)}")

        QMessageBox.information(self, "Success", f"Images resized and saved to {output_folder}!")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = ResizeApp()
    window.show()
    sys.exit(app.exec())

Sunday, April 13, 2025

Friday, April 11, 2025

Python 3.13.0rc1 : pig for AI-powered automation.

Pig is an API for AI-powered automation of Windows desktop applications and workflows. It allows AI agents to remotely navigate user interfaces and interact with software on Windows computers, even for applications without public APIs. Organizations might use Pig to automate complex, repetitive tasks involving legacy enterprise software, overcome limitations of traditional robotic process automation tools, and reduce the need for manual data handling by human workers.
Follow the install from the official website.
pip install pig-python
Collecting pig-python
...
Successfully installed aiohttp-retry-2.9.1 click-8.1.8 iso8601-2.1.0 pig-python-0.1.2 simple-term-menu-1.6.6 tabulate-0.9.0
From the API Keys url get the key and run a server:
piglet join --secret the_API_key
Display dimensions: 1280x720
Local server running at http://localhost:3000
Connected to control server
The local machine can be found on this webpage.
I go to machine chat and I ask to create a source script in python for test the artificial intelligence.
It looks like you're encountering an error with the Pig API. The error message indicates that the API key hasn't been set properly. To resolve this issue, you need to set the PIG_SECRET_KEY environment variable or pass the API key directly to the Client constructor when initializing the Pig client. Would you like me to help you with setting up the environment variable or modifying your code to include the API key in the Client constructor? yes , I need a python script example
The remote artificial intelligence machine move the mouse and open notepad, add source code and save to my desktop.
I add my API key and I run it, see the result:
This source script is the result of artificial intelligence processing
"""
Example script showing how to properly initialize the Pig client with an API key
using both environment variable and direct constructor methods.
"""

import os
from pig import Client

# Example API key (replace with your actual API key in production)
API_KEY = "SK-..."

def initialize_with_env_variable():
    """
    Method 1: Initialize Pig client using environment variable
    This is the recommended approach for production environments
    """
    # Set the environment variable
    os.environ["PIG_SECRET_KEY"] = API_KEY
    
    # Initialize the client (it will automatically use the environment variable)
    client = Client()
    
    print("Client initialized using environment variable")
    return client

def initialize_with_direct_key():
    """
    Method 2: Initialize Pig client by passing the API key directly
    Useful for testing or when environment variables are not preferred
    """
    # Initialize the client by passing the API key directly to the constructor
    client = Client(api_key=API_KEY)
    
    print("Client initialized by passing API key directly")
    return client

if __name__ == "__main__":
    # Example 1: Using environment variable
    client1 = initialize_with_env_variable()
    
    # Example 2: Passing API key directly
    client2 = initialize_with_direct_key()
    
    # Both methods achieve the same result
    print("Both initialization methods are complete!")

Saturday, March 1, 2025

Python 3.13.0rc1 : testing the elevenlabs with artificial intelligence.

Today I teste the elevenlabs python package to use it with artifical inteligence to create sound.
I install this python package with pip tool, I created a python script file and the basic script run well with the api key from the official website.
pip install elevenlabs
Collecting elevenlabs
...
Installing collected packages: websockets, sniffio, pydantic-core, h11, annotated-types, pydantic, httpcore, anyio, httpx,
elevenlabs
Successfully installed annotated-types-0.7.0 anyio-4.8.0 elevenlabs-1.52.0 h11-0.14.0 httpcore-1.0.7 httpx-0.28.1 
pydantic-2.10.6the official website pydantic-core-2.27.2 sniffio-1.3.1 websockets-15.0
...
pip install playsound
Collecting playsound
...
Installing collected packages: playsound
Successfully installed playsound-1.3.0
...
python elevenlabs_test_001.py
Fișierul audio a fost salvat la generated_audio.mp3
This is the source code:
import io  # Importarea bibliotecii io
from elevenlabs import ElevenLabs
from playsound import playsound
import tempfile
import os

# API Key pentru ElevenLabs
api_key = "API_KEY"
voice_id = "JBFqnCBsd6RMkjVDRZzb"


# Configurarea clientului ElevenLabs
client = ElevenLabs(api_key=api_key )

# Textul pe care vrei să-l convertești în audio
text = 'Hello! This is a test without mpv.'

# Generarea audio
audio_generator = client.generate(text=text, voice=voice_id)

# Colectarea datelor din generator într-un obiect BytesIO
audio_data = io.BytesIO()
for chunk in audio_generator:
    audio_data.write(chunk)
audio_data.seek(0)  # Resetarea pointerului la începutul streamului

# Specificarea căii de salvare pentru fișierul audio
save_path = 'generated_audio.mp3'

# Salvarea audio într-un fișier temporar
with tempfile.NamedTemporaryFile(delete=False, suffix='.mp3') as temp_audio:
    temp_audio.write(audio_data.read())
    temp_audio_path = temp_audio.name

# Redarea fișierului audio utilizând playsound
playsound(temp_audio_path)

# Salvarea fișierului audio generat într-o locație specificată
with open(save_path, 'wb') as f:
    audio_data.seek(0)  # Resetarea pointerului la începutul streamului pentru a citi din nou datele
    f.write(audio_data.read())

print(f'Fișierul audio a fost salvat la {save_path}')

Saturday, February 8, 2025

Python 3.13.0rc1 : Testing python with Ollama local install.

I was very busy with development and testing for about two weeks and my laptop was stuck and I was working hard... Today I managed to test local background clipping on my laptop with a local Ollama installation separated by a Python module but with processing from the Python script. I also used Microsoft's Copilot artificial intelligence for python and it works well even though it is not theoretically specialized in development. The source code is quite large but the result is very good and fast:
import subprocess
import os
import json
from PIL import Image, ImageOps

class OllamaProcessor:
    def __init__(self, config_file):
        self.config_file = config_file
        self.model_methods = self.load_config()

    def load_config(self):
        try:
            with open(self.config_file, 'r') as file:
                config = json.load(file)
            print("Configuration loaded successfully.")
            return config
        except FileNotFoundError:
            print(f"Configuration file {self.config_file} not found.")
            raise
        except json.JSONDecodeError:
            print(f"Error decoding JSON from the configuration file {self.config_file}.")
            raise

    def check_ollama(self):
        try:
            result = subprocess.run(["ollama", "--version"], capture_output=True, text=True, check=True)
            print("Ollama is installed. Version:", result.stdout)
        except subprocess.CalledProcessError as e:
            print("Ollama is not installed or not found in PATH. Ensure it's installed and accessible.")
            raise
... 
Here is the result obtained after finishing running in the command line:
python ollama_test_001.py
Configuration file ollama_config.json created successfully.
Configuration loaded successfully.
Ollama is installed. Version: ollama version is 0.5.7

Available models: ['NAME']
pulling manifest
pulling 170370233dd5... 100% ▕██████████████▏ 4.1 GB
pulling 72d6f08a42f6... 100% ▕██████████████▏ 624 MB
pulling 43070e2d4e53... 100% ▕██████████████▏  11 KB
pulling c43332387573... 100% ▕██████████████▏   67 B
pulling ed11eda7790d... 100% ▕██████████████▏   30 B
pulling 7c658f9561e5... 100% ▕██████████████▏  564 B
verifying sha256 digest
writing manifest
success
Model llava pulled successfully for method process_images_in_folder.
Some "Command failed ..." but the result is cutting well and it has transparency !

Thursday, January 30, 2025

Blender 3D and python scripting - part 032.

Today I created an addon for Blender version 4.3.2 that allows me to select two folders to render 3D objects from the first folder and add 512px samples with these renderings to the second folder.
This is what the addon installed in Blender 3D looks like:
Here's what the source code of this addon looks like:
bl_info = {
    "name": "3D File Renderer by catafest",
    "blender": (4, 3, 2),
    "category": "Object",
    "author": "Catalin George Festila\n"
              "nicknames: catafest and mythcat\n"
              "country: Romania\n"
              "mail: catafest [at] yahoo.com",
    "version": (1, 0),
    "blender": (2, 80, 0),
    "location": "View3D > UI > 3D File Renderer",
    "description": "Addon for rendering 3D files",
    "warning": "",
    "doc_url": "https://github.com/catafest",
    "tracker_url": "https://github.com/catafest/issues",
    "support": "COMMUNITY",
}

import bpy
import os

class FileRendererProperties(bpy.types.PropertyGroup):
    input_directory: bpy.props.StringProperty(
        name="Input Directory",
        description="Directory containing 3D files",
        default="",
        maxlen=1024,
        subtype='DIR_PATH'
    )
    output_directory: bpy.props.StringProperty(
        name="Output Directory",
        description="Directory to save rendered images",
        default="",
        maxlen=1024,
        subtype='DIR_PATH'
    )

class RENDER_OT_files(bpy.types.Operator):
    bl_idname = "render.files"
    bl_label = "Start render 3D files for all files"
    
    def execute(self, context):
        input_directory = context.scene.file_renderer_props.input_directory
        output_directory = context.scene.file_renderer_props.output_directory
        
        if not input_directory or not output_directory:
            self.report({'ERROR'}, "Input and Output directories must be set.")
            return {'CANCELLED'}
        
        if not os.path.exists(output_directory):
            os.makedirs(output_directory)
        
        def render_file(file_path, output_path):
            try:
                bpy.ops.wm.read_factory_settings(use_empty=True)
                ext = os.path.splitext(file_path)[1].lower()
                if ext == ".glb":
                    bpy.ops.import_scene.gltf(filepath=file_path)
                elif ext == ".obj":
                    bpy.ops.import_scene.obj(filepath=file_path)
                elif ext == ".fbx":
                    bpy.ops.import_scene.fbx(filepath=file_path)
                else:
                    raise ValueError("Unsupported file format")
                
                bpy.ops.object.camera_add(location=(0, -3, 1.5), rotation=(1.1, 0, 0))
                camera = bpy.context.scene.objects['Camera']
                bpy.context.scene.camera = camera
                bpy.ops.object.light_add(type='POINT', location=(0, -3, 3))
                light = bpy.context.view_layer.objects.active
                light.data.energy = 1000
                
                bpy.context.scene.render.resolution_x = 512
                bpy.context.scene.render.resolution_y = 512
                bpy.context.scene.render.filepath = output_path
                bpy.ops.render.render(write_still=True)
            except Exception as e:
                # Generate a red image with "BAD FILE" text using Blender
                bpy.ops.wm.read_factory_settings(use_empty=True)
                bpy.ops.mesh.primitive_plane_add(size=2)
                plane = bpy.context.active_object
                mat = bpy.data.materials.new(name="BadFileMaterial")
                mat.diffuse_color = (1, 0, 0, 1)  # Red
                plane.data.materials.append(mat)
                
                # Add "BAD FILE" text
                bpy.ops.object.text_add(location=(0, 0, 0.1))
                text_obj = bpy.context.active_object
                text_obj.data.body = "BAD FILE"
                text_obj.data.size = 0.5
                text_obj.data.align_x = 'CENTER'
                text_obj.data.align_y = 'CENTER'
                text_obj.rotation_euler = (1.5708, 0, 0)
                
                # Set camera and light
                bpy.ops.object.camera_add(location=(0, -3, 1.5), rotation=(1.1, 0, 0))
                camera = bpy.context.scene.objects['Camera']
                bpy.context.scene.camera = camera
                bpy.ops.object.light_add(type='POINT', location=(0, -3, 3))
                light = bpy.context.view_layer.objects.active
                light.data.energy = 1000
                
                bpy.context.scene.render.resolution_x = 512
                bpy.context.scene.render.resolution_y = 512
                bpy.context.scene.render.filepath = output_path
                bpy.ops.render.render(write_still=True)
        
        for filename in os.listdir(input_directory):
            if filename.lower().endswith((".glb", ".obj", ".fbx")):
                file_path = os.path.join(input_directory, filename)
                output_path = os.path.join(output_directory, os.path.splitext(filename)[0] + ".png")
                render_file(file_path, output_path)
        
        self.report({'INFO'}, "Rendering of files is complete.")
        return {'FINISHED'}

class ABOUT_OT_dialog(bpy.types.Operator):
    bl_idname = "wm.about_dialog"
    bl_label = "About this addon"
    
    def execute(self, context):
        return context.window_manager.invoke_props_dialog(self)
    
    def draw(self, context):
        layout = self.layout
        layout.label(text="3D File Renderer by catafest")
        layout.label(text="Author: Catalin George Festila")
        layout.label(text="Nicknames: catafest and mythcat")
        layout.label(text="Country: Romania")
        layout.label(text="Email: catafest [at] yahoo.com")
        layout.operator("wm.url_open", text="LinkedIn").url = "https://www.linkedin.com/in/c%C4%83t%C4%83lin-george-fe%C8%99til%C4%83-05780a67"
        layout.operator("wm.url_open", text="Author Site").url = "https://sites.google.com/view/festila-george-catalin"
        layout.operator("wm.url_open", text="catafest GitHub").url = "https://github.com/catafest"
        layout.operator("wm.url_open", text="catafest-work GitHub").url = "https://github.com/catafest-work"

class FileRendererPanel(bpy.types.Panel):
    bl_label = "3D File Renderer by catafest"
    bl_idname = "OBJECT_PT_file_renderer"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'File Renderer'
    
    def draw(self, context):
        layout = self.layout
        scene = context.scene
        file_renderer_props = scene.file_renderer_props
        
        layout.prop(file_renderer_props, "input_directory")
        layout.prop(file_renderer_props, "output_directory")
        
        # Styling the render button
        render_button = layout.operator("render.files", text="Start render 3D files for all files")
        
        layout.separator()
        
        layout.operator("wm.about_dialog", text="About this addon")

def register():
    bpy.utils.register_class(FileRendererProperties)
    bpy.utils.register_class(RENDER_OT_files)
    bpy.utils.register_class(ABOUT_OT_dialog)
    bpy.utils.register_class(FileRendererPanel)
    bpy.types.Scene.file_renderer_props = bpy.props.PointerProperty(type=FileRendererProperties)

def unregister():
    bpy.utils.unregister_class(FileRendererProperties)
    bpy.utils.unregister_class(RENDER_OT_files)
    bpy.utils.unregister_class(ABOUT_OT_dialog)
    bpy.utils.unregister_class(FileRendererPanel)
    del bpy.types.Scene.file_renderer_props

if __name__ == "__main__":
    register()