analitics

Pages

Showing posts with label shutil. Show all posts
Showing posts with label shutil. Show all posts

Tuesday, June 9, 2026

Python Qt : Simple script to install the G'MIC archive to the Krita folder.

Today, this simple script will install the G'MIC archive to the Krita folder:
import sys
import os
import zipfile
import shutil
import subprocess
from PyQt6.QtWidgets import (
    QApplication, QWidget, QPushButton, QFileDialog,
    QVBoxLayout, QLabel, QMessageBox
)

class GMICInstaller(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("GMIC Installer for Krita")

        self.gmic_zip = ""
        self.krita_folder = ""

        layout = QVBoxLayout()

        self.label_zip = QLabel("GMIC archive: Not selected")
        self.label_krita = QLabel("Krita folder: Not selected")

        btn_zip = QPushButton("Select GMIC Archive (.zip)")
        btn_zip.clicked.connect(self.select_gmic_zip)

        btn_krita = QPushButton("Select Krita Folder")
        btn_krita.clicked.connect(self.select_krita_folder)

        btn_install = QPushButton("Install on Krita")
        btn_install.clicked.connect(self.install_gmic)

        layout.addWidget(self.label_zip)
        layout.addWidget(btn_zip)
        layout.addWidget(self.label_krita)
        layout.addWidget(btn_krita)
        layout.addWidget(btn_install)

        self.setLayout(layout)

    def select_gmic_zip(self):
        file, _ = QFileDialog.getOpenFileName(self, "Select GMIC ZIP", "", "ZIP Files (*.zip)")
        if file:
            self.gmic_zip = file
            self.label_zip.setText(f"GMIC archive: {file}")

    def select_krita_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "Select Krita Folder")
        if folder:
            self.krita_folder = folder
            self.label_krita.setText(f"Krita folder: {folder}")

    def install_gmic(self):
        if not self.gmic_zip or not self.krita_folder:
            QMessageBox.warning(self, "Error", "Select both GMIC archive and Krita folder first.")
            return

        # Step 1: Extract GMIC ZIP
        extract_path = os.path.join(os.getcwd(), "gmic_extracted")
        if os.path.exists(extract_path):
            shutil.rmtree(extract_path)
        os.makedirs(extract_path)

        with zipfile.ZipFile(self.gmic_zip, 'r') as zip_ref:
            zip_ref.extractall(extract_path)

        # Step 2: Find GMIC folder inside extracted content
        gmic_folder = None
        for root, dirs, files in os.walk(extract_path):
            if "gmic_krita_qt.dll" in files or "gmic_qt.exe" in files:
                gmic_folder = root
                break

        if not gmic_folder:
            QMessageBox.critical(self, "Error", "GMIC plugin files not found in archive.")
            return

        # Step 3: Find Krita plugin folders
        possible_paths = [
            os.path.join(self.krita_folder, "share", "krita", "pykrita"),
            os.path.join(self.krita_folder, "lib", "krita", "plugins"),
            os.path.join(os.getenv("APPDATA"), "krita", "pykrita")
        ]

        installed = False

        for path in possible_paths:
            if os.path.exists(path):
                try:
                    shutil.copytree(gmic_folder, os.path.join(path, "gmic_qt"), dirs_exist_ok=True)
                    installed = True
                except Exception as e:
                    print("Copy error:", e)

        if not installed:
            QMessageBox.critical(self, "Error", "Could not find a valid Krita plugin folder.")
            return

        # Step 4: Launch Krita
        krita_bin = os.path.join(self.krita_folder, "bin", "krita.exe")
        if os.path.exists(krita_bin):
            subprocess.Popen([krita_bin])
        else:
            QMessageBox.warning(self, "Warning", "GMIC installed, but Krita executable not found.")

        QMessageBox.information(self, "Success", "GMIC successfully installed into Krita!")

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

Saturday, May 16, 2026

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

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

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

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

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

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

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

shutil.copytree(pip_src, target)

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

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

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

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

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

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

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

Saturday, October 18, 2025

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

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, March 22, 2023

Python 3.11.0 : clean from frequent folder and the list of recent files.

This python script that clears all entries in the Windows File Explorer from frequent folder and the list of recent files:
import os
import shutil

# Quick Access folder path on Windows
quick_access_path = os.path.join(os.environ['USERPROFILE'], 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Recent', 'AutomaticDestinations')

# List all files in the Quick Access folder
files = os.listdir(quick_access_path)
print(files)
# Loop through all files in the Quick Access folder
for file in files:
    # Check if the file name contains "tmp" or "temp"
    if 'tmp' in file.lower() or 'temp' in file.lower():
        # Construct the full file path
        file_path = os.path.join(quick_access_path, file)
        # Delete the file
        os.remove(file_path)
        # Print a message to the console
        print(f"{file_path} deleted successfully.")

# Clear Frequent folder
frequent_folder = os.path.join(os.environ['APPDATA'], 'Microsoft', 'Windows', 'Recent', 'AutomaticDestinations')
os.system('del /f /q "{}\*"'.format(frequent_folder))

# Clear Recent files list
recent_folder = os.path.join(os.environ['APPDATA'], 'Microsoft', 'Windows', 'Recent')
os.system('del /f /q "{}\*"'.format(recent_folder))

Sunday, October 21, 2018

The shutil python module.

The shutil module helps you to accomplish tasks, such as: copying, moving, or removing directory trees.
This python script creates a zip file in the current directory containing all contents of dir and then clears dir.

import shutil
from os import makedirs

def zip(out_fileName, dir):
shutil.make_archive(str(out_fileName), 'zip', dir)
shutil.rmtree(dir)
makedirs(dir[:-1])