analitics

Pages

Showing posts with label python packages. Show all posts
Showing posts with label python packages. Show all posts

Saturday, March 15, 2025

Python Qt6 : Dependency checker for python packages with pipdeptree and PyQt6.

Today I created this python script to test and check python package dependency.
You need to install the pipdeptree with the pip tool.
I used the Python 3.13.0rc1 version and the result is this:
This is the source code I used:
import sys
import subprocess
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QVBoxLayout, QTreeWidget, QTreeWidgetItem, QWidget
)
from PyQt6.QtWidgets import QHeaderView
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import QStyle


class DependencyViewer(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Dependency Checker")

        # Maximizarea ferestrei la lansare
        self.showMaximized()

        # Creează un QTreeWidget pentru afișarea dependențelor
        self.tree_widget = QTreeWidget()
        self.tree_widget.setHeaderLabels(["Dependency", "Status"])

        # Ajustează aliniamentul central pentru fiecare coloană
        for i in range(2):  # Pentru cele două coloane
            self.tree_widget.headerItem().setTextAlignment(i, Qt.AlignmentFlag.AlignCenter)

        # Configurarea automată a lățimii coloanelor
        self.tree_widget.header().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)

        # Layout
        layout = QVBoxLayout()
        layout.addWidget(self.tree_widget)

        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

        # Obține și afișează dependențele
        self.display_dependencies()

    def display_dependencies(self):
        try:
            # Rulează pipdeptree pentru a obține ierarhia dependențelor
            result = subprocess.run(['pipdeptree', '--warn', 'silence'], capture_output=True, text=True)
            dependencies = result.stdout.splitlines()

            for line in dependencies:
                # Determină nivelul de indentare pentru ierarhia dependențelor
                indent_level = len(line) - len(line.lstrip())
                dependency_name = line.strip()

                # Creează un item pentru fiecare dependență
                item = QTreeWidgetItem([dependency_name])

                # Atribuie iconițe pe baza compatibilității (exemplu simplificat)
                if "(*)" in dependency_name:  # Exemplu de incompatibilitate (poți schimba după caz)
                    item.setIcon(0, self.style().standardIcon(QStyle.StandardPixmap.SP_DialogCancelButton))
                    item.setText(1, "Incompatible")
                else:
                    item.setIcon(0, self.style().standardIcon(QStyle.StandardPixmap.SP_DialogApplyButton))
                    item.setText(1, "Compatible")

                # Adaugă item-ul în arbore
                if indent_level == 0:
                    self.tree_widget.addTopLevelItem(item)
                else:
                    # Alege ultimul item părinte și adaugă dependența ca sub-item
                    parent_item = self.tree_widget.topLevelItem(self.tree_widget.topLevelItemCount() - 1)
                    parent_item.addChild(item)

            # Extinde toate elementele din arbore
            self.tree_widget.expandAll()

        except Exception as e:
            error_item = QTreeWidgetItem(["Error", str(e)])
            error_item.setIcon(0, self.style().standardIcon(QStyle.StandardPixmap.SP_MessageBoxCritical))
            self.tree_widget.addTopLevelItem(error_item)


if __name__ == "__main__":
    from PyQt6.QtCore import Qt

    app = QApplication(sys.argv)
    viewer = DependencyViewer()
    viewer.show()
    sys.exit(app.exec())

Wednesday, March 12, 2025

Python 3.13.0rc1 : testing the new Kivy-2.3.1 with default examples.

Today I tested the Kivy-2.3.1 with Python 3.13.0rc1.
Read more on the official webpage.
The install step is easy with the pip tool:
python -m pip install kivy
Collecting kivy
...
Installing collected packages: kivy-deps.sdl2, kivy-deps.glew, kivy-deps.angle, filetype, pypiwin32, pygments, docutils, Kivy-Garden, kivy
Successfully installed Kivy-Garden-0.1.5 docutils-0.21.2 filetype-1.2.0 kivy-2.3.1 kivy-deps.angle-0.4.0 kivy-deps.glew-0.3.1 kivy-deps.sdl2-0.8.0 pygments-2.19.1 pypiwin32-223
Let's install the kivy examples source code:
python -m pip install --pre "kivy[base]" kivy_examples
Collecting kivy_examples
...
Installing collected packages: kivy_examples
Successfully installed kivy_examples-2.3.1
You can test these examples with this command into the python folder:
C:\Python313\share\kivy-examples>python demo\showcase\main.py
[WARNING] [Config      ] Older configuration version detected (0 instead of 27)
[WARNING] [Config      ] Upgrading configuration in progress.
[DEBUG  ] [Config      ] Upgrading from 0 to 1
This is the result:

Monday, March 10, 2025

News : Ollama-Adaptive-Image-Code-Gen project test - need more memory.

Python code that leverages a language model (such as LLaMA) to generate images featuring basic shapes in 2D or 3D. The script randomly selects shapes, colors, and areas to create diverse visuals. It continuously generates images based on AI-generated code, validates them, and provides feedback for iterative improvements.
This source code can be found on this GitHub project named Ollama-Adaptive-Image-Code-Gen.
You can download this and use these commands to run and test this feature of create and generate images with ollama.
NOTE: Compose up process might upto 20 - 25 Mins. first time. Because it will download all the respective ModelFiles
Let's start:
git clone https://github.com/jaypatel15406/Ollama-Adaptive-Image-Code-Gen.git
Cloning into 'Ollama-Adaptive-Image-Code-Gen'...
Resolving deltas: 100% (30/30), done.

cd Ollama-Adaptive-Image-Code-Gen

Ollama-Adaptive-Image-Code-Gen>pip3 install -r requirements.txt
Collecting ollama (from -r requirements.txt (line 1))
...
Installing collected packages: propcache, multidict, frozenlist, aiohappyeyeballs, yarl, aiosignal, ollama, 
aiohttp
Successfully installed aiohappyeyeballs-2.4.8 aiohttp-3.11.13 aiosignal-1.3.2 frozenlist-1.5.0 multidict-6.1.0
ollama-0.4.7 propcache-0.3.0 yarl-1.18.3

Ollama-Adaptive-Image-Code-Gen>python main.py
 utility : pull_model_instance : Instansiating 'llama3.1' ...
 utility : pull_model_instance : 'llama3.1' Model Fetching Status : pulling manifest
 utility : pull_model_instance : 'llama3.1' Model Fetching Status : pulling 667b0c1932bc
 
Seams to work but I need more memory:
Ollama-Adaptive-Image-Code-Gen>python main.py
 utility : pull_model_instance : Instansiating 'llama3.1' ...
 utility : pull_model_instance : 'llama3.1' Model Fetching Status : pulling manifest
 utility : pull_model_instance : 'llama3.1' Model Fetching Status : pulling 667b0c1932bc
 utility : pull_model_instance : 'llama3.1' Model Fetching Status : pulling 948af2743fc7
 utility : pull_model_instance : 'llama3.1' Model Fetching Status : pulling 0ba8f0e314b4
 utility : pull_model_instance : 'llama3.1' Model Fetching Status : pulling 56bb8bd477a5
 utility : pull_model_instance : 'llama3.1' Model Fetching Status : pulling 455f34728c9b
 utility : pull_model_instance : 'llama3.1' Model Fetching Status : verifying sha256 digest
 utility : pull_model_instance : 'llama3.1' Model Fetching Status : writing manifest
 utility : pull_model_instance : 'llama3.1' Model Fetching Status : success

=========================================================================================

 utility : get_prompt_response : Prompt : Choose the dimension of the shape: '2D' or '3D'. NOTE: Return only the chosen dimension.
ERROR:root: utility : get_prompt_response : Error : model requires more system memory (5.5 GiB) than is available (5.1 GiB) (status code: 500) ... 

Python 3.13.0rc1 : Python script as window 10 service.

The main reason I tested this python issues comes from this:
I have a strange usage of msedgewebview2.exe on my window 10.
The right way was to install but I cannot do that because is not my laptop.
I created a python script to make logs and kill this process:
import win32serviceutil
import servicemanager
import win32event
import win32service
import time
import logging
import psutil

class MyService(win32serviceutil.ServiceFramework):
    _svc_name_ = 'catafestService'
    _svc_display_name_ = 'My catafest Service tool'

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        self.stop_requested = False
        logging.basicConfig(filename='D:\\PythonProjects\\catafest_services\\logfile.log', level=logging.DEBUG,
                            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        self.stop_requested = True
        logging.info('Service stop requested')

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_, ''))
        logging.info('Service started')
        self.main()

    def main(self):
        while not self.stop_requested:
            try:
                # Check and terminate 'msedgewebview2.exe' process
                for proc in psutil.process_iter(['pid', 'name']):
                    if proc.info['name'] == 'msedgewebview2.exe':
                        proc.kill()
                        logging.info(f'Terminated process: {proc.info["name"]} (PID: {proc.info["pid"]})')
                logging.info('Service running...')
                time.sleep(10)  # Runs every 10 seconds
            except Exception as e:
                logging.error(f'Error occurred: {e}', exc_info=True)
                servicemanager.LogMsg(servicemanager.EVENTLOG_ERROR_TYPE,
                                      servicemanager.PYS_SERVICE_STOPPED,
                                      (self._svc_name_, f'Error occurred: {e}'))
                break
        logging.info('Service stopped')

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(MyService)
You need to install pyinstaller to make a exe file:
pip install pyinstaller
...
Successfully installed altgraph-0.17.4 pefile-2023.2.7 pyinstaller-6.12.0 pyinstaller-hooks-contrib-2025.1
pywin32-ctypes-0.2.3
The next step install the pywin32 python package:
pip install pywin32
... try to make executable:
pyinstaller --onefile catafest_services.py
... use the start:
catafest_services.exe start
Traceback (most recent call last):
  ...
  File "win32serviceutil.py", line 706, in HandleCommandLine
  File "win32serviceutil.py", line 624, in GetServiceClassString
ModuleNotFoundError: No module named 'win32timezone'
[PYI-6880:ERROR] Failed to execute script 'catafest_services' due to unhandled exception!
... this error comes from interaction on running with windows services:
try to fix with this:
pyinstaller --name=catafest_services --onefile catafest_services.py
Open the generated spec file catafest_services.spec and add the win32timezone module to the hidden imports. See:
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None

a = Analysis(['catafest_services.py'],
             pathex=['D:\\PythonProjects\\catafest_services'],
             binaries=[],
             datas=[],
             hiddenimports=['win32timezone'],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
             
# The rest of the file remains unchanged
Rebuild the executable using the modified spec file:
pyinstaller catafest_services.spec
709 INFO: PyInstaller: 6.12.0, contrib hooks: 2025.1
711 INFO: Python: 3.13.0rc1
791 INFO: Platform: Windows-10-10.0.19045-SP0
791 INFO: Python environment: C:\Python313
...
Try again with start:
catafest_services.exe start
I got this output:
catafest_services.exe start
Starting service catafestService 
Error starting service: Access is denied.
Open the command prompt as administrator and try again:
catafest_services.exe start
Starting service catafestService
Error starting service: The service did not respond to the start or control request in a timely fashion.
Same interaction because try to kill a process and more ...
This not work, but is another good way to use it by using the debug:
catafest_services.exe debug
Debugging service catafestService - press Ctrl+C to stop.
I n f o   0 x 4 0 0 0 1 0 0 2   -   T h e   c a t a f e s t S e r v i c e   s   r v i c e   h a s   s t a r t e d .
 Stopping debug service.
Under debug all works well, some msedgewebview2 processes are gone.
I stop this run and I try to work on development ...
Next step: If all works then you can use install:
catafest_services.exe install
Installing service catafestService
Service installed
You can do more. Make sure the account running the service has the necessary permissions. You can specify a user account during service installation:
catafest_services.exe install --username your_domain\your_username --password your_password
You can use this command to see these info:
set user
Last step: You can open the Services Manager by typing services.msc in the Windows Run dialog. Use press Win + R keys to open the Run dialog.
Manage the service as any services. You can use even on command prompt:
catafest_services.exe
Usage: 'catafest_services.exe [options] install|update|remove|start [...]|stop|restart [...]|debug [...]'t_services>cd dist
Options for 'install' and 'update' commands only:
 --username domain\username : The Username the service is to run under
 --password password : The password for the username
 --startup [manual|auto|disabled|delayed] : How the service starts, default = manual
 --interactive : Allow the service to interact with the desktop.
 --perfmonini file: .ini file to use for registering performance monitor data
 --perfmondll file: .dll file to use when querying the service for
   performance data, default = perfmondata.dll
Options for 'start' and 'stop' commands only:
 --wait seconds: Wait for the service to actually start or stop.
                 If you specify --wait with the 'stop' option, the service
                 and all dependent services will be stopped, each waiting
                 the specified period.

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}')

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

Tuesday, January 28, 2025

Blender 3D and python scripting - part 031.

This python script for blender will select vertrices based on boolean ...
This is the result with Blender version 4.3 :
This is the python script:
import bpy, bmesh

obj = bpy.context.active_object
me = obj.data

bpy.ops.object.mode_set(mode = 'EDIT') 
bpy.ops.mesh.select_mode(type="VERT")


bm = bmesh.from_edit_mesh(obj.data)
selected = [False,False,True,True,True,True,True,True]
verts = [vert for vert in bpy.context.active_object.data.vertices if vert.select]
all = [vert for vert in bpy.context.active_object.data.vertices]
print("selected:",len(verts))
print("all:",len(all))
bpy.ops.object.mode_set(mode = 'OBJECT')
me.vertices.foreach_set(
"select",
selected
)
bpy.ops.object.mode_set(mode = 'EDIT')

Thursday, January 23, 2025

Blender 3D and python scripting - part 030.

Today I will show a python script for Blender 3D that create an 3D object and one material.
This is the final result of this python script:
This is the python script:
# give Python access to Blender's functionality
import bpy

# extend Python's math functionality
import math


# extend Python functionality to generate random numbers
import random


def partially_clean_the_scene():
    # select all object in the scene
    bpy.ops.object.select_all(action="SELECT")

    # delete all selected objects in the scene
    bpy.ops.object.delete()

    # make sure we remove data that was connected to the objects we just deleted
    bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)


def create_noise_mask(material):
    """Add a set of nodes to create a noise mask using:
    * Texture Coordinate node
    * Mapping node
    * Noise Texture node
    * Color Ramp node
    """
    node_location_x_step = 300
    node_location_x = -node_location_x_step

    # create a Color Ramp node
    # https://docs.blender.org/api/current/bpy.types.ShaderNodeValToRGB.html
    color_ramp_node = material.node_tree.nodes.new(type="ShaderNodeValToRGB")
    color_ramp_node.color_ramp.elements[0].position = 0.45
    color_ramp_node.color_ramp.elements[1].position = 0.5
    color_ramp_node.location.x = node_location_x
    node_location_x -= node_location_x_step

    # create a Noise Texture node
    # https://docs.blender.org/api/current/bpy.types.ShaderNodeTexNoise.html#bpy.types.ShaderNodeTexNoise
    noise_texture_node = material.node_tree.nodes.new(type="ShaderNodeTexNoise")
    noise_texture_node.inputs["Scale"].default_value = random.uniform(1.0, 20.0)
    noise_texture_node.location.x = node_location_x
    node_location_x -= node_location_x_step

    # create a Mapping node
    # https://docs.blender.org/api/current/bpy.types.ShaderNodeMapping.html#bpy.types.ShaderNodeMapping
    mapping_node = material.node_tree.nodes.new(type="ShaderNodeMapping")
    mapping_node.inputs["Rotation"].default_value.x = math.radians(random.uniform(0.0, 360.0))
    mapping_node.inputs["Rotation"].default_value.y = math.radians(random.uniform(0.0, 360.0))
    mapping_node.inputs["Rotation"].default_value.z = math.radians(random.uniform(0.0, 360.0))
    mapping_node.location.x = node_location_x
    node_location_x -= node_location_x_step

    # create a Texture Coordinate node
    texture_coordinate_node = material.node_tree.nodes.new(type="ShaderNodeTexCoord")
    texture_coordinate_node.location.x = node_location_x

    # connect the nodes
    # https://docs.blender.org/api/current/bpy.types.NodeTree.html#bpy.types.NodeTree
    # https://docs.blender.org/api/current/bpy.types.NodeLinks.html#bpy.types.NodeLinks
    material.node_tree.links.new(noise_texture_node.outputs["Color"], color_ramp_node.inputs["Fac"])
    material.node_tree.links.new(mapping_node.outputs["Vector"], noise_texture_node.inputs["Vector"])
    material.node_tree.links.new(texture_coordinate_node.outputs["Generated"], mapping_node.inputs["Vector"])

    return color_ramp_node


def create_material(name):
    # create new material
    material = bpy.data.materials.new(name=name)
    # enable creating a material via nodes
    material.use_nodes = True

    # get a reference to the Principled BSDF shader node
    principled_bsdf_node = material.node_tree.nodes["Principled BSDF"]

    # set the base color of the material
    principled_bsdf_node.inputs["Base Color"].default_value = (0.8, 0.120827, 0.0074976, 1)

    # set the metallic value of the material
    principled_bsdf_node.inputs["Metallic"].default_value = 1.0

    color_ramp_node = create_noise_mask(material)

    material.node_tree.links.new(color_ramp_node.outputs["Color"], principled_bsdf_node.inputs["Roughness"])

    return material

def add_mesh():
    # create an ico sphere
    bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=5)
    # shade smooth
    bpy.ops.object.shade_smooth()
    # get reference to mesh object
    mesh_obj = bpy.context.active_object

    return mesh_obj

def main():

    partially_clean_the_scene()

    name = "my_generated_material"
    material = create_material(name)

    mesh_obj = add_mesh()

    # apply the material to the mesh object
    mesh_obj.data.materials.append(material)

main()

Tuesday, January 7, 2025

Python 3.13.0rc1 : Simple convert all webp files from folder.

Today, a simple script is used to convert WEBP to PNG files from a defined folder.
The script reads a folder path for each WEBP file that is opened and saved as a PNG file.
import os
import sys
from PIL import Image

def convert_webp_to_png(directory):
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith(".webp"):
                webp_path = os.path.join(root, file)
                png_path = os.path.splitext(webp_path)[0] + ".png"
                with Image.open(webp_path) as img:
                    img.save(png_path, "PNG")
                print(f"webp to png file: {webp_path} -> {png_path}")

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("How to use: python convert.py path_to_folder_with_webp_files")
        sys.exit(1)

    directory = sys.argv[1]
    if not os.path.isdir(directory):
        print(f"{directory} folder is not valid.")
        sys.exit(1)

    convert_webp_to_png(directory)
    print("Finished !")

Sunday, December 29, 2024

Python 3.13.0 : Only the unstructured library in Fedora 41 ...

The unstructured library provides open-source components for ingesting and pre-processing images and text documents, such as PDFs, HTML, Word docs, and many more. The use cases of unstructured revolve around streamlining and optimizing the data processing workflow for LLMs. unstructured modular functions and connectors form a cohesive system that simplifies data ingestion and pre-processing, making it adaptable to different platforms and efficient in transforming unstructured data into structured outputs.
I used pip tool to install this python package:
mythcat@fedora:~$ pip install unstructured
Defaulting to user installation because normal site-packages is not writeable
Collecting unstructured
  Downloading unstructured-0.11.8-py3-none-any.whl.metadata (26 kB)
...
Successfully installed aiofiles-24.1.0 annotated-types-0.7.0 anyio-4.7.0 backoff-2.2.1 beautifulsoup4-4.12.3 chardet-5.2.0 click-8.1.8 cryptography-44.0.0 dataclasses-json-0.6.7 emoji-2.14.0 eval-type-backport-0.2.2 filetype-1.2.0 h11-0.14.0 httpcore-1.0.7 httpx-0.28.1 jsonpath-python-1.0.6 langdetect-1.0.9 marshmallow-3.23.2 mypy-extensions-1.0.0 nest-asyncio-1.6.0 nltk-3.9.1 pydantic-2.9.2 pydantic-core-2.23.4 pypdf-5.1.0 python-iso639-2024.10.22 python-magic-0.4.27 rapidfuzz-3.11.0 requests-toolbelt-1.0.0 sniffio-1.3.1 soupsieve-2.6 tabulate-0.9.0 tqdm-4.67.1 typing-extensions-4.12.2 typing-inspect-0.9.0 unstructured-0.11.8 unstructured-client-0.28.1 wrapt-1.17.0
But I got error on the unstructured-inference:
pip install unstructured-inference
Collecting unstructured-inference
...
  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for onnx
Failed to build onnx
ERROR: ERROR: Failed to build installable wheels for some pyproject.toml based projects (onnx)
... and errors on unstructured[pdf]:
mythcat@fedora:~$ pip install unstructured[pdf]
...
  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for onnx
Failed to build onnx
ERROR: ERROR: Failed to build installable wheels for some pyproject.toml based projects (onnx)

Monday, December 16, 2024

Python 3.12.8 : Install quil in Fedora 41.

The quil python package on Fedora 41 can be used with the 3.12.8 version of python :
Python 3.12.8 (main, Dec  6 2024, 00:00:00) [GCC 14.2.1 20240912 (Red Hat 14.2.1-3)] on linux
mythcat@localhost:~$ python3.12 -m pip install quil --user
First, install the python version then install the pip tool:
mythcat@localhost:~$ curl https://bootstrap.pypa.io/get-pip.py | python3.12 -
...
Installing collected packages: pip
Successfully installed pip-24.3.1
Next, install with the pip version:
mythcat@localhost:~$ python3.12 -m pip install quil --user
Collecting quil

Saturday, December 14, 2024

Python 3.13.0 : OpenCV - part 002.

The PyQt6 python package and Fedora works great. I tested today this source code with opencv, numpy, PyQt6 python packages.
I install opencv python package with dnf5 tool:
root@localhost:/home/mythcat# dnf5 install  python3-opencv.x86_64
The source code let you to open, change a image and save using sliders and a reset option.
This is the source code:
import sys
import cv2
import numpy as np
from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QSlider, QFileDialog, QPushButton, QHBoxLayout
from PyQt6.QtGui import QImage, QPixmap
from PyQt6.QtCore import Qt, pyqtSlot

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Real-Time Color Selection")
        self.setGeometry(100, 100, 1200, 800)

        # Create central widget and main layout
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)

        # Create image label
        self.image_label = QLabel()
        main_layout.addWidget(self.image_label)

        # Initialize sliders
        self.lower_h = QSlider(Qt.Orientation.Horizontal)
        self.lower_s = QSlider(Qt.Orientation.Horizontal)
        self.lower_v = QSlider(Qt.Orientation.Horizontal)
        self.upper_h = QSlider(Qt.Orientation.Horizontal)
        self.upper_s = QSlider(Qt.Orientation.Horizontal)
        self.upper_v = QSlider(Qt.Orientation.Horizontal)

        # Set slider ranges
        for slider in [self.lower_h, self.upper_h]:
            slider.setRange(0, 179)
        for slider in [self.lower_s, self.lower_v, self.upper_s, self.upper_v]:
            slider.setRange(0, 255)

        # Set initial slider values
        self.lower_h.setValue(50)
        self.lower_s.setValue(100)
        self.lower_v.setValue(50)
        self.upper_h.setValue(130)
        self.upper_s.setValue(255)
        self.upper_v.setValue(255)

        # Connect sliders to update function
        self.lower_h.valueChanged.connect(self.update_hsv_range)
        self.lower_s.valueChanged.connect(self.update_hsv_range)
        self.lower_v.valueChanged.connect(self.update_hsv_range)
        self.upper_h.valueChanged.connect(self.update_hsv_range)
        self.upper_s.valueChanged.connect(self.update_hsv_range)
        self.upper_v.valueChanged.connect(self.update_hsv_range)

        # Create slider layouts with labels
        sliders_layout = QVBoxLayout()
        
        # Add slider pairs with labels
        slider_pairs = [
            ("Lower Hue", self.lower_h),
            ("Lower Saturation", self.lower_s),
            ("Lower Value", self.lower_v),
            ("Upper Hue", self.upper_h),
            ("Upper Saturation", self.upper_s),
            ("Upper Value", self.upper_v)
        ]

        for label_text, slider in slider_pairs:
            row_layout = QHBoxLayout()
            label = QLabel(label_text)
            label.setMinimumWidth(120)
            row_layout.addWidget(label)
            row_layout.addWidget(slider)
            sliders_layout.addLayout(row_layout)

        main_layout.addLayout(sliders_layout)

        # Add buttons
        button_layout = QHBoxLayout()
        
        self.reset_button = QPushButton("Reset Values")
        self.reset_button.clicked.connect(self.reset_values)
        button_layout.addWidget(self.reset_button)

        self.open_image_button = QPushButton("Open Image")
        self.open_image_button.clicked.connect(self.open_image)
        button_layout.addWidget(self.open_image_button)

        self.save_button = QPushButton("Save Image")
        self.save_button.clicked.connect(self.save_image)
        button_layout.addWidget(self.save_button)
        main_layout.addLayout(button_layout)

        # Process initial image
        self.process_image()

    def process_image(self):
        image_bgr = cv2.imread("image.png")
        if image_bgr is None:
            image_bgr = cv2.imread("default_image.png")
        
        self.image_bgr = image_bgr
        self.image_hsv = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2HSV)

        # Create initial mask using current slider values
        lower_values = np.array([self.lower_h.value(), self.lower_s.value(), self.lower_v.value()])
        upper_values = np.array([self.upper_h.value(), self.upper_s.value(), self.upper_v.value()])
        
        mask_test = cv2.inRange(self.image_hsv, lower_values, upper_values)
        image_bgr_masked = cv2.bitwise_and(image_bgr, image_bgr, mask=mask_test)
        self.image_rgb = cv2.cvtColor(image_bgr_masked, cv2.COLOR_BGR2RGB)
        self.update_image()

    def update_image(self):
        height, width, channel = self.image_rgb.shape
        bytes_per_line = width * channel
        q_image = QImage(self.image_rgb.data, width, height, bytes_per_line, QImage.Format.Format_RGB888)
        pixmap = QPixmap.fromImage(q_image)
        self.image_label.setPixmap(pixmap.scaled(700, 500, Qt.AspectRatioMode.KeepAspectRatio))

    def update_hsv_range(self):
        lower_values = np.array([self.lower_h.value(), self.lower_s.value(), self.lower_v.value()])
        upper_values = np.array([self.upper_h.value(), self.upper_s.value(), self.upper_v.value()])
        mask_test = cv2.inRange(self.image_hsv, lower_values, upper_values)
        image_bgr_masked = cv2.bitwise_and(self.image_bgr, self.image_bgr, mask=mask_test)
        self.image_rgb = cv2.cvtColor(image_bgr_masked, cv2.COLOR_BGR2RGB)
        self.update_image()

    def reset_values(self):
        self.lower_h.setValue(50)
        self.lower_s.setValue(100)
        self.lower_v.setValue(50)
        self.upper_h.setValue(130)
        self.upper_s.setValue(255)
        self.upper_v.setValue(255)

    def open_image(self):
        filename, _ = QFileDialog.getOpenFileName(self, "Select Image File", "", "Image Files (*.png *.jpg *.jpeg)")
        if filename:
            self.image_bgr = cv2.imread(filename)
            if self.image_bgr is not None:
                self.image_hsv = cv2.cvtColor(self.image_bgr, cv2.COLOR_BGR2HSV)
                self.update_hsv_range()  # This will apply current filter and update display

    def save_image(self):
        filename, _ = QFileDialog.getSaveFileName(self, "Save Image", "", "PNG Files (*.png);;JPEG Files (*.jpg)")
        if filename:
            # Make sure filename has an extension
            if not filename.endswith(('.png', '.jpg', '.jpeg')):
                filename += '.png'
            # Convert and save
            output_image = cv2.cvtColor(self.image_rgb, cv2.COLOR_RGB2BGR)
            cv2.imwrite(filename, output_image)

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

Sunday, November 24, 2024

Python 3.13.0 : emoji symbols with PIL.

Today I want to use emoji symbols and I wrote this python script:
from PIL import Image, ImageDraw, ImageFont
import os

# Font size and image dimensions
font_size = 88
width = 640
height = 480

# Use Symbola.ttf from current directory
font_path = "Symbola.ttf"

# Create image
img = Image.new('RGB', (width, height), color='white')
draw = ImageDraw.Draw(img)

# Get font
font = ImageFont.truetype(font_path, font_size)

# Emoji matrix
emoji_matrix = [
    ['😀', '😁', '😂', '🤣', '😃'],
    ['😄', '😅', '😆', '😇', '😈'],
    ['😉', '😊', '😋', '😌', '😍'],
    ['😎', '😏', '😐', '😑', '😒']
]

# Calculate spacing
x_spacing = font_size + 10
y_spacing = font_size + 10

# Calculate starting position to center the grid
start_x = (width - (len(emoji_matrix[0]) * x_spacing)) // 2
start_y = (height - (len(emoji_matrix) * y_spacing)) // 2

# Draw emojis
for i, row in enumerate(emoji_matrix):
    for j, emoji in enumerate(row):
        x = start_x + (j * x_spacing)
        y = start_y + (i * y_spacing)
        draw.text((x, y), emoji, font=font, fill='black')

# Save the image
img.save('emoji_art.png')
print("Emoji art has been created successfully! Check emoji_art.png")
The result image named emoji_art.png is this:

Wednesday, November 20, 2024

Python 3.13.0 : generates multiple deformed polygonal shapes .

Today I created this source code in python that generates eight random convex polygons. The idea was to create sprites for a 2D game: snowballs, boulders, or similar objects... Obviously I also used Sonet 3.5 artificial intelligence. You can find the source code on the pagure account in fedora.
#!/usr/bin/env python3
"""
SVG Polygon Generator

This script generates multiple deformed polygonal shapes and saves them as separate SVG files.
Each polygon maintains convex properties while having controlled random deformations.

Features:
    - Generates 8 unique polygonal shapes
    - Controls deformation through radial and angular factors
    - Maintains convex properties
    - Exports each shape to a separate SVG file
    - Uses random colors for visual distinction

Usage:
    python generate_svgs.py

Output:
    Creates 8 SVG files named 'polygon_1.svg' through 'polygon_8.svg'
"""

from lxml import etree
import random
import math
from pathlib import Path


def create_svg_root():
    """Create and return a base SVG root element with standard attributes."""
    root = etree.Element("svg")
    root.set("width", "500")
    root.set("height", "500")
    root.set("xmlns", "http://www.w3.org/2000/svg")
    return root


def calculate_points(center_x: float, center_y: float, radius: float, 
                    num_sides: int, deform_factor: float) -> list:
    """
    Calculate polygon points with controlled deformation.

    Args:
        center_x: X coordinate of polygon center
        center_y: Y coordinate of polygon center
        radius: Base radius of the polygon
        num_sides: Number of polygon sides
        deform_factor: Maximum allowed deformation factor

    Returns:
        List of tuples containing (x, y) coordinates
    """
    points = []
    angle_step = 2 * math.pi / num_sides
    
    for i in range(num_sides):
        angle = i * angle_step
        radial_deform = random.uniform(-deform_factor, deform_factor)
        angular_deform = random.uniform(-deform_factor/2, deform_factor/2)
        
        modified_angle = angle + angular_deform
        modified_radius = radius * (1 + radial_deform)
        
        x = center_x + modified_radius * math.cos(modified_angle)
        y = center_y + modified_radius * math.sin(modified_angle)
        points.append((x, y))
    
    return points


def generate_deformed_shapes():
    """Generate multiple deformed polygons and save them to separate SVG files."""
    # Base parameters
    num_sides = 8
    center_x = 250
    center_y = 250
    base_radius = 150
    max_deformation = 0.15
    output_dir = Path("generated_polygons")
    
    # Create output directory if it doesn't exist
    output_dir.mkdir(exist_ok=True)

    for i in range(8):
        root = create_svg_root()
        points = calculate_points(center_x, center_y, base_radius, 
                                num_sides, max_deformation)
        
        path = etree.SubElement(root, "path")
        path_data = f"M {points[0][0]} {points[0][1]}"
        path_data += "".join(f" L {p[0]} {p[1]}" for p in points[1:])
        path_data += " Z"
        
        path.set("d", path_data)
        path.set("fill", "none")
        path.set("stroke", f"#{random.randint(0, 16777215):06X}")
        path.set("stroke-width", "2")
        path.set("opacity", "0.7")

        # Save individual SVG file
        output_file = output_dir / f"polygon_{i+1}.svg"
        tree = etree.ElementTree(root)
        tree.write(str(output_file), pretty_print=True, 
                  xml_declaration=True, encoding='utf-8')
    
    print(f"Generated {num_sides} polygons in {output_dir}")

if __name__ == "__main__":
    generate_deformed_shapes()

Monday, November 18, 2024

Python 3.13.0 : Tested TinyDB on Fedora 41.

Today I tested the TinyDB python package on Fedora 41 Linux distro:
TinyDB is a lightweight document oriented database optimized for your happiness :) It’s written in pure Python and has no external dependencies. The target are small apps that would be blown away by a SQL-DB or an external database server.
The documentation for this python package can be found on the official website.
The install on Fedora 14 distro can be done with pip tool:
pip install tinydb
This is the source code I tested:
from tinydb import TinyDB, Query
import datetime

# Create a TinyDB instance
db = TinyDB('my_database.json')

# Define a schema for our documents
UserSchema = {
    'name': str,
    'email': str,
    'age': int,
    'created_at': datetime.datetime
}# Insert some sample data
users = [
    {'name': 'John Doe', 'email': 'john@example.com', 'age': 30},
    {'name': 'Jane Smith', 'email': 'jane@example.com', 'age': 25},
    {'name': 'Bob Johnson', 'email': 'bob@example.com', 'age': 35}
]

for user in users:
    db.insert(user)

# Querying all users
print("\nQuerying all users:")
all_users = db.all()
for user in all_users:
    print(f"Name: {user['name']}, Email: {user['email']}, Age: {user['age']}")

# Filtering data
print("\nFidig users older than 28:")
older_than_28 = db.search(Query().age > 28)
for user in older_than_28:
    print(f"Name: {user['name']}, Email: {user['email']}, Age: {user['age']}")

# Updating data
print("\nUpdatig John Doe's age:")
db.update({'age': 31}, Query().name == 'John Doe')

# Deleting data
print("\nDeletig Jane Smith:")
doc_ids = [doc.doc_id for doc in db.search(Query().email == 'jane@example.com')]
if doc_ids:
    db.remove(doc_ids=doc_ids)
else:
    print("No document found with email 'jane@example.com'")

# Adding a new field
print("\nAddig a 'city' field to all users:")
for user in db.all():
    user['city'] = 'New York'
    db.update(user, doc_ids=[doc.doc_id for doc in db.search(Query().name == user['name'])])

# Sorting data
print("\nSorting users by age:")
sorted_users = sorted(db.all(), key=lambda x: x['age'])
for user in sorted_users:
    print(f"Name: {user['name']}, Email: {user['email']}, Age: {user['age']}")

# Getting document count
print("\nTotal number of users:", len(db.all()))

# Closing the database connection
db.close()
This is the result:
$ python test_001.py 

Querying all users:
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 30
Name: Jane Smith, Email: jane@example.com, Age: 25
Name: Bob Johnson, Email: bob@example.com, Age: 35

Fidig users older than 28:
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: John Doe, Email: john@example.com, Age: 30
Name: Bob Johnson, Email: bob@example.com, Age: 35

Updatig John Doe's age:

Deletig Jane Smith:

Addig a 'city' field to all users:

Sorting users by age:
Name: John Doe, Email: john@example.com, Age: 31
Name: John Doe, Email: john@example.com, Age: 31
Name: John Doe, Email: john@example.com, Age: 31
Name: John Doe, Email: john@example.com, Age: 31
Name: John Doe, Email: john@example.com, Age: 31
Name: John Doe, Email: john@example.com, Age: 31
Name: John Doe, Email: john@example.com, Age: 31
Name: John Doe, Email: john@example.com, Age: 31
Name: John Doe, Email: john@example.com, Age: 31
Name: John Doe, Email: john@example.com, Age: 31
Name: John Doe, Email: john@example.com, Age: 31
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: Bob Johnson, Email: bob@example.com, Age: 35
Name: Bob Johnson, Email: bob@example.com, Age: 35

Total number of users: 22

Saturday, November 16, 2024

Python 3.13.0 : Test the gi python package on Fedora distro linux.

The gi (GObject Introspection) Python package is excellent! It provides Python bindings for GObject-based libraries like GTK, GLib, and Secret Service. It enables you to write native GNOME applications in Python and access system services seamlessly.
First, install these Fedora packages:
[mythcat@fedora ~]# dnf5 install python3-gobject libsecret-devel
...
Package "python3-gobject-3.48.2-3.fc41.x86_64" is already installed.
Package "libsecret-devel-0.21.4-3.fc41.x86_64" is already installed.
...
[mythcat@fedora ~]# dnf5 install gnome-shell gnome-keyring libsecret
...
I used this simple python source code to test the gi python package:
import gi

# Specify the version of Gio and Secret we want to use
gi.require_version('Gio', '2.0')
gi.require_version('Secret', '1')

from gi.repository import Gio, Secret, GLib

def check_schema(schema_name):
    try:
        Gio.Settings.new(schema_name)
        print(f"Schema '{schema_name}' is available")
        return True
    except GLib.GError as e:
        print(f"Schema '{schema_name}' is not installed: {str(e)}")
        return False

def store_secret():
    schema = Secret.Schema.new("org.example.Password",
        Secret.SchemaFlags.NONE,
        {
            "username": Secret.SchemaAttributeType.STRING,
        }
    )
    
    Secret.password_store_sync(schema, 
        {"username": "myuser"},
        Secret.COLLECTION_DEFAULT,
        "My secret item",
        "Hello, World!",
        None)
    
    print("Secret stored successfully")

def get_secret():
    schema = Secret.Schema.new("org.example.Password",
        Secret.SchemaFlags.NONE,
        {
            "username": Secret.SchemaAttributeType.STRING,
        }
    )
    
    password = Secret.password_lookup_sync(schema,
        {"username": "myuser"},
        None)
    
    if password is not None:
        print(f"Retrieved secret: {password}")
    else:
        print("No secret found")

if __name__ == "__main__":
    print("Starting secret operations...")
    store_secret()
    get_secret()
    print("Finished secret operations.")
The result is this:
$ python test_001.py 
Starting secret operations...
Secret stored successfully
Retrieved secret: Hello, World!
Finished secret operations.

Sunday, October 27, 2024

Python 3.13.0 : know this -OOt ?

Today, I follow the actions from NEMO the basic explorer files from linux and I found this: -OOt
This first source code:
#!/usr/bin/python3 -OOt
... this is new, and refers to Python compiler optimizations.
The -OOt par in the shebang line #!/usr/bin/python3 -OOt refers to Python compiler optimizations:
  1. -O: This flag enables basic optimizations. It tells the Python compiler to optimize constants and global variables.
  2. -O2: This flag enables more aggressive optimizations. It applies additional compiler passes to further optimize the code .
  3. -Ot: This flag optimizes for size. It reduces memory usage by eliminating unused variables and dead code.
So, -OOt combines three levels of optimization:
  • Basic optimization (-O)
  • More aggressive optimization (-O2)
  • Optimization for size (-Ot)

Friday, October 25, 2024

Python 3.13.0 : Testing ntplib on Fedora 42.

Today I tested the ntplib python package on Fedora 42 with the python 3.13.0 version.
This is the source code:
from ntplib import NTPClient
import socket
import os
import datetime

def sync_ntp_server(server_address='pool.ntp.org'):
    try:
        client = NTPClient()
        response = client.request(server_address)
        
        # Get the offset in seconds
        offset = response.offset
        
        # Calculate the drift rate (seconds per day)
        drift_rate = offset * 86400 / response.tx_time
        
        print(f"NTP synchronization successful. Offset: {offset:.2f} seconds")
        print(f"Drift rate: {drift_rate:.2f} seconds per day")
        
        # Apply the offset to the system clock
        os.system(f"date -s '@{response.tx_time}'")
        
        # Optionally, you can also apply the drift rate to the system clock
        # However, this is generally not recommended as it can lead to further drift
        # os.system(f"ntpdate -d {server_address}")
        
    except Exception as e:
        print(f"Error synchronizing NTP: {e}")

# Disable SELinux temporarily
os.system("setenforce 0")

# Sync with NTP server
sync_ntp_server()

# Print current system time after sync
current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print(f"Current system time after sync: {current_time}")
The result is output is in the romanian language but this is not a problem for programmers:
[mythcat@fedora network_python_tools]$ pip install ntplib
Defaulting to user installation because normal site-packages is not writeable
Collecting ntplib
  Downloading ntplib-0.4.0-py2.py3-none-any.whl.metadata (1.6 kB)
Downloading ntplib-0.4.0-py2.py3-none-any.whl (6.8 kB)
Installing collected packages: ntplib
Successfully installed ntplib-0.4.0
[mythcat@fedora network_python_tools]$ python ntplib_test_001.py 
...
NTP synchronization successful. Offset: -5.25 seconds
Drift rate: -0.00 seconds per day
date: nu se poate stabili data: Operație nepermisă
vineri 25 octombrie 2024, 22:24:13 +0300
Current system time after sync: 2024-10-25 22:24:19

Sunday, September 22, 2024

Python 3.13.0rc1 : ... pkg_resources is deprecated as an API .

I tried an old version of python script for upgrade all my ython packages on windows 10 with pkg_resources python package.
I got this error:
DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
... and this python script will upgrade all python packages
import subprocess

try:
    # Obține lista pachetelor învechite
    outdated_packages = subprocess.check_output(["pip", "list", "--outdated", "--format=columns"]).decode("utf-8")
    packages = [line.split()[0] for line in outdated_packages.splitlines()[2:]]
    
    if not packages:
        print("Toate pachetele sunt deja actualizate.")
    else:
        print("Pachete Python învechite: ", packages)
        
        # Actualizează fiecare pachet
        for package in packages:
            print("Actualizează pachetul: ", package)
            subprocess.check_call(["pip", "install", "--upgrade", package])
except subprocess.CalledProcessError as e:
    print("A apărut o eroare la rularea comenzii pip:", e)
except Exception as e:
    print("A apărut o eroare neașteptată:", e)

Saturday, September 21, 2024

Python 3.12.3 : 8in8 game project with pygame and agentpy - 001.

I started a game project with the python packages pygame and agentpy in the Fedora Linux distribution.
You can find it on my fedora pagure repo