User Tools

Site Tools


doc_opener

====== Differences ====== This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
doc_opener [2015/08/19 14:14]
swann
doc_opener [2015/08/20 12:07] (current)
swann [c. custompath operator]
Line 32: Line 32:
 Dans cette partie, nous allons éclaircir le fonctionnement de certaines fonctions clef de l'​addon. Dans cette partie, nous allons éclaircir le fonctionnement de certaines fonctions clef de l'​addon.
  
-=== a. initSceneProperties ===+==== a. initSceneProperties ​====
 C'est ici que ce déroule l'​enregistrement des variables de scènes, nécessaires à toutes sortes de choses. On retrouve par exemple tous les champs, que ce soit la séquence, l'​asset ou autre sous forme de variable de scène ici. C'est ici que ce déroule l'​enregistrement des variables de scènes, nécessaires à toutes sortes de choses. On retrouve par exemple tous les champs, que ce soit la séquence, l'​asset ou autre sous forme de variable de scène ici.
 Cette fonction de divise en deux parties, l'​instentiations de variables déstinées à être **dynamiques** ou **statiques**.\\ Cette fonction de divise en deux parties, l'​instentiations de variables déstinées à être **dynamiques** ou **statiques**.\\
 Ici nous suivrons le code séquentiellement,​ et donc commencerons par la partie dynamique.\\ Ici nous suivrons le code séquentiellement,​ et donc commencerons par la partie dynamique.\\
 +
 +Dans Blender, l'​actualisation des propriétés de scènes EnumProperty pose toujours problème: il est impossible de les mettres à jour sans les supprimer et les recréer. Ainsi, pour mettre en place les Enum dynamique, j'ai simplement crée une fonction(UpdateEnum) donc l'​unique utilité est de supprimer et de recréer les Enums passés en paramètre.\\ ​
 +Dans le cas de l'​Opener,​ les propriétés dynamique servent à mémoriser les assets,​séquences,​shots,​stores,​... ajoutés par l'​utilisateur. \\
 +L'​exemple qui suit est juste une partie succinte du code de la fonction initSceneProperties concernant la création de la propriété stockant le(s) répertoires choisi par l'​utilisateur.\\
 +
 <code python> <code python>
 #DYNAMIC ATTIBS------------------------------------------------->​ #DYNAMIC ATTIBS------------------------------------------------->​
-#contient ​toutes les propriété ​dynamiques+#Liste contennant ​toutes les propriétés ​dynamiques
 properties = [] properties = []
  
-#Valeurs par défaut pour la propriété Store+#Valeurs par défaut pour la propriété Store(le repertroire racine)
 drives = (('​Store',"​Store directory",''​),​('/​u/​Project/',"/​u/​Project/",''​),​('​test2_1',"​test2_2",''​),​('',"",''​)) drives = (('​Store',"​Store directory",''​),​('/​u/​Project/',"/​u/​Project/",''​),​('​test2_1',"​test2_2",''​),​('',"",''​))
  
-#Création de l'​attibut ​vide+#Création de la sceneProperty ​vide
 bpy.types.Scene.drives = EnumProperty(name="​none",​description="​none",​items=((''​)),​update=interface.update_naming) bpy.types.Scene.drives = EnumProperty(name="​none",​description="​none",​items=((''​)),​update=interface.update_naming)
  
 s = len(properties) s = len(properties)
  
-#Ajout de la propriété ​      ​+#Ajout de la propriété ​pour l'​enregistrement ​     ​
 ressources.Items.append(('​none',"​none",""​)) ​ ressources.Items.append(('​none',"​none",""​)) ​
  
 +#Pour chaque propriétés
 for i in range(s): for i in range(s):
    #​Setup store dir    #​Setup store dir
-   if properties[i][0][0] == '​Store':​  +   if properties[i][0][0] == '​Store': ​#Dans le cas du store 
-       if not persistence.load_config(): ​     +       #Si il n'y a pas de fichier persistent déjà existant, on rempli avec les valeurs par défaut ​ 
 +       if not persistence.load_config(): ​    ​
            for line in range(1,​len(properties[i])):​            for line in range(1,​len(properties[i])):​
                 essources.Items.append((str(properties[i][line][0]),​str(properties[i][line][1]),​str(properties[i][line][2]))) ​                         essources.Items.append((str(properties[i][line][0]),​str(properties[i][line][1]),​str(properties[i][line][2]))) ​        
 +       #On met à jour la variable grâce à la fonction UpdateEnum
        ​interface.UpdateEnum(bpy.types.Scene,​ressources.Items,​properties[i][0][0],​properties[i][0][1],​ressources.Items[0][0]) ​   ​        ​interface.UpdateEnum(bpy.types.Scene,​ressources.Items,​properties[i][0][0],​properties[i][0][1],​ressources.Items[0][0]) ​   ​
 </​code>​ </​code>​
 +
 +Ci-dessous, un exemple de déclaration de propriété de scène statique, pour la propriété root.
  
 <code python> <code python>
 #STATICS ATTIBS------------------------------------------------->​ #STATICS ATTIBS------------------------------------------------->​
- 
 #​ROOT----------------------------->​ #​ROOT----------------------------->​
 bpy.types.Scene.roots = EnumProperty( bpy.types.Scene.roots = EnumProperty(
Line 76: Line 85:
 </​code>​ </​code>​
    
 +La partie intéressante est la ligne suivante:
 +<code python>
 +update = interface.update_naming
 +</​code>​
 +Cette dernière consiste à appeler la fonction **update_naming(self,​context)** __à chaque modification de la propriété__. Ce qui nous amène à cette fonction.
 +
 +==== b. update_naming ====
 +Comme précédement expliqué, Opener utilise la librairie de Naming. Cette dernière est mise en place dans le module interface.py,​ la fonction update_naming est chargé de préparer le dictionnaire avec tous les champs avant de le charger avec la librairie.
 +<code python>
 +#Copie du dictionnaire pour des vérifications
 +temp = ressources.path.copy()
 +    ​
 +#Nettoyage du dictionnaire path
 +ressources.path.clear()
 +    ​
 +#​Séparation des éléments en fonction de l'OS
 +if sys.platform == '​win32':​
 +   dest = bpy.context.scene.drives.split('​\\'​)
 +else:
 +   dest = bpy.context.scene.drives.split('/'​)
 +#Mise à jour du dictionnaire
 +if bpy.context.scene.roots == '​LIB':​
 +   ​ressources.path['​Lib'​] = '​LIB'​
 +   ​ressources.path['​Family'​]=bpy.context.scene.famille
 +   #Si jamais l'​asset sélectinné dans la liste est Other lors de la MAJ, on l'​ajoute aux assets
 +   if bpy.context.scene.asset == '​other':​
 +      #​Construction du tuple temporaire pour vérifier son unicité dans la liste des asset
 +      tempAsset = (str(bpy.context.scene.newA),​str(bpy.context.scene.newA),''​)
 +      #Si il n'​existe pas alors on l'​ajoute
 +      if tempAsset not in ressources.Items_asset:​
 +         #On récupère le nouvel asset à ajouter dans le dictionnaire
 +         ​ressources.path['​Asset'​]=bpy.context.scene.newA
 +         #On ajoute cet asset à la liste
 +         ​ressources.Items_asset.append((str(bpy.context.scene.newA),​str(bpy.context.scene.newA),''​))
 +         ​UpdateEnum('',​ressources.Items_asset,'​asset','',''​)
 +         ​bpy.context.scene.asset = bpy.context.scene.newA
 +      ​
 +      else:
 +         ​bpy.context.scene.newA = ""​
 +          ​
 +  else:
 +     ​ressources.path['​Asset'​]=bpy.context.scene.asset
 +</​code>​
 +
 +Dans les lignes suivantes, les champs dynamique sont mis à jour, si modification il y a eu.
 +<code python>
 +#​................Quelques lignes plus tard..............
 +#Si jamais lors de l'​execution de la fonction, on se trouve dans la fenêtre MOVIE alors:
 +elif (bpy.context.scene.roots == '​MOVIE'​) and (bpy.context.scene.drives != '​none'​) and (temp['​Dept'​] == ressources.path['​Dept'​]) and ('​Shot'​ in temp) and ('​Sequence'​ in temp):
 +   temps = persistence.load_seq()
 +   for i in range(len(temps)):​
 +      y = (str(temps[i]),​str(temps[i]),''​)
 +      if y not in ressources.Items_seq:​
 +        ressources.Items_seq.append((str(temps[i]),​str(temps[i]),''​))
 +        change = True
 +   #Si il y a de nouvelles séquences, on les ajoutes
 +   if change:
 +      UpdateEnum('',​ressources.Items_seq,'​seq','','​none'​)
 +      bpy.context.scene.shot = ressources.Items_shot[0][0]
 +   #Si jamais le shot n'a pas changé
 +   elif temp['​Shot'​] == bpy.context.scene.shot:​
 +      #On va scanner les dossier pour vérifier la présence de nouveaux shots
 +      temps = persistence.load_shots()
 +      ressources.Items_shot.clear()
 +      ressources.Items_shot.append(('​none','​none',''​))
 +      for i in range(len(temps)):​
 +         y = (str(temps[i]),​str(temps[i]),''​)
 +         #Si jamais il y en a des nouveaux, on les ajoutes
 +         if y not in ressources.Items_shot:​
 +            ressources.Items_shot.append((str(temps[i]),​str(temps[i]),''​))
 +     #On met à jour la liste des shots
 +     ​UpdateEnum('',​ressources.Items_shot,'​shot','','​none'​) ​
 +</​code>​
 +
 +Ces dernières lignes sont également très importantes,​ c'est ici que l'on va appeler la fonction qui, à l'aide de la librairie Naming et du dictionnaire ressource.path va génerer et vérifier l'​authenticité du chemin et des nom par rapport à une syntax précise.
 +
 +<code python>
 +#​................Quelques lignes plus tard..............
 +try:
 +   ​bpy.context.scene.newF = create_naming(self,​context,'',​ressources.path,​ressources.command) ​  
 +   ​files.Update_ListFile(bpy.context.scene.newF)
 +except:
 +   ​print('​naming no setup, clearing list')
 +   #​Nettoyage de la liste des fichier
 +   ​bpy.context.scene.custom.clear()
 +   ​ressources.command.append("​! missing field !")
 +   
 +</​code>​
 +
 +Nous allons maintenant parcourir un des opérateurs crée pour l'​addon. Il faut savoir que dans Blender, les Operateurs représentent un des outils les plus pratiques pour le développement pour de nombreuses raisons( ils sont facilement accessible pour l'​utilisateur,​ ils peuvent prendre des arguments,​etc,​...).\\
 +Dans l'​Opener,​ j'​utilise aussi les opérateurs à des fins ergonomiques pour mettre en place des boutons. En fin de compte, chaque boutons est un opérateur auquel on a assigné une icône lors de son appel dans l'​interface.
 +
 +
 +==== c. custompath operator====
 +
 +Pour apprendre à faire un opérateur de base, suivez le lien suivant:​[[http://​www.blender.org/​api/​blender_python_api_2_57_release/​bpy.types.Operator.html|Blender Operator Guide]]\\
 +
 +La première partie de l'​opérateur consiste naturellement à déclarer les différents champs est variables nécessaire:​
 +<code python>
 +#Entête avec héritage de l'​object Operator de blender, nécessaire à chaque fois que l'on créer un opérateur
 +class OBJECT_OT_custompath(bpy.types.Operator):​
 + ​bl_idname = "​object.custom_path"​
 + ​bl_label = "​open"​
 + ​__doc__ = "" ​  
 +    ​
 + ​filename_ext = ""​
 + ​filter_glob = StringProperty(default="",​ options={'​HIDDEN'​},​subtype='​DIR_PATH'​) ​   ​
 +        ​
 +
 + #​Variable stockant le chemin choisi
 + ​filepath = StringProperty(name="​File Path", description="​Filepath importing store dir", maxlen= 1024)
 + #​optionnel,​ récupère les fichiers séléctionnés ​
 + files = CollectionProperty(
 +  name="​File Path",
 +  type=bpy.types.OperatorFileListElement)
 +</​code>​
 +
 +Ici il s'agit du corps de l'​opérateur,​ la fonction execute va s'​éxecuter à chaque appel de l'​opérateur. Il en est de même ,pour les fonctions draw et invoke. ​
 +<code python>
 + def execute(self,​ context):
 +  #ajout du chemin sélectionné ​   ​
 +  ressources.Items.append((str(self.properties.filepath),​str(self.properties.filepath),""​))
 +  #Création du tuple temporaire
 +  t=(str(self.properties.filepath),​str(self.properties.filepath)))
 +  #Mise à jour de la liste des dossier racine
 +  interface.UpdateEnum(bpy.types.Scene,​ressources.Items,'​Store',​t)
 +        ​
 +  #Sauvegarde du dossier sélectionné de manière persistante
 +  persistence.write_config()
 +  #Find de fonction
 +  return {'​FINISHED'​}
 +  ​
 +  #Fonction d'​affichage de l'​explorateur de fichier
 +  def draw(self, context):
 +   ​self.layout.operator('​file.select_all_toggle'​) ​       ​
 +  def invoke(self,​ context, event):
 +   wm = context.window_manager
 +   ​wm.fileselect_add(self)
 +   ​return {'​RUNNING_MODAL'​} ​    
 +</​code>​
 ---- ----
 <WRAP center round important 60%> <WRAP center round important 60%>
doc_opener.1439993676.txt.gz · Last modified: 2015/08/19 14:14 by swann