Difference between revisions of "IT:HowTo:Use POV-Ray with Blender/Developers"
m |
|||
Line 241: | Line 241: | ||
La funzione ''execute()'' segnala l'esecuzione riuscita restituendo un set di flag, in questo caso un set costituito solo dalla stringa di caratteri ''"FINISHED"''. | La funzione ''execute()'' segnala l'esecuzione riuscita restituendo un set di flag, in questo caso un set costituito solo dalla stringa di caratteri ''"FINISHED"''. | ||
− | === | + | ===Creazione e aggiunta di un menu al registro=== |
− | + | Quando abilitiamo la casella di controllo ''Enable add-on'' nelle ''preferenze'', Blender cercherà una funzione '''register()''' e la eseguirà. Allo stesso modo, quando si disattiva un componente aggiuntivo, viene richiamata la funzione'''unregister()'''. Usiamoli quindi sia per registrare il nostro operatore con Blender sia per inserire una voce di menu che faccia riferimento al nostro operatore. è inoltre importante annullare l'iscrizione e disattivarla affinché il nostro componente aggiuntivo non venga percepito come invasivo o distruttivo di una configurazione precedente senza possibile ritorno. | |
Pour créer une entrée de menu, nous devons faire deux choses : | Pour créer une entrée de menu, nous devons faire deux choses : |
Revision as of 09:55, 1 January 2025
Go To User's page ► |
- POV-Ray Site
- Exporter Quick Start
- POV-Ray Documentation
- Sample Materials
- POV-Ray centric default workspace
- ---
- ---
- ---
- ---
- ---
- ---
- ---
- ---
- design ideas
- regression files
Il componente aggiuntivo Blender per POV-Ray e viceversa (noto anche come "POV@Ble") è iniziato come un singolo file Python.
Per rispettare i criteri per le estensioni disponibili con Blender, questo componente aggiuntivo doveva:
- fornire alcune informazioni generali sul componente aggiuntivo: nome e versione,
- definire il codice per eseguire azioni, principalmente tramite "operatori" o "funzioni"
- e assicurarsi che siano iscritti nel registro affinché possano essere utilizzati.
bpy
Blender dispone di un interprete Python integrato che è incaricato di avviare Blender e resta attivo mentre Blender è in corso di esecuzione. L'interprete esegue script per progettare l'utente dell'interfaccia ed è anche utilizzato per alcuni strumenti interni di Blender. L'interprete integrato di Blender fornisce un ambiente tipico Python, quindi il codice didattico sulla forma di scrittura degli script Python può essere eseguito anche con l'interprete di Blender. Frullatore fornitomoduli Python, come bpy e mathutils, per la console integrata affinché possano essere importati in uno script e accedere ai dati, alle classi e alle funzioni di Blender. Gli script che trattano i dati di Blender devono essere l'oggetto di unimport di moduli per funzionare. Quindi, tra tutti i moduli, se non ce n'è un altro, bpy è generalmente sempre importato negli script di Blender. (eccetto nella console interattiva dove questa importazione viene eseguita automaticamente per maggiore comodità)
metadati del componente aggiuntivo
bl_info = {
"name": "Persistence of Vision",
"author": "Campbell Barton, "
"Maurice Raybaud, "
"Leonid Desyatkov, "
"Bastien Montagne, "
"Constantin Rahn, "
"Silvio Falcinelli",
"version": (0, 1, 1),
"blender": (2, 81, 0),
"location": "Render Properties > Render Engine > Persistence of Vision",
"description": "Persistence of Vision integration for blender",
"doc_url": "{BLENDER_MANUAL_URL}/addons/render/povray.html",
"category": "Render",
"warning": "Under active development, seeking co-maintainer(s)",
}
Prima di Blender 4.2, le informazioni generali sui componenti aggiuntivi erano definite in un dizionario denominato
bl_info
Ciascuna chiave in questo dizionario forniva a Blender informazioni specifiche sull'add-on, sebbene non tutte fossero ugualmente importanti. La maggior parte delle informazioni sono state utilizzate nella finestra di dialogo delle preferenze dell'utente e hanno aiutato l'utente a trovare e selezionare un componente aggiuntivo.
name
Una sorta di nome in codice per l'add-on, con cui viene indicato nei sorgenti di Blender... (il nome dell'interfaccia utente potrebbe mostrare qualcos'altro)
author
Se contribuisci, ti verrà accreditato qui!
version
Numero di versione del software. Qualsiasi schema di numerazione è valido, purché sia una tupla di tre numeri interi. È preferibile spostare solo l'ultima posizione, ma in seguito potremmo optare per uno schema più strutturato.
blender
La versione minima di Blender richiesta da questo componente aggiuntivo. Un'altra tupla di tre numeri interi. Anche se ti aspetti che le cose funzionino con le versioni più vecchie e quelle più recenti, potrebbe essere una buona idea elencare la versione più vecchia con cui hai effettivamente testato il tuo componente aggiuntivo! È importante sviluppare con qualcosa di abbastanza stabile e poi testare con l'ultima versione appena prima di impegnarsi, ma generalmente non aggiornare il numero di versione compatibile, solo quando qualcosa si rompe a quel punto.
category
La categoria nelle Preferenze utente in cui è raggruppato il nostro componente aggiuntivo. Sfrutta motori compatibili con POV che sono motori di rendering, quindi aveva senso aggiungerlo storicamente alla categoria Render. Sfortunatamente, questo non riflette la natura versatile che ha finito per sviluppare (come la spedizione a un importatore, l'evidenziazione della sintassi dell'editor di testo, ecc...). Se sono possibili più categorie, forse dovremmo aggiungerle.
location
Dove trovare il componente aggiuntivo una volta attivato. Può trattarsi di un riferimento a un pannello specifico o a un caso particolare, una descrizione della sua posizione in un menu.
description
Una descrizione concisa di ciò che fa il componente aggiuntivo.
warning
Se non è una stringa vuota, il componente aggiuntivo verrà visualizzato con un segno di avviso nelle preferenze dell'utente. Puoi usarlo per contrassegnare un componente aggiuntivo come sperimentale, ad esempio.
doc_url
La documentazione pubblica online è un derivato del lavoro svolto qui. Questo sarà un elemento cliccabile nelle preferenze dell'utente.
tracker_url
Dato che il componente aggiuntivo POV è una parte incorporata di Blender, viene fornito con una propria voce di tracciamento dei bug e questa chiave fornirà un puntatore ad esso.
Commenti
I commenti a riga singola iniziano con un carattere cancelletto (#) immediatamente seguito da uno spazio vuoto e il commento inizia con una lettera maiuscola ma non termina con un punto. Possono essere aggiunti dopo una riga di codice sufficientemente breve (lasciare uno spazio vuoto prima del #).
Ad esempio, all'inizio del file, il commento
# <pep8 compliant>
significa che una delle direttive di stile di formattazione/codifica del testo da utilizzare è PEP8 quindi ad esempio dovresti assicurarti di creare righe sufficientemente brevi in tutto lo script file di meno di 79 caratteri e meno di 72 per docstring (vedi sotto) o commenti. Nota che questo commento ha una sintassi speciale: Periodicamente, la Blender Foundation controlla gli script di Blender per la conformità pep8, affinché gli script siano inclusi in questo controllo, aggiungi questa riga come commento all'inizio dello script.
Altre regole includono:
- Camel caps per i nomi delle classi: MyClass
- tutti i nomi dei moduli separati da caratteri di sottolineatura minuscoli: mio_modulo
- 4 rientri di spazio (nessuna tabulazione)
- spazi attorno agli operatori matematici: 1 + 1, non
1+1
- Utilizzare solo importazioni esplicite (non eseguire mai import *)
- Non utilizzare una sola riga per un test e l'istruzione che dipende da esso:
if val : body, invece, separare i blocchi di istruzioni dalle condizioni da cui dipendono di almeno 2 righe.
Mentre i commenti su più righe possono iniziare con virgolette triple. È convenzione che vengano usate tre virgolette doppie per docstrings, un tipo specifico di commento che non fa nulla nel programma ma che appare comunque quando la console viene utilizzata per ottenere aiuto su una funzione.
"""Questa è una spiegazione generale"""
Oltre ad essere una buona pratica inserire una stringa di documento all'inizio di ogni classe per descriverla e anche all'inizio di ogni file per spiegarne l'utilizzo relativo, ciò consente anche alle chiamate da riga di comando di visualizzare questo documento su richiesta. Questa è una delle principali differenze tra i commenti docstrings e inline (solo su parte di una riga).
Funzioni
Le funzioni sono definite con def, seguita dal nome della funzione e da una coppia di parentesi. Quando si fa riferimento alla funzione per spostarla e passarla da un file all'altro, non ha più bisogno delle parentesi, ma quasi ogni altra volta queste seguono il nome della funzione e tra di esse si possono facoltativamente specificare alcuni attributi.
Classi
Le Classi non solo ti consentono di creare definizioni riutilizzabili. Ma questo è l'unico modo per configurare gli elementi dell'interfaccia utente in Blender. La buona notizia è che ciò significa che gli sviluppatori di componenti aggiuntivi hanno effettivamente meno lavoro da fare. L'implementazione hardcoded si occupa di determinare quando e quali parti aggiornare automaticamente per tutti gli elementi dell'interfaccia utente e l'ereditarietà delle classi tra cui scegliere (l'API) garantisce che si possano gestire le situazioni in modo ottimizzato. Alcune funzioni legacy come draw() devono essere sovrascritte dallo sviluppatore del componente aggiuntivo che le riscrive in base alle proprie esigenze.
Per creare nuove classi, utilizzare il consueto schema di definizione delle classi come mostrato nei modelli Python nell'editor di testo interno di Blender... Oppure copiarne e incollarne uno altrove nello script.
Nomenclatura delle classi
Rispettare il più possibile lo stesso modello di denominazione richiesto dalla Blender Foundation: OT significa che è un operatore, MT un menu, PT un pannello: Il nome della classe deve essere composto come segue: NOMEDELLADDON_PT_pannello_principale NOMEDELLADDON - In lettere maiuscole digitiamo il nome del componente aggiuntivo. È possibile utilizzare un carattere di sottolineatura se il nome contiene più di una parola.
_PT_ - viene poi separata da due lettere che designano il tipo di classe (il cui tipo è ereditato).
pannello_principale - utilizzando le lettere minuscole possiamo digitare il nome dell'operatore/pannello/intestazione ecc.
Altri tipi di classi:
HT – Header MT – Menu OT – Operator PT – Panel UL – UI list
Quindi, nel nostro caso il nome del componente aggiuntivo è POV, ecco alcuni esempi di nomi di classi validi e relativi identificatori:
class POV_MATERIAL_PT_replacement_field(bpy.types.Panel):
bl_idname = 'pov_PT_replacement_field_panel'
class POV_OT_add_basic(bpy.types.Operator):
bl_idname = 'pov.addbasicitem'
class POV_VIEW_MT_Basic_Items(bpy.types.Menu):
bl_idname = 'pov_MT_basic_items_tools'
Notate come l'ID dell'operatore è diverso? Non è necessario aggiungere "OT" ma piuttosto un punto. Inoltre non è necessario aggiungere "_operator" alla fine.
L'API Python di Blender consente l'integrazione di:
bpy.types.Panel
bpy.types.Menu
bpy.types.Operator
bpy.types.PropertyGroup
bpy.types.KeyingSet
bpy.types.RenderEngine
Questo è intenzionalmente limitato. Attualmente, per funzionalità più avanzate come i modificatori di mesh, i tipi di oggetto o i nodi shader, dovrebbe essere utilizzato C/C++.
Per l'integrazione con Python, Blender definisce metodi comuni a tutti i tipi. Funziona creando una sottoclasse Python di una classe Blender che contiene variabili e funzioni specificate dalla classe genitore che sono predefinite per interfacciarsi con Blender.
Nota che stiamo creando una sottoclasse di un membro di bpy.types, questo è comune a tutte le classi che possono essere integrate in Blender e utilizzate, quindi sappiamo se si tratta di un operatore e non di un pannello quando entri nel registro.
Entrambe le proprietà della classe iniziano con il prefisso bl_. Questa è una convenzione usata per distinguere le proprietà di Blender da quelle che aggiungi tu stesso.
Operatori
La maggior parte dei componenti aggiuntivi definisce nuovi operatori, si tratta di classi che implementano funzionalità specifiche. La definizione effettiva dell'operatore prende la forma di una classe derivata da bpy.types.Operator
import bpy
class MyTool(bpy.types.Operator):
"""Fai cose su un oggetto"""
bl_idname = "object.fai_cose_su_oggetto"
bl_label = "Fai cose su un oggetto"
bl_options = {'REGISTER', 'UNDO'}
La docstring all'inizio della definizione della classe verrà utilizzata come tooltip ovunque questo operatore sia disponibile, ad esempio in un menu, mentre bl_label definisce l'etichetta effettiva utilizzata nella voce di menu stessa. Qui li abbiamo mantenuti entrambi uguali. Gli operatori faranno parte dei dati di Blender e saranno memorizzati nel modulo bpy.ops. Questo bl_idname garantirà che l'input per questo operatore verrà chiamato bpy.ops.object.move_object. Gli operatori vengono normalmente registrati in modo da renderli utilizzabili e questa è infatti l'impostazione predefinita di bl_options. Tuttavia, se vogliamo che il componente aggiuntivo appaia anche nella cronologia in modo che possa essere annullato o ripetuto, dobbiamo aggiungere UNDO all'insieme di flag assegnati a bl_options, come qui.
Gli operatori hanno limitazioni che possono rendere noiosa la loro scrittura.
I limiti principali sono...
Non è possibile trasferire dati come oggetti, mesh o materiali su cui operare (gli operatori utilizzano invece il contesto)
Il valore restituito dalla chiamata di un operatore è successo (indipendentemente dal fatto che sia stato completato o annullato), in alcuni casi avrebbe più senso per l'API restituire il risultato dell'operazione.
La funzione poll() per richiestare gli operatori potrebbe non riuscire quando una funzione API lancia un'eccezione fornendo i dettagli del motivo esatto.
La funzione execute()
Una classe operatore può avere un numero qualsiasi di funzioni membro ma per essere utile normalmente sovrascrive la funzione execute() :
def execute(self, context):
context.active_object.rotation.z += 33
return {'FINISHED'}
La funzione execute() viene passata un riferimento a a a contesto oggetto. Questo oggetto di contesto contiene tra l'altro un attributo active_object che punta a l'oggetto attivo di Blender. Ogni oggetto in Blender ha un attributo di rotazione che è un vettore con componenti x, y e z. Cambiare la rotazione di un oggetto è semplice come cambiare uno di questi componenti, che è esattamente ciò che facciamo nella riga 2.
La funzione execute() segnala l'esecuzione riuscita restituendo un set di flag, in questo caso un set costituito solo dalla stringa di caratteri "FINISHED".
Quando abilitiamo la casella di controllo Enable add-on nelle preferenze, Blender cercherà una funzione register() e la eseguirà. Allo stesso modo, quando si disattiva un componente aggiuntivo, viene richiamata la funzioneunregister(). Usiamoli quindi sia per registrare il nostro operatore con Blender sia per inserire una voce di menu che faccia riferimento al nostro operatore. è inoltre importante annullare l'iscrizione e disattivarla affinché il nostro componente aggiuntivo non venga percepito come invasivo o distruttivo di una configurazione precedente senza possibile ritorno.
Pour créer une entrée de menu, nous devons faire deux choses :
- Créer une fonction qui produira une entrée de menu
- Et ajouter cette fonction à un menu approprié.
De nos jours, presque tout dans Blender est disponible sous forme d'objet Python et les menus ne font pas exception. Nous voulons ajouter notre entrée au menu Objet dans la vue 3D, nous appelons donc
bpy.types.VIEW3D_MT_object.append()
et lui passer une référence à la fonction que nous définissons un peu plus bas. Comment savons-nous le nom de cet objet de menu? Si vous avez cochéFile⇒ User preferences ⇒ Interface ⇒ Python Tooltips le nom du menu sera affiché dans une info-bulle lorsque vous survolez un menu. À partir de l'image ci-dessus, nous pouvons voir que nous pouvons utiliser bpy.types.VIEW3D_MT_object.append() pour ajouter quelque chose au menu Objet car VIEW3D_MT_object s'affiche alors dans le texte de la bulle apparue au survol de ce menu.
La fonction menu_func() n'implémente pas d'action elle-même (c'est le rôle d'un opérateur) | |
Mais lorsqu'elle est appelée, menu_func() ajoutera un élément d'interface utilisateur pointant vers l'objet qui lui est passé dans le paramètre self. Cet élément d'interface peut être modifié par l'utilisateur et, à son tour, interagira avec l'opérateur. |
Ici, nous allons simplement ajouter une entrée d'opérateur (c'est-à-dire un élément qui exécutera notre opérateur lorsqu'il sera cliqué).
L'argument self passé à menu_func() fait référence au menu.
Ce menu possède un attribut layout avec une fonction operator() à laquelle nous passons le nom de notre opérateur. Cela garantira qu'à chaque fois qu'un utilisateur survolera le menu Object, notre opérateur sera affiché dans la liste des options.
Le nom de notre nouvel opérateur MyTool se trouve dans son attribut bl_idname, c'est pourquoi nous passons MyTool.bl_idname.
Le nom de l'entrée et son info-bulle sont déterminés en regardant le bl_label et la docstring définis dans notre classe MyTool et l'icône utilisée dans le menu est déterminée en passant un paramètre facultatif icon à la fonction operator().
Définir un menu ne suffit pas en soi à rendre ce menu utilisable. Pour que l'utilisateur puisse le trouver et l'utiliser, nous devons ajouter le menu à son parent depuis la fonction register() à la fin de son fichier python.
def menu_func(self, context):
self.layout.operator(MyTool.bl_idname, icon='MESH_CUBE')
def register():
bpy.types.VIEW3D_MT_object.append(menu_func)
def unregister():
bpy.types.VIEW3D_MT_object.remove(menu_func)
Les opérateurs doivent être enregistrés de la même manière, donc ajouter un opérateur enregistré à un menu nécessite deux à trois actions distinctes :
- Enregistrer l'opérateur ;
- puis l'ajouter à un menu existant ;
- Ou enregistrer d'abord le menu nouvellement créé auquel il doit appartenir et ensuite seulement y ajouter l'opérateur.
Ce n'est qu'après tout cela que le nouvel opérateur apparaîtra sur l'interface utilisateur, du moins, si rien d'autre, en appuyant sur F3 dans la fenêtre de vue 3D et en saisissant le libellé de l'opérateur
Tout cela pourrait paraître une peu plus compliqué que nécessaire mais cela permet par exemple d'afficher bien d'autres choses qu'uniquement de simples entrées cliquables dans un menu, par exemple de regrouper plusieurs opérateurs dans une même case, par exemple s'ils font partie de modalités disponibles dans une même sous-thématique, etc.
Les commandes unregister() de désinscription au registre doivent apparaître dans l'ordre inverse de leurs lignes register() correspondantes | |
Cela garantit que vous n'essayez pas de désinscrire, par exemple, un menu qui a été ajouté en tant qu'enfant d'une classe d'élément d'interface utilisateur que vous pourriez essayer de désinscrire avant cet enfant, désinscrivant ainsi quelque chose qui n'est déjà plus là... Ce qui déclencherait une erreur et l'échec de l'activation/désactivation du module complémentaire. |
Optimisation
Gardez vos notations "pointées" minimale
Comme dans de nombreux langages orientés objet, Python permet la visualisation d'une structure hiérarchique avec ses niveaux séparés par des points (imaginez chaque point comme l'ouverture de la plus grande poupée russe à gauche qui enjambe les plus petites à sa droite).
Remplacez ses parcours inutilement répétés par des alias | |
L'opérateur point est un moyen simple de récupérer une classe, une donnée, une méthode, n'importe quel objet Python inclus dans une hiérarchie plus large... Mais sachez qu'il est coûteux, surtout s'il est utilisé dans une boucle. |
Loorsque l'on invoque
bpy.data.objects["my_obj"]
Chaque point plonge plus profondément d'un niveau et toute la hiérarchie est parcourue jusqu'à lui. Vous devez toujours le faire au moins une fois pour atteindre l'élément souhaité, il est donc acceptable d'utiliser cette syntaxe lorsque cette référence n'est faite qu'une seule fois, mais au-delà, préférez toujours éviter de refaire entièrement tout ce parcours, et donnez lui plutôt une variable d'alias comme
obj = bpy.data.objects["my_obj"]
Ainsi vous pouvez ensuite toujours l'utiliser, en ne gérant plus que les ramifications du parcours vers des attributs occasionnels à usages uniques comme l'exemple ci-dessous:
obj.location.z += 1
pour tous les autres attributs disponibles à vos opérations de lecture ou d'écriture. Définissez autant de ces alias requis qu'il le faut avant d'entrer dans une boucle plutôt qu'à l'intérieur de celle-ci. Imaginez si votre GPS vous indiquait toujours les directions précédentes prises depuis le point de départ en passant par les suivantes à chaque virage !
Utilisez try/except avec parcimonie
L'instruction try est utile pour gagner du temps lors de l'écriture d'un code de vérification d'erreurs.
Cependant, try est nettement plus lent qu'un if car une exception doit aussi être définie à chaque fois, évitez donc d'utiliser try dans les zones de votre code qui s'exécutent en boucle et s'exécutent plusieurs fois.
Il existe des cas où l'utilisation de try est plus rapide que d'explicitement détecter par un test (if ou autre) la condition complexe qui génèrerait potentiellement une erreur, il vaut donc la peine d'expérimenter.
Une bonne façon de supprimer certains de vos try ... except lorsque l'erreur constatée s'avère une AttributeError est de les remplacer par un test en amont avec la fonction hasattr() :
for member in dir(properties_freestyle):
subclass = getattr(properties_freestyle, member)
try:
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
except:
pass
pourrait alors devenir
for member in dir(properties_freestyle):
subclass = getattr(properties_freestyle, member)
if hasattr(subclass, "COMPAT_ENGINES"):
subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
Si vous devez utiliser try:... except:... ne laissez pas le type d'exception non spécifié, cela ne donne absolument aucun retour d'information ni aucune lisibilité du code. Au lieu de cela, si vous n'avez aucune idée de ce que cela pourrait être, remplacez le par quelque chose qui donnerait plus d'informations pendant votre phase de débogage :
(BaseException est au sommet de la hiérarchie des exceptions)
def your_function(b, c):
try:
a = b + c
return True
except BaseException as e:
print(e.__doc__)
print('An exception occurred: {}'.format(e))
return False
Séparez vos fichiers Python (*.py)
Historiquement, le script a ensuite été agencé en unpackage (plusieurs fichiers dans un dossier) en utilisant le moins de fichiers possible pour que le flux de données soit plus facile à comprendre. Il y avait initialement 3 fichiers principaux:
- __init__.py
- ui.py
- render.py
Cependant, certains fichiers dépassaient les 10 000 lignes, ce qui, pour un tel add-on était encore trop volumineux. Il est donc actuellement toléré que le plus gros fichier reste en dessous de 2000 lignes. Il est très probable que les fichiers pourraient perdre un peu de poids, en supprimant le support 32 bits obsolète et en modularisant une partie du code en quelques fonctions plus réutilisables. Voici les fichiers actuels composant l'add-on et leur utilisation respective :
Initialisation
__init__.py
Création des préférences utilisateur du module complémentaire POV, autorisation de l'utilisateur à mettre à jour le module complémentaire vers la dernière version et chargement de tous les autres modules. (Notez les doubles traits de soulignement autour du nom du fichier. Cela en fait le premier fichier lancé) définition de l'unité « principale » pour un package ; cela oblige également Python à traiter le répertoire spécifique comme un package. C'est l'unité qui sera utilisée lorsque l'on appele import render_povray (et render_povray est un répertoire).
scenography_properties.py
Initialisation des propriétés pour traduire les paramètres de caméra/lumière/environnement de Blender en langage POV.
object_properties.py
Initialisation des propriétés pour traduire les paramètres d'objets de Blender en langage POV.
shading_properties.py
Initialisation des propriétés pour traduire les paramètres de matériaux de Blender en langage POV.
texturing_properties.py
Initialisation des propriétés pour traduire les paramètres d'influences de textures des matériaux, d'environnement (world)... de Blender en langage POV.
render_properties.py
Initialisation des propriétés pour traduire les paramètres de rendu (de Blender mais surtout les plus spécifiquement natifs de POV).
scripting_properties.py
Initialisation des propriétés et champs pour renseigner des instructions directement dans le langage de description de scène POV.
Interface
Quelques notes à garder à l’esprit lors de la création d'éléments d’interface utilisateur :
Les commandes fournies par Blender pour l'interface utilisateur sont relativement simples. Les déclarations d'interface sont là pour créer facilement une disposition acceptable. Règle générale ici : si vous avez besoin de plus de code pour la disposition de leur interface que pour les propriétés elles-mêmes, alors vous vous y prenez mal.
Exemples d'interface:
layout()
La disposition de base est une disposition simple de Haut -> en Bas.
layout.prop() layout.prop()
layout.row()
Use row(), when you want more than 1 property in one line.
row = layout.row() row.prop() row.prop()
layout.column()
Use column(), when you want your properties in a column.
col = layout.column() col.prop() col.prop()
layout.split()
This can be used to create more complex layouts. For example you can split the layout and create two column() layouts next to each other. Don’t use split, when you simply want two properties in a row. Use row() for that.
split = layout.split()
col = split.column() col.prop() col.prop()
col = split.column() col.prop() col.prop()
Declaration names:
Try to only use these variable names for layout declarations:
row for a row() layout
col for a column() layout
split for a split() layout
flow for a column_flow() layout
sub for a sub layout (a column inside a column for example)
base_ui.py
Provide a Moray like workspace and define some utility functions. Load all the other GUI related modules
scenography_gui.py)
Display cam/light/environment properties from situation_properties.py for user to change them
object_gui.py :
Display properties from object_properties.py for user to change them
shading_gui.py
Display properties from shading_properties.py for user to change them
shading_nodes.py
Translate node trees to the pov file
texturing_gui.py
Display properties from texturing_properties.py for user to change them
render_gui.py
Display properties from render_properties.py for user to change them
scripting_gui.py
Display properties from scripting_properties.py for user to add his custom POV code
Render / import / edit
render.py
Translate geometry and UI properties (Blender and POV native) to the POV file
scenography.py
Translate cam/light/environment properties to corresponding pov features
primitives.py
Display some POV native primitives in 3D view for input and output
object_mesh_topology.py
Translate to POV the meshes geometries
object_curve_topology.py
Translate to POV the curve based geometries
object_particles.py
Translate to POV the particle based geometries
shading.py
Translate shading properties to declared textures at the top of a pov file
texturing.py
Translate blender texture influences into POV
df3_library.py
Render smoke to *.df3 files
scripting.py
Insert POV native scene description elements to exported POV file
update_files.py
Update new variables to values from older API. This file needs an update
Presets
Along these essential files also coexist a few additional libraries to help make Blender stand up to other Persistence of Vision compatible frontends such as povwin or QTPOV
Material (sss)
Radiosity
World
Light
04_(6000K)_2500W_HMI_(Halogen_Metal_Iodide).py
05_(4000K)_100W_Metal_Halide.py
06_(3200K)_100W_Quartz_Halogen.py
09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py
10_(4300K)_40W_Vintage_Fluorescent_T12.py
11_(5000K)_18W_Standard_Fluorescent_T8.py
12_(4200K)_18W_Cool_White_Fluorescent_T8.py
13_(3000K)_18W_Warm_Fluorescent_T8.py
14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py
15_(3200K)_40W_Induction_Fluorescent.py
16_(2100K)_150W_High_Pressure_Sodium.py
17_(1700K)_135W_Low_Pressure_Sodium.py
18_(6800K)_175W_Mercury_Vapor.py
22_(30000K)_40W_Black_Light_Fluorescent.py
templates
C++
Some areas could only be added POV specific functionality using C++ :
blender/editors/space_text/text_format_pov.c
blender/editors/space_text/text_format_ini.c
Mspace_text.c
Mtext_format.h
Mtext_format_ini.c
Mtext_format_pov.c
and the MCMakelists.txt had to be modified to include added files.
But in Blender, all of the user interface is written in Python, that's why the file scripts/startup/bl_ui/space_text.py also has been updated even though it lies outside the usual addons folder:
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8-80 compliant>
import bpy
from bpy.types import Header, Menu, Panel
from bpy.app.translations import pgettext_iface as iface_
class TEXT_HT_header(Header):
bl_space_type = 'TEXT_EDITOR'
def draw(self, context):
layout = self.layout
st = context.space_data
text = st.text
row = layout.row(align=True)
row.template_header()
TEXT_MT_editor_menus.draw_collapsible(context, layout)
if text and text.is_modified:
sub = row.row(align=True)
sub.alert = True
sub.operator("text.resolve_conflict", text="", icon='HELP')
row = layout.row(align=True)
row.template_ID(st, "text", new="text.new", unlink="text.unlink", open="text.open")
row = layout.row(align=True)
row.prop(st, "show_line_numbers", text="")
row.prop(st, "show_word_wrap", text="")
row.prop(st, "show_syntax_highlight", text="")
if text:
osl = text.name.endswith(".osl") or text.name.endswith(".oso")
if osl:
row = layout.row()
row.operator("node.shader_script_update")
else:
row = layout.row()
row.operator("text.run_script")
row = layout.row()
row.active = text.name.endswith(".py")
row.prop(text, "use_module")
row = layout.row()
if text.filepath:
if text.is_dirty:
row.label(text=iface_("File: *%r (unsaved)") %
text.filepath, translate=False)
else:
row.label(text=iface_("File: %r") %
text.filepath, translate=False)
else:
row.label(text="Text: External"
if text.library
else "Text: Internal")
class TEXT_MT_editor_menus(Menu):
bl_idname = "TEXT_MT_editor_menus"
bl_label = ""
def draw(self, context):
self.draw_menus(self.layout, context)
@staticmethod
def draw_menus(layout, context):
st = context.space_data
text = st.text
layout.menu("TEXT_MT_view")
layout.menu("TEXT_MT_text")
if text:
layout.menu("TEXT_MT_edit")
layout.menu("TEXT_MT_format")
layout.menu("TEXT_MT_templates")
class TEXT_PT_properties(Panel):
bl_space_type = 'TEXT_EDITOR'
bl_region_type = 'UI'
bl_label = "Properties"
def draw(self, context):
layout = self.layout
st = context.space_data
flow = layout.column_flow()
flow.prop(st, "show_line_numbers")
flow.prop(st, "show_word_wrap")
flow.prop(st, "show_syntax_highlight")
flow.prop(st, "show_line_highlight")
flow.prop(st, "use_live_edit")
flow = layout.column_flow()
flow.prop(st, "font_size")
flow.prop(st, "tab_width")
text = st.text
if text:
flow.prop(text, "use_tabs_as_spaces")
flow.prop(st, "show_margin")
col = flow.column()
col.active = st.show_margin
col.prop(st, "margin_column")
class TEXT_PT_find(Panel):
bl_space_type = 'TEXT_EDITOR'
bl_region_type = 'UI'
bl_label = "Find"
def draw(self, context):
layout = self.layout
st = context.space_data
# find
col = layout.column(align=True)
row = col.row(align=True)
row.prop(st, "find_text", text="")
row.operator("text.find_set_selected", text="", icon='TEXT')
col.operator("text.find")
# replace
col = layout.column(align=True)
row = col.row(align=True)
row.prop(st, "replace_text", text="")
row.operator("text.replace_set_selected", text="", icon='TEXT')
col.operator("text.replace")
# settings
layout.prop(st, "use_match_case")
row = layout.row(align=True)
row.prop(st, "use_find_wrap", text="Wrap")
row.prop(st, "use_find_all", text="All")
class TEXT_MT_view(Menu):
bl_label = "View"
def draw(self, context):
layout = self.layout
layout.operator("text.properties", icon='MENU_PANEL')
layout.separator()
layout.operator("text.move",
text="Top of File",
).type = 'FILE_TOP'
layout.operator("text.move",
text="Bottom of File",
).type = 'FILE_BOTTOM'
layout.separator()
layout.operator("screen.area_dupli")
layout.operator("screen.screen_full_area")
layout.operator("screen.screen_full_area", text="Toggle Fullscreen Area").use_hide_panels = True
class TEXT_MT_text(Menu):
bl_label = "Text"
def draw(self, context):
layout = self.layout
st = context.space_data
text = st.text
layout.operator("text.new")
layout.operator("text.open")
if text:
layout.operator("text.reload")
layout.column()
layout.operator("text.save")
layout.operator("text.save_as")
if text.filepath:
layout.operator("text.make_internal")
layout.column()
layout.operator("text.run_script")
class TEXT_MT_templates_py(Menu):
bl_label = "Python"
def draw(self, context):
self.path_menu(
bpy.utils.script_paths("templates_py"),
"text.open",
props_default={"internal": True},
)
class TEXT_MT_templates_osl(Menu):
bl_label = "Open Shading Language"
def draw(self, context):
self.path_menu(
bpy.utils.script_paths("templates_osl"),
"text.open",
props_default={"internal": True},
)
class TEXT_MT_templates_pov(Menu):
bl_label = "POV-Ray Scene Description Language"
def draw(self, context):
self.path_menu(
bpy.utils.script_paths("templates_pov"),
"text.open",
props_default={"internal": True},
)
class TEXT_MT_templates(Menu):
bl_label = "Templates"
def draw(self, context):
layout = self.layout
layout.menu("TEXT_MT_templates_py")
layout.menu("TEXT_MT_templates_osl")
layout.menu("TEXT_MT_templates_pov")
class TEXT_MT_edit_select(Menu):
bl_label = "Select"
def draw(self, context):
layout = self.layout
layout.operator("text.select_all")
layout.operator("text.select_line")
class TEXT_MT_format(Menu):
bl_label = "Format"
def draw(self, context):
layout = self.layout
layout.operator("text.indent")
layout.operator("text.unindent")
layout.separator()
layout.operator("text.comment")
layout.operator("text.uncomment")
layout.separator()
layout.operator_menu_enum("text.convert_whitespace", "type")
class TEXT_MT_edit_to3d(Menu):
bl_label = "Text To 3D Object"
def draw(self, context):
layout = self.layout
layout.operator("text.to_3d_object",
text="One Object",
).split_lines = False
layout.operator("text.to_3d_object",
text="One Object Per Line",
).split_lines = True
class TEXT_MT_edit(Menu):
bl_label = "Edit"
@classmethod
def poll(cls, context):
return (context.space_data.text)
def draw(self, context):
layout = self.layout
layout.operator("ed.undo")
layout.operator("ed.redo")
layout.separator()
layout.operator("text.cut")
layout.operator("text.copy")
layout.operator("text.paste")
layout.operator("text.duplicate_line")
layout.separator()
layout.operator("text.move_lines",
text="Move line(s) up").direction = 'UP'
layout.operator("text.move_lines",
text="Move line(s) down").direction = 'DOWN'
layout.separator()
layout.menu("TEXT_MT_edit_select")
layout.separator()
layout.operator("text.jump")
layout.operator("text.start_find", text="Find...")
layout.operator("text.autocomplete")
layout.separator()
layout.menu("TEXT_MT_edit_to3d")
class TEXT_MT_toolbox(Menu):
bl_label = ""
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_DEFAULT'
layout.operator("text.cut")
layout.operator("text.copy")
layout.operator("text.paste")
layout.separator()
layout.operator("text.run_script")
classes = (
TEXT_HT_header,
TEXT_MT_edit,
TEXT_MT_editor_menus,
TEXT_PT_properties,
TEXT_PT_find,
TEXT_MT_view,
TEXT_MT_text,
TEXT_MT_templates,
TEXT_MT_templates_py,
TEXT_MT_templates_osl,
TEXT_MT_templates_pov,
TEXT_MT_edit_select,
TEXT_MT_format,
TEXT_MT_edit_to3d,
TEXT_MT_toolbox,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)
Notice that the special command line
# <pep8-80 compliant>
writes pep-80 rather than pep-8 as getting closer to the native blender program often requires more demanding standards : pep-80 asks for even shorter line lengths and so a file passing the pep-80 automated tests that the Blender Foundation regularly runs might fail passing PEP-80.