analitics

Pages

Sunday, September 1, 2024

Python 3.13.0rc1 : Test MSBuild with PyQt6.

The goal of this tutorial is to test the build process of the Python package.
Installing the PyQt6 python module may come with the build error.
In this case, MS Build is installed from the official website, and then other necessary Python modules can be installed depending on the files needed to build the PyQt6 package.
pip install --upgrade setuptools
pip install msvc-runtime
ERROR: Could not find a version that satisfies the requirement msvc-runtime (from versions: none)
ERROR: No matching distribution found for msvc-runtime
pip install pyqt6
Collecting pyqt6
  Using cached PyQt6-6.7.1-cp38-abi3-win_amd64.whl.metadata (2.1 kB)
...
Successfully built PyQt6-sip
Installing collected packages: PyQt6-Qt6, PyQt6-sip, pyqt6
Successfully installed PyQt6-Qt6-6.7.2 PyQt6-sip-13.8.0 pyqt6-6.7.1
This process uses a lot of files and MSBuild needs some Gb free on the hard disk.

Saturday, August 31, 2024

Python 3.13.0rc1 : Using pydoc python module.

This is default python module.
You can find pydoc - documentation.
Simple use of this python module for example: sys python module to see the documentation.
PythonProjects\test_pydoc>python -m pydoc sys
Using the w argument willcreate a HTML file with the documentation.
In this example will be sys.html, because is sys python module.
PythonProjects\test_pydoc>python -m pydoc -w sys
wrote sys.html
Best feature is search by word and show the result as python modules, in this case will show a list ...
PythonProjects\test_pydoc>python -m pydoc -k url
nturl2path - Convert a NT pathname to a file URL and vice versa.
test_sqlite3: testing with SQLite version 3.45.3
test.test_urllib - Regression tests for what was in Python 2's "urllib" module
test.test_urllib2
test.test_urllib2_localnet
test.test_urllib2net
Share the documentation with an server, in this case is set to localhost:
\PythonProjects\test_pydoc>python -m pydoc -p 1234
Server ready at http://localhost:1234/
Server commands: [b]rowser, [q]uit
You can create your python module script named test.py formated and then use pydoc.
"""
my python module
====
This is documentation
"""
def test():
"""
Function test
"""
    print("test")
Use this to show the text from your python source script module:
python -m pydoc test
PythonProjects\test_pydoc>python -m pydoc test
Help on module test:

NAME
    test

FILE
...

Thursday, August 29, 2024

News : ... august 2024

... news for Python users and developers.
Python 3.12.5 released, see the official webpage.
... new Python 3.13.0 release candidate 1 released, you can read on this webpage.
the last one: Announcing Python Software Foundation Fellow Members for Q1 2024! from this webpage.
About the releases, I can say that all kinds of improvements are coming, I liked that they made the shell more interactive.

Monday, August 19, 2024

Python 3.12.1 : Web server with SQLite database using flask - update.

Update with new URL with params, see the first tutorial:
from flask import Flask, request, jsonify, render_template_string
import sqlite3
from datetime import datetime

app = Flask(__name__)

# Clasa pentru serverul SQL
class SQLiteServer:
    def __init__(self, db_name):
        self.db_name = db_name
        self.init_db()

    def init_db(self):
        conn = sqlite3.connect(self.db_name)
        c = conn.cursor()
        c.execute('''
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY,
                first_name TEXT,
                last_name TEXT,
                occupation TEXT,
                hobby TEXT,
                year_of_birth INTEGER,
                age INTEGER
            )
        ''')
        conn.commit()
        conn.close()

    def calculate_age(self, year_of_birth):
        # Adjust year_of_birth if only two digits are provided
        if len(str(year_of_birth)) == 2:
            if year_of_birth > int(str(datetime.now().year)[-2:]):
                year_of_birth += 1900
            else:
                year_of_birth += 2000
        current_year = datetime.now().year
        return current_year - year_of_birth

    def add_user(self, first_name, last_name, occupation, hobby, year_of_birth):
        age = self.calculate_age(year_of_birth)
        conn = sqlite3.connect(self.db_name)
        c = conn.cursor()
        c.execute('''
            INSERT INTO users (first_name, last_name, occupation, hobby, year_of_birth, age)
            VALUES (?, ?, ?, ?, ?, ?)
        ''', (first_name, last_name, occupation, hobby, year_of_birth, age))
        conn.commit()
        conn.close()

    def get_users(self):
        conn = sqlite3.connect(self.db_name)
        c = conn.cursor()
        c.execute("SELECT * FROM users")
        users = c.fetchall()
        conn.close()
        return users
    def get_users_jsonify():
        conn = sqlite3.connect('sqlite_database.db')
        c = conn.cursor()
        c.execute("SELECT * FROM users")
        users = c.fetchall()
        conn.close()
        return jsonify(users)

# Clasa pentru serverul web
class WebServer:
    def __init__(self, sqlite_server):
        self.sqlite_server = sqlite_server

    def run(self):
        app.run(debug=True)
    #   adauga user si buton de redirect la pagina users
    @app.route('/')
    def index():
        users = sqlite_server.get_users()
        return render_template_string('''
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <meta name="viewport" content="width=device-width, initial-scale=1.0">
                <title>Flask website for testing cypress with sqlite</title>
            </head>
            <body>
                <h2>Add User</h2>
                <form action="/add_user" method="post">
                    First Name: <input type="text" name="first_name"><br>
                    Last Name: <input type="text" name="last_name"><br>
                    Occupation: <input type="text" name="occupation"><br>
                    Hobby: <input type="text" name="hobby"><br>
                    Year of Birth: <input type="text" name="year_of_birth"><br>
                    <input type="submit" value="Add User">
                </form>
                <a href="http://127.0.0.1:5000/users"><button type="button">Show Users</button></a>
            </body>
            </html>
        ''', users=users)
    @app.route('/add_user', methods=['POST'])
    def add_user():
        first_name = request.form['first_name']
        last_name = request.form['last_name']
        occupation = request.form['occupation']
        hobby = request.form['hobby']
        year_of_birth = int(request.form['year_of_birth'])
        sqlite_server.add_user(first_name, last_name, occupation, hobby, year_of_birth)
        return 'User added successfully! <a href="/">Go back</a>'
    @app.route('/users', methods=['GET'])
    def get_users():
        query_type = request.args.get('query_type', 'simple')
        
        conn = sqlite3.connect('sqlite_database.db')
        c = conn.cursor()
        
        try:
            c.execute('SELECT name FROM sqlite_master WHERE type="table" AND name="users"')
            if not c.fetchone():
                return jsonify({"error": "Table 'users' does not exist"})

            if query_type == 'advanced':
                # Advanced query logic
                first_name = request.args.get('first_name')
                last_name = request.args.get('last_name')
                occupation = request.args.get('occupation')
                hobby = request.args.get('hobby')
                year_of_birth = request.args.get('year_of_birth')

                query = 'SELECT * FROM users WHERE 1=1'
                params = []
                # Exemple query simple 
                # Basic query: /users
                # Simple query: /users?query_type=simple for simple selection
                # Addvanced query: /users?query_type=advanced&first_name=John&occupation=Engineer for advanced querying
                # Advanced query with name search: /users?query_type=advanced&first_name=John&last_name=Doe
                # Query by occupation: /users?query_type=advanced&occupation=Engineer
                # Query by hobby: /users?query_type=advanced&hobby=Reading
                # Query by year of birth: /users?query_type=advanced&year_of_birth=1990

                if first_name:
                    query += ' AND first_name LIKE ?'
                    params.append(f'%{first_name}%')
                if last_name:
                    query += ' AND last_name LIKE ?'
                    params.append(f'%{last_name}%')
                if occupation:
                    query += ' AND occupation LIKE ?'
                    params.append(f'%{occupation}%')
                if hobby:
                    query += ' AND hobby LIKE ?'
                    params.append(f'%{hobby}%')
                if year_of_birth:
                    query += ' AND year_of_birth = ?'
                    params.append(year_of_birth)

                # Query by minimum age: /users?query_type=advanced&min_age=30
                # Query by maximum age: /users?query_type=advanced&max_age=50
                # Query with ordering: /users?query_type=advanced&order_by=last_name
                # Query with limit: /users?query_type=advanced&limit=10
                # Combined query: /users?query_type=advanced&first_name=John&occupation=Engineer&min_age=25&order_by=year_of_birth&limit=5          
                # Additional advanced query options
                for param, value in request.args.items():
                    match param:
                        case 'min_age':
                            query += ' AND (? - year_of_birth) >= ?'
                            params.extend([datetime.now().year, int(value)])
                        case 'max_age':
                            query += ' AND (? - year_of_birth) <= ?'
                            params.extend([datetime.now().year, int(value)])
                        case 'order_by':
                            query += f' ORDER BY {value}'
                        case 'limit':
                            query += ' LIMIT ?'
                            params.append(int(value))
                c.execute(query, params)
            else:
                # Simple query logic
                c.execute('SELECT * FROM users')

            users = c.fetchall()
        except sqlite3.OperationalError as e:
            return jsonify({"error": str(e)})
        finally:
            conn.close()
        
        return jsonify(users)

# Instanțierea serverului SQL și a serverului web
sqlite_server = SQLiteServer('sqlite_database.db')
web_server = WebServer(sqlite_server)

if __name__ == '__main__':
    web_server.run()

Wednesday, August 14, 2024

Python 3.12.1 : Web server with SQLite database using flask.

This python surce script can be used to start a web server with an SQLite server.
For example, you can use this to test with javascript on sql server, see next image:
This is the source code:
from flask import Flask, request, jsonify, render_template_string
import sqlite3
from datetime import datetime

app = Flask(__name__)

# Clasa pentru serverul SQL
class SQLServer:
    def __init__(self, db_name):
        self.db_name = db_name
        self.init_db()

    def init_db(self):
        conn = sqlite3.connect(self.db_name)
        c = conn.cursor()
        c.execute('''
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY,
                first_name TEXT,
                last_name TEXT,
                occupation TEXT,
                hobby TEXT,
                year_of_birth INTEGER,
                age INTEGER
            )
        ''')
        conn.commit()
        conn.close()

    def calculate_age(self, year_of_birth):
        # Adjust year_of_birth if only two digits are provided
        if len(str(year_of_birth)) == 2:
            if year_of_birth > int(str(datetime.now().year)[-2:]):
                year_of_birth += 1900
            else:
                year_of_birth += 2000
        current_year = datetime.now().year
        return current_year - year_of_birth

    def add_user(self, first_name, last_name, occupation, hobby, year_of_birth):
        age = self.calculate_age(year_of_birth)
        conn = sqlite3.connect(self.db_name)
        c = conn.cursor()
        c.execute('''
            INSERT INTO users (first_name, last_name, occupation, hobby, year_of_birth, age)
            VALUES (?, ?, ?, ?, ?, ?)
        ''', (first_name, last_name, occupation, hobby, year_of_birth, age))
        conn.commit()
        conn.close()

    def get_users(self):
        conn = sqlite3.connect(self.db_name)
        c = conn.cursor()
        c.execute("SELECT * FROM users")
        users = c.fetchall()
        conn.close()
        return users

# Clasa pentru serverul web
class WebServer:
    def __init__(self, sql_server):
        self.sql_server = sql_server

    def run(self):
        app.run(debug=True)

    @app.route('/')
    def index():
        users = sql_server.get_users()
        return render_template_string('''
            <h1>Users</h1>
            <ul>
                {% for user in users %}
                    <li>{{ user[1] }} {{ user[2] }} - {{ user[3] }} - {{ user[4] }} - {{ user[5] }} ({{ user[6] }} years old)</li>
                {% endfor %}
            </ul>
            <h2>Add User</h2>
            <form action="/add_user" method="post">
                First Name: <input type="text" name="first_name"><br>
                Last Name: <input type="text" name="last_name"><br>
                Occupation: <input type="text" name="occupation"><br>
                Hobby: <input type="text" name="hobby"><br>
                Year of Birth: <input type="text" name="year_of_birth"><br>
                <input type="submit" value="Add User">
            </form>
        ''', users=users)

    @app.route('/add_user', methods=['POST'])
    def add_user():
        first_name = request.form['first_name']
        last_name = request.form['last_name']
        occupation = request.form['occupation']
        hobby = request.form['hobby']
        year_of_birth = int(request.form['year_of_birth'])
        sql_server.add_user(first_name, last_name, occupation, hobby, year_of_birth)
        return 'User added successfully! <a href="/">Go back</a>'

# Instanțierea serverului SQL și a serverului web
sql_server = SQLServer('example.db')
web_server = WebServer(sql_server)

if __name__ == '__main__':
    web_server.run()

Saturday, August 3, 2024

Blender 3D and python scripting - part 029.

This is a simple Blender 3D script that render images from myimage_000 to myimage_035 around object named Cube.
The script can be changed with any object and any steps for 0 to 360 degree.
import bpy
import math
# Set the object that the camera will orbit around
#target_object = bpy.data.objects["MyObject"]

# Create a new empty object
empty = bpy.data.objects.new("Empty", None)

# Set the empty object's location to the origin point
empty.location = (0, 0, 0)

# Set the starting position for the camera
camera = bpy.data.objects["Camera"]

# Set the number of degrees to rotate the camera around the object
degrees = 360

# Set the distance that the camera should be from the object
distance = 7.6

# Set the speed at which the camera should orbit
speed = 10

# Set the direction in which the camera should orbit (1 for clockwise, -1 for counter-clockwise)
direction = 1

# Set the camera to track the object
bpy.ops.object.select_all(action="DESELECT")
camera.select_set(True)

# Set the distance to origin point
camera.location = (-distance, 0, 0)
bpy.context.view_layer.objects.active = camera

# Remove all constraints from the object "Cube"
bpy.data.objects['Cube'].select_get()
bpy.context.view_layer.objects.active = bpy.data.objects['Cube']
bpy.ops.object.constraints_clear()

# Add a track to constraint to the object and set it
bpy.ops.object.constraint_add(type="TRACK_TO")
bpy.ops.object.track_set(type="TRACKTO")

# Set the target object as the tracking target
bpy.data.objects['Cube'].select_get()
bpy.context.view_layer.objects.active = bpy.data.objects['Cube']

# Select the file image format
bpy.context.scene.render.image_settings.file_format = 'PNG'

# Animate the camera orbiting around the object
for frame in range(0, 36):
    # Set the current frame
    bpy.context.scene.frame_set(frame)

    # Calculate the new position for the camera based on its distance from the object
    x = distance * math.sin(math.radians(frame*speed*direction))
    y = distance * math.cos(math.radians(frame*speed*direction))
    camera.location = (x,y,0)
    # Set the output path for the rendered image
    bpy.context.scene.render.filepath = "C:\\tmp\\myimage_" + str(frame).zfill(3) + ".png"
    # Render the frame and save it to the output file
    bpy.ops.render.render(write_still=True)

Sunday, July 14, 2024

The Zen of Python ...

... see the The Zen of Python, with this source code import this:
python
Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun  6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Sunday, July 7, 2024

Python 3.12.1 : testing with ollama - part 001.

You need to download the ollama from the official website.
This allow you to use commands on the console: ollama --help into new command shell.
Use the pip tool to install ollma python package:
pip install ollama
Collecting ollama
  Downloading ollama-0.2.1-py3-none-any.whl.metadata (4.2 kB)
...
Installing collected packages: httpx, ollama
  Attempting uninstall: httpx
    Found existing installation: httpx 0.26.0
    Uninstalling httpx-0.26.0:
      Successfully uninstalled httpx-0.26.0
Successfully installed httpx-0.27.0 ollama-0.2.1
This command will install llama3
ollama run llama3
Let's see a basic python script with mistral model:
import ollama
from rich.console import Console
console = Console()
print(ollama.pull("mistral"))
#with console.pager(styles=True):
#	console.print(ollama.show("mistral"))
with console.pager(styles=True):
	console.print(ollama.list())
Another python script with llama3 model:
import ollama

stream = ollama.chat(
    model='llama3',
    messages=[{'role': 'user', 'content': 'Tell me the sizes of Earth?'}],
    stream=True,
)

for chunk in stream:
  print(chunk['message']['content'], end='', flush=True)
The result is this:
What a great question!

The size of Earth can be measured in various ways, depending on what aspect you're interested in. Here are some common sizes and dimensions of our planet ...

Python 3.12.1 : Check proxies on network.

Simple example about how to check proxies in network ...
import socket

# Get IP 
hostname = socket.gethostname()
workstation_ip = socket.gethostbyname(hostname)
#print("workstation_ip : ", workstation_ip )

# Put your IP's proxies from network
proxy_ips = ["192.168.1.1", "192.168.1.2", "192.168.1.3"]

# check IP proxy
if workstation_ip in proxy_ips:
    print("Is a proxy on network")
else:
    print("Not network proxy")
pip install sockschain
Collecting sockschain
  Downloading sockschain-1.0.0-py3-none-any.whl.metadata (10 kB)
...
Installing collected packages: sockschain
Successfully installed sockschain-1.0.0
Example with sockschain python module:
import socket
import sockschain as socks

# Enable debugging
def DEBUG(msg):
  print (msg)

socks.DEBUG = DEBUG
# This would have set proxies using the standard environment variables:
socks.usesystemdefaults()

# Configure a default chain
chain = [
  'tor://localhost:9050/', # First hop is Tor,
  'tor://localhost:8080/', # 8080 port,
  'ssl://proxy.example.com:8443/', # SSL to proxy.example.com
  'http://user:pass@proxy.example.com/' # ... try auth to an HTTP proxy
]
socks.setdefaultproxy() # Clear the default chain
for hop in chain:
   socks.adddefaultproxy(*socks.parseproxy(hop))

# Configure alternate routes (no proxy for localhost)
socks.setproxy('localhost', socks.PROXY_TYPE_NONE)
socks.setproxy('127.0.0.1', socks.PROXY_TYPE_NONE)
... the result is:
python socket_web.py Routes are: {'localhost': [(0, None, None, True, None, None, None)]} Routes are: {'localhost': [(0, None, None, True, None, None, None)], 'localhost.localdomain': [(0, None, None, True, None, None, None)]} Routes are: {'localhost': [(0, None, None, True, None, None, None)], 'localhost.localdomain': [(0, None, None, True, None, None, None)], '127.0.0.1': [(0, None, None, True, None, None, None)]} Routes are: {'localhost': [(0, None, None, True, None, None, None)], 'localhost.localdomain': [(0, None, None, True, None, None, None)], '127.0.0.1': [(0, None, None, True, None, None, None)], '': [(0, None, None, True, None, None, None)]} Routes are: {'localhost': [(0, None, None, True, None, None, None)], 'localhost.localdomain': [(0, None, None, True, None, None, None)], '127.0.0.1': [(0, None, None, True, None, None, None)], '': [(0, None, None, True, None, None, None)], '*': [(7, 'localhost', 9050, True, None, None, None)]} Routes are: {'localhost': [(0, None, None, True, None, None, None)], 'localhost.localdomain': [(0, None, None, True, None, None, None)], '127.0.0.1': [(0, None, None, True, None, None, None)], '': [(0, None, None, True, None, None, None)], '*': [(7, 'localhost', 9050, True, None, None, None), (7, 'localhost', 8080, True, None, None, None)]} Routes are: {'localhost': [(0, None, None, True, None, None, None)], 'localhost.localdomain': [(0, None, None, True, None, None, None)], '127.0.0.1': [(0, None, None, True, None, None, None)], '': [(0, None, None, True, None, None, None)], '*': [(7, 'localhost', 9050, True, None, None, None), (7, 'localhost', 8080, True, None, None, None), (4, 'proxy.example.com', 8443, True, None, None, ['proxy.example.com'])]} Routes are: {'localhost': [(0, None, None, True, None, None, None)], 'localhost.localdomain': [(0, None, None, True, None, None, None)], '127.0.0.1': [(0, None, None, True, None, None, None)], '': [(0, None, None, True, None, None, None)], '*': [(7, 'localhost', 9050, True, None, None, None), (7, 'localhost', 8080, True, None, None, None), (4, 'proxy.example.com', 8443, True, None, None, ['proxy.example.com']), (3, 'proxy.example.com', 8080, False, 'user', 'pass', None)]} Routes are: {'localhost': [(0, None, None, True, None, None, None)], 'localhost.localdomain': [(0, None, None, True, None, None, None)], '127.0.0.1': [(0, None, None, True, None, None, None)], '': [(0, None, None, True, None, None, None)], '*': [(7, 'localhost', 9050, True, None, None, None), (7, 'localhost', 8080, True, None, None, None), (4, 'proxy.example.com', 8443, True, None, None, ['proxy.example.com']), (3, 'proxy.example.com', 8080, False, 'user', 'pass', None)]} Routes are: {'localhost': [(0, None, None, True, None, None, None)], 'localhost.localdomain': [(0, None, None, True, None, None, None)], '127.0.0.1': [(0, None, None, True, None, None, None)], '': [(0, None, None, True, None, None, None)], '*': [(7, 'localhost', 9050, True, None, None, None), (7, 'localhost', 8080, True, None, None, None), (4, 'proxy.example.com', 8443, True, None, None, ['proxy.example.com']), (3, 'proxy.example.com', 8080, False, 'user', 'pass', None)]}

Tuesday, July 2, 2024

Python 3.12.1 : Ursina python game engine - part 002 .

This is the second tutorial with ursina, because now I use Python version 3.12.1 and Ursina comes with version 7.0.0 .
The install is easy with the pip tool.
pip install ursina
Collecting ursina
...
Installing collected packages: panda3d, screeninfo, panda3d-simplepbr, panda3d-gltf, ursina
Successfully installed panda3d-1.10.14 panda3d-gltf-1.2.0 panda3d-simplepbr-0.12.0 screeninfo-0.8.1 ursina-7.0.0
About Ursina you can read more on the official website.
Platforms:
  • Windows
  • Linux
  • Mac (not officially supported, but will most likely work)
I tested with samples from the GitHub project and works well.
from ursina import *

app = Ursina(size=(1280,720))

physics_entities = []
class PhysicsEntity(Entity):
    def __init__(self, model='cube', collider='box', **kwargs):
        super().__init__(model=model, collider=collider, **kwargs)
        physics_entities.append(self)

    def update(self):
        if self.intersects():
            self.stop()
            return

        self.velocity = lerp(self.velocity, Vec3(0), time.dt)
        self.velocity += Vec3(0,-1,0) * time.dt * 5
        self.position += (self.velocity + Vec3(0,-4,0)) * time.dt


    def stop(self):
        self.velocity = Vec3(0,0,0)
        if self in physics_entities:
            physics_entities.remove(self)

    def on_destroy(self):
        self.stop()


    def throw(self, direction, force):
        pass

from ursina.shaders import lit_with_shadows_shader
Entity.default_shader = lit_with_shadows_shader
DirectionalLight().look_at(Vec3(1,-1,-1))

ground = Entity(model='plane', scale=32, texture='white_cube', texture_scale=Vec2(32), collider='box')

from ursina.prefabs.first_person_controller import FirstPersonController
player = FirstPersonController()

def input(key):
    if key == 'left mouse down':
        e = PhysicsEntity(model='cube', color=color.azure, velocity=Vec3(0), position=player.position+Vec3(0,1.5,0)+player.forward, collider='sphere')
        e.velocity = (camera.forward + Vec3(0,.5,0)) * 10
        # physics_entities.append(e)

Sky()
app.run()

Tuesday, June 25, 2024

Blender 3D and python scripting - part 028.

A simple script for Blender 4.0.0 version with Bright Pencil material, see the output image:
This is the source code:
import bpy
import random

# fix an active object before changing mode
if bpy.context.view_layer.objects.active is None:
    # If there is no active object, set the first object in the scene as active
    if len(bpy.context.scene.objects) > 0:
        bpy.context.view_layer.objects.active = bpy.context.scene.objects[0]
    else:
        print("There are no objects in the scene to set as active.")

# set the mode
bpy.ops.object.mode_set(mode='OBJECT')

# set camera from the scene
cam_ob = bpy.context.scene.camera

# if is a camera in the scene and set it as active object
if cam_ob is None:
    print("There is no camera in the scene.")
elif cam_ob.type == 'CAMERA':
    # Set the camera as active object
    bpy.context.view_layer.objects.active = cam_ob
    print("The camera has been set as active object.")
else:
    print(f"The object {cam_ob.type} is set as a camera, but it is not of type 'CAMERA'.")

# set data for the ink brush
gpencil = bpy.data.grease_pencils.new("Bright Pencil")

# make material for the ink brush
if "Bright Material" in bpy.data.materials.keys():
    gp_mat = bpy.data.materials["Bright Material"]
else:
    gp_mat = bpy.data.materials.new("Bright Material")

if not gp_mat.is_grease_pencil:
    bpy.data.materials.create_gpencil_data(gp_mat)
    bpy.context.object.active_material.grease_pencil.color = (0, 0, 1, 1)


# set the object for the ink brush 
if "Bright Pencil" not in bpy.data.objects:
    gp_data = bpy.data.objects.new("Bright Pencil", gpencil)
    bpy.context.scene.collection.objects.link(gp_data)
# if it does not already exist in the scene
else:
    gp_data = bpy.data.objects["Bright Pencil"]

# assign material for drawing 
if gp_mat.name not in gp_data.data.materials:
    gp_data.data.materials.append(gp_mat)

# define a function to create random lines
def create_random_strokes(num_strokes, max_width):
    # Set the active object and mode
    bpy.context.view_layer.objects.active = gp_data
    bpy.ops.object.mode_set(mode='PAINT_GPENCIL')

    # get or create layer and set it as active
    if gpencil.layers and gpencil.layers.active:
        layer = gpencil.layers.active
    else:
        layer = gpencil.layers.new('my_test_layer', set_active=True)
    # set layer as active
    gpencil.layers.active = layer

    # get or create frame and set it as active using change_frame() method
    if layer.frames and layer.active_frame:
        frame = layer.active_frame
    else:
        frame = layer.frames.new(1)

    for _ in range(num_strokes):
        stroke = frame.strokes.new()
        stroke.line_width = int(random.uniform(1.0, max_width))
        stroke.points.add(count=3)
        for point in stroke.points:
            point.co = (random.uniform(-1.0, 1.0), random.uniform(-1.0, 1.0), 0.0)

# this function with desired parameters
create_random_strokes(num_strokes=10, max_width=16.0)

# return to original mode
bpy.ops.object.mode_set(mode='OBJECT')

Wednesday, June 5, 2024

News : JAX in 100 Seconds by Fireship!

JAX is a Python library similar to NumPy for scientific computing and linear algebra, but designed to run on accelerators like Cuda-based GPUs and Google's TPUs.

Blender 3D and python scripting - part 027.

Is more easy to create your player character based on the armature.
The next step is to add more animation and export to your game engine ...
Based on this issue - blender.stackexchange website, I tested to today and works very well:
Let's see the source code:
import bpy
import mathutils 
from mathutils import Vector 
from math import *

class ArmatureMenu(bpy.types.Menu):
    bl_label = "Mesh 2 Armature Menu"
    bl_idname = "OBJECT_MT_Mesh_From_Armature"

    def draw(self, context):
        layout = self.layout
        layout.operator("wm.mesh_from_armature", text="Pyramid").mesh_type = 'Pyramid' # from here
        layout.operator("wm.mesh_from_armature", text="Tapered").mesh_type = 'Tapered' # from here
        layout.operator("wm.mesh_from_armature", text="Box").mesh_type = 'Box' # from here

def CreateMesh(self, meshType):

    obj = bpy.context.active_object

    if obj == None:
        self.report({"ERROR"}, "No selection" )
    elif obj.type != 'ARMATURE':
        self.report({"ERROR"}, "Armature expected" )
    else:
        processArmature( bpy.context, obj, meshType = meshType )

#Create the base object from the armature
def meshFromArmature( arm ):
    name = arm.name + "_mesh"
    meshData = bpy.data.meshes.new( name + "Data" )
    meshObj = bpy.data.objects.new( name, meshData )
    meshObj.matrix_world = arm.matrix_world.copy()
    return meshObj

#Create the bone geometry (vertices and faces)
def boneGeometry( l1, l2, x, z, baseSize, l1Size, l2Size, base, meshType ):
    
    if meshType == 'Tapered':
        print(meshType)
        x1 = x * baseSize * l1Size 
        z1 = z * baseSize * l1Size

        x2 = x * baseSize * l2Size 
        z2 = z * baseSize * l2Size
    elif meshType == 'Box':
        print(meshType)
        lSize = (l1Size + l2Size) / 2
        x1 = x * baseSize * lSize 
        z1 = z * baseSize * lSize

        x2 = x * baseSize * lSize 
        z2 = z * baseSize * lSize

    else: # default to Pyramid
        print(meshType)
        x1 = x * baseSize * l1Size 
        z1 = z * baseSize * l1Size

        x2 = Vector( (0, 0, 0) )
        z2 = Vector( (0, 0, 0) )

    verts = [
        l1 - x1 + z1,
        l1 + x1 + z1,
        l1 - x1 - z1,
        l1 + x1 - z1,
        l2 - x2 + z2,
        l2 + x2 + z2,
        l2 - x2 - z2,
        l2 + x2 - z2
        ] 

    faces = [
        (base+3, base+1, base+0, base+2),
        (base+6, base+4, base+5, base+7),
        (base+4, base+0, base+1, base+5),
        (base+7, base+3, base+2, base+6),
        (base+5, base+1, base+3, base+7),
        (base+6, base+2, base+0, base+4)
        ]

    return verts, faces

#Process the armature, goes through its bones and creates the mesh
def processArmature(context, arm, genVertexGroups = True, meshType = 'Pyramid'):
    print("processing armature {0} {1}".format(arm.name, meshType) )

    #Creates the mesh object
    meshObj = meshFromArmature( arm )
    context.collection.objects.link( meshObj )

    verts = []
    edges = []
    faces = []
    vertexGroups = {}

    bpy.ops.object.mode_set(mode='EDIT')

    try:
        #Goes through each bone
        for editBone in [b for b in arm.data.edit_bones if b.use_deform]:
            boneName = editBone.name
            # print( boneName )
            poseBone = arm.pose.bones[boneName]

            #Gets edit bone informations
            editBoneHead = editBone.head
            editBoneTail = editBone.tail
            editBoneVector = editBoneTail - editBoneHead
            editBoneSize = editBoneVector.dot( editBoneVector )
            editBoneRoll = editBone.roll
            editBoneX = editBone.x_axis
            editBoneZ = editBone.z_axis
            editBoneHeadRadius = editBone.head_radius
            editBoneTailRadius = editBone.tail_radius

            #Creates the mesh data for the bone
            baseIndex = len(verts)
            baseSize = sqrt( editBoneSize )
            newVerts, newFaces = boneGeometry( editBoneHead, editBoneTail, editBoneX, editBoneZ, baseSize, editBoneHeadRadius, editBoneTailRadius, baseIndex, meshType )

            verts.extend( newVerts )
            faces.extend( newFaces )

            #Creates the weights for the vertex groups
            vertexGroups[boneName] = [(x, 1.0) for x in range(baseIndex, len(verts))]

        #Assigns the geometry to the mesh
        meshObj.data.from_pydata(verts, edges, faces)

    except:
        bpy.ops.object.mode_set(mode='OBJECT')
    else:
        bpy.ops.object.mode_set(mode='OBJECT')

    #Assigns the vertex groups
    if genVertexGroups:
        for name, vertexGroup in vertexGroups.items():
            groupObject = meshObj.vertex_groups.new(name=name)
            for (index, weight) in vertexGroup:
                groupObject.add([index], weight, 'REPLACE')

    #Creates the armature modifier
    modifier = meshObj.modifiers.new('ArmatureMod', 'ARMATURE')
    modifier.object = arm
    modifier.use_bone_envelopes = False
    modifier.use_vertex_groups = True

    meshObj.data.update()

    return meshObj

class MeshFromArmatureOperator(bpy.types.Operator):
    bl_idname = "wm.mesh_from_armature"
    bl_label  = "MeshFromArmatureOperator"

    mesh_type : bpy.props.StringProperty(name="mesh_type")

    def execute(self, context):
        print('The mesh type is', self.mesh_type)
        CreateMesh(self, self.mesh_type)
        return {'FINISHED'}

def register():
    bpy.utils.register_class( ArmatureMenu )
    bpy.utils.register_class( MeshFromArmatureOperator )


def unregister():
    bpy.utils.unregister_class( ArmatureMenu )
    bpy.utils.unregister_class( MeshFromArmatureOperator )


if __name__ == "__main__":
    register()

    # The menu can also be called from scripts
    bpy.ops.wm.call_menu(name='OBJECT_MT_Mesh_From_Armature')
The result of this running script using a mixamo animation is this: