La base

Si Maya a tiré son épingle du jeu dans le milieu des années 90, c'est en grande partie grâce à l'ouverture qu'il offrait. Le MEL était un avantage indéniable sur la concurrence.

Mais c'est surtout la possibilité de scripter des interfaces assez évoluées, sans avoir de compétences en programmation, qui l'a rendu indispensable aux graphistes un peu techniques.

Cela dit, si coder une interface en script en 95 était une révolution, on peut dire qu'en 2011 c'est un peu dépassé, même pour un "vrai" développeur.

En effet, les frameworks d'interface (sortes de grosses librairies de code) ont énormément évolués, gagnant en flexibilité, et avec toujours le même objectif: "Code less, create more".

C'est dans ce contexte qu'est "née" (notez que j'utilise les guillemets) la commande loadUI.

Précisions importantes concernant Qt

Comme vous le savez sûrement, depuis Maya 2011, le framework utilisé pour l'interface (et sûrement, à l'avenir beaucoup d'autres choses) est Qt (prononcez à l'anglaise: "Quioute").

Je ne vais pas m'étaler sur ce framework mais je vous encourage à lire cette très bonne présentation du "Site du zéro" (Et je vous encourage à y rester et fouiller un peu :sourit: ).

En revanche je vais passer plus de temps sur un point qui me semble important car j'ai entendu énormément de graphistes se planter en parlant de "Qt dans Maya", et pour cause, c'est assez subtil. Vous allez voir. :sourit:

Il faut distinguer deux choses (voir trois):

  • Le framework Qt.
  • Le "Qt dans Maya".
  • PyQt (pour ceux qui travaillent dans des studios qui l'utilisent).
Le framework Qt

Ça, c'est la librairie multiplateforme C++ dont je parle plus haut, le framework, le vrai.

Qt_logo002.png

C'est "ce" Qt qu'utilise les branquignoles développeurs de chez Autodesk quand ils compilent Maya (là, je viens de pourrir ma carrière à l’international avec ma blague minable/facile/pas drôle :baffed: ). Le viewport 2.0 est sûrement le fruit de l'utilisation de ce framework, comme pleins de petites features qui sont apparues avec Maya 2011.

Celle là, vous n'y avez pas accès "simplement"! Vous ne pouvez pas scripter des threads, ouvrir un contexte OpenGL, afficher des SVG, accéder à une base de données SQL, gérer des certificats SSL, créer vos propres widget, etc...

L'API de Maya permet de passer par une classe (MQtUtil) pour accéder de manière plus poussé aux "controls" (interface). Mais de ce que j'en lis, il n'y a rien de bien folichon. Pas d'accès direct à Qt. :nervous:

Le "Qt dans Maya"

Je ne compte plus le nombre de fois ou j'ai pu entendre: "On peut faire du Qt dans Maya 2011!" (à chantonner sur un ton guilleret et jovial parce que c'est new et que ça a l'air bien :marioCours: ).

maya_logo_bigcrop_w590_1_.jpg

La vérité, c'est qu'on peut faire du Qt (PyQt pour être exact) depuis Maya 2008. Depuis qu'on peut faire du Python dans Maya en fait... J'y reviendrai.

- "Mais pourtant si! Regarde, le mec sur internet, il utilise Qt Designer pour faire son interface, il clique et là... Oaaaaah! Il la charge dans Maya!"

Et nous y voila!

Si la définition de "faire du Qt" veut dire utiliser Qt Designer pour faire son interface et la charger, alors oui, on peut faire du Qt dans Maya.

Mais comme vous l'avez lu plus haut dans ce billet, Qt c'est beaucoup plus que ça (en fait ce n'est même pas ça).

- "Mais alors il fait quoi de ce fichier Qt Designer?"

Il le converti en MEL (ou en Python de Maya).

- "Wait! Wft! o_O"

Et oui, ce n'est pas super sexy mais c'est pourtant la vérité. Et pour vous le prouver, voici une citation de la doc:

The loadUI command creates the widgets specified in the file by executing their corresponding Maya commands.

Ok, sauf que si le widget en question n'a pas d'équivalent en MEL, il fait quoi? Et bien il ne le créé pas!

If the loadUI command encounters a Qt widget for which there is no equivalent Maya control, the Qt widget is still created, but you cannot access it from MEL or Python scripts.

Mange!

Et ça va même plus loin! :injures:

Démonstration:

interface_Maya2011_loadUI_001.png

Dans Qt Designer.

Notez l'utilisation d'un QStakedWidget (les deux petites flèches noires en haut à droite)

interface_Maya2011_loadUI_002.png

Dans Maya.

Les deux flèches ont disparu!

Bon, on s'en fout de ce widget (De toute façon personne ne l'utilise :baffed: ) mais l'idée est surtout de montrer qu'un QWidget qui n'existe pas dans Maya (pas d'équivalences MEL) est soit créé mais il est impossible d'y accéder, soit n'est pas être créé du tout!

Le log est aussi assez explicite:

# Creating a QStackedWidget named "stackedWidget". # 
# Creating a QWidget named "page_3". # 
# Creating a QGroupBox named "groupBox". # 
# Creating a QmayaCheckBox named "checkBox_2". # 
# Creating a QmayaCheckBox named "checkBox_3". # 
# Creating a QWidget named "page_4". # 
# Creating a QPushButton named "pushButton". # 
# Creating a QmayaCheckBox named "checkBox". # 
# There is no Maya command for objects of type QVBoxLayout. # 
# There is no Maya command for objects of type QVBoxLayout. # 
# There is no Maya command for objects of type QStackedLayout. # 
# There is no Maya command for objects of type QVBoxLayout. # 
# There is no Maya command for objects of type QVBoxLayout. # 
Tiens donc! Des QmayaCheckBox... :siffle:

Bref, vous l'aurez compris, "Qt dans Maya" ça veut bien dire ce que ça veut dire: C'est Qt encastré dans Maya. Et c'est Maya qui décide comment on l'utilise. Autrement dit, c'est du MEL...

PyQt

Et voici le dernier le la liste. Qui c'est quoi qu'est-ce?

RiverBanklogo.png

PyQt est le binding Python de Qt (enfin un des bindings, vu que Nokia a décidé, pour des raisons de licence, de faire le sien, qui de toute façon, a peine fini, n'a plus d'avenir).

Pour résumer, ça permet de faire du vrai Qt avec la syntaxe Python. Et comme on peut intégrer, dans Maya, n'importe quelle lib Python du moment qu'elle utilise la même version, on peut faire du PyQt dans Maya.

C'est particulièrement puissant pour les TDs (pas de connaissances en C++ requises). Toute les classes de la lib d'origine sont disponibles.

Et quand un graphiste arrive dans un studio qui utilise PyQt, il y a 90% de chance qu'il fasse un amalgame entre PyQt et le "Qt de Maya".

:reflechi:

Conclusion

Le fait que Maya utilise Qt ne veut pas dire qu'on peut faire du Qt dans Maya!

C'est Maya qui "fait du Qt" et vous qui utilisez Maya.

La pratique

Bon assez de blabla, passons à la pratique! :gniarkgniark:

Qt Designer

Pour faire une zolie n'interface, il faut utiliser Qt Designer. Malgré son apparente simplicité, il nécessite un certains temps d'adaptation avant de pouvoir sortir des UI convenables.

Vous devez être à l'aise avec les notions de "layout" notamment.

Une fois de plus, je vous invite à lire le tuto du "site du zéro" qui fait une très bonne présentation.

N'hésitez pas à vous chercher un peu sur le net, il y a beaucoup de tuto/sujets sur Qt Designer (Hop! Encore un!)

Voici une vidéo très bien faite (anglais):

Glissez quelques widgets dans votre interface, faites Ctrl+R pour faire quelques tests puis sauvegardez votre fichier ".ui".

interface_Maya2011_loadUI_003.png

Le script

C'est maintenant qu'on attaque Maya! :gniarkgniark:

Charger son interface dans maya

Pour lancer notre fenêtre faite avec Qt Designer, il faut lancer la nouvelle commande de Maya 2011: loadUI.

Pour cela, rien de plus simple:

import maya.cmds as cmds
mainWin = cmds.loadUI( uiFile="monFichier.ui" )
cmds.showWindow( mainWin )

Et voici notre fenêtre:

interface_Maya2011_loadUI_004.png

Maintenant que vous savez comment charger votre interface dans Maya, voyons comment demander à un bouton d'exécuter une commande.

Mais juste avant on va structurer un peu nos fichiers. :nannan:

La hierarchie

Pour organiser notre code, on va faire simple. Dans votre:

C:\Users\userName\Documents\maya\scripts

Placez les deux fichiers suivants:

  • myScript.ui contient le fichier Qt Designer.
  • myScript.py contient les éléments qui appellent l'interface ainsi que les scripts appelés par l'interface elle même.

Dans myScript.py, il est fortement conseillé de séparé la classe de l'ui + lib:

import maya.cmds as cmds
 
uiFilePath = "C:/Users/userName/Documents/maya/scripts/myScript.ui"	# le fichier ui
 
class ui() :
	"""Classe principale de l'interface"""
 
	def __init__( self ) :
		"""Initialise l'interface"""
 
		self.lib = lib()
 
		self.mainWin = cmds.loadUI( uiFile=uiFilePath )
 
		cmds.showWindow( self.mainWin )
 
	def toto( self, *arg ) :
		"""Fonction d'exemple qui utilise la lib"""
 
		self.lib.doSomething()
 
 
class lib() :
	"""Classe principale de le lib"""
 
	def doSomething( self ) :
		"""Fonction simple qui print quelque chose"""
 
		print "toto"

Le tout s'exécute dans Maya:

import myScript
reload(myScript)
myScript.ui()

Cette méthode permet de bien séparer le comportement de l'interface de celui de Maya:

  • L'UI communique avec la lib.
  • La lib communique avec Maya.

La lib ne doit jamais agir sur l'interface. C'est l'interface qui se modifie suivant ce que lui dit la lib. (Ça me rappelle un Chuck Norris fact tiens :seSentCon: )

Exécuter du code python

Vous pouvez définir des appels à des fonctions MEL ou Python directement depuis Qt Designer:

Comme indiqué dans la doc, il faut ajouter un attribut dynamique.

Sélectionnez un boutton et faites:

interface_Maya2011_loadUI_005.png

interface_Maya2011_loadUI_006.png

Notez l'utilisation du "+" pour préciser que l'argument qu'on va lui donner sera un argument Python.

Si dans le cas du MEL (qui n'est pas un langage objet) le fait d'entrer le nom complet de la commande à exécuter ne pose pas de soucis (c'est même comme ça qu'il faut faire), en Python, le fait de ne pas pouvoir passer un objet (au sens programmation) à son interface est assez problématique.

En effet, si vous passez self.toto vous aurez une jolie erreur lors du lancement:

interface_Maya2011_loadUI_007.png

# Error: name 'self' is not defined
# Traceback (most recent call last):
#   File "<maya console>", line 1, in <module>
# NameError: name 'self' is not defined #

Le soucis c'est que l'interface n'a pas d'accès à la classe. Donc impossible d'utiliser self.toto facilement.

Vous pouvez toujours ne pas utiliser les objets avec Python. Vous pourrez donc ne donner que le nom de la fonction à lancer. Mais c'est très dommage de ne pas utiliser la puissance des classes. :redface:

Mais alors, comment lancer une commande tel que self.toto avec cette méthode?

Et bien on édite le bouton (ou le widget à modifier) dans le code. On "repasse" dessus une fois qu'il est créé.

En effet, loadUI conserve les noms des widgets donné dans le .ui. Vous pouvez donc les appeler et les modifier dans votre code une fois le loadUI fait:

interface_Maya2011_loadUI_008.png

Note: N'oubliez pas de supprimer l'attribut dynamique que nous avons créé plus haut (+command). En effet, il est impossible de redéclarer un attribut si il est déjà setté dans le fichier .ui.

self.mainWin = cmds.loadUI( uiFile=uiFilePath )
 
# add callbacks
cmds.button( "totoPushButton", edit=True, command=self.toto )	# on modifie le button "totoPushButton" cree dans Qt Designer
 
cmds.showWindow( self.mainWin )

Et oui... C'est assez assez con subtil vous en conviendrez mais il semble qu'on ait pas le choix... :nousfesonslemal:

Cliquez sur le bouton, vous devriez voir "toto" s'écrire dans le script editor.

Note concernant les noms des widgets

Il est important de connaitre les limites concernant l'utilisation des noms des widgets des fichiers .ui dans Maya.

En effet, si Maya s'apprète à créer un widget qui a le nom qu'un widget déja présent dans votre session, Maya le renommera (souvent en ajoutant un chiffre derrière).

Il faut donc être rigoureux sur la gestion de ses widgets dans Maya. La meilleure solution consistant à supprimer la fenetre principale avant d'en recharger une nouvelle.

L'idée étant qu'aucun nom ne rentre en collision avec un autre. :reflechi:

Je n'ai pas essayé de construire des fenêtres "à la volée" (l'interface génère ses propres fenètres) via loadUI mais aux vues du fonctionnement, on ne peut utiliser qu'une seule instance.

Cas divers

caddies_vert_crop_small.jpg

Photo par gwenflickr sous licence Créative by-nc-sa. Merci à lui! :sourit:

Ici, je vous propose des solutions rapides à la plupart des cas particuliers qu'on rencontre.

Interagir avec l'interface

Comme les noms restent valides, on peut aussi s'en servir pour modifier un widget.

# change l'etat d'une checkBox
cmds.checkBox( "myCheckBox", edit=True, value=0 )
# ajoute un element a la liste
cmds.textScrollList( "myTextScrollList", edit=True, append="un element" )
Intégrer un widget MEL dans un .ui

Si un widget qui existe dans Maya n'est pas dispo dans Qt Designer (et il y en a un paquet), le plus simple est de faire une interface avec des layouts "vides" et de les intégrer une fois le loadUI fait via le flag parent:

cmds.intFieldGrp( parent="myParentLayout" )
Un mot sur les layouts

Sachez que les différents types de layouts de Qt Designer ne sont pas gérés. Ou plutôt si:

Any layouts in the file are created as generic layouts and are only accessible within Maya using the layout command, not the commands for specific types of layouts such as formLayout, columnLayout, and so forth.

En gros, quel que soit le type de layout créé (vertical, horizontal, etc...) il n'y a qu'un seul équivalent MEL: La commande layout.

Vous n'avez donc aucun contrôle dessus depuis le MEL, la commande layout ne permettant de faire quasiment rien.

Conclusion

Bon, maintenant il est temps de dire ce que je pense de ce système. :gniarkgniark:

Sur le fond je trouve que se faire des UI en se passant du script est une très bonne idée. Je l'ai fais en utilisant PyQt au boulot et c'est vraiment sympa et "pas prise de tête" de bosser comme ça (MELANIE proposait déja cette approche à l'époque mais n'était pas une réelle alternative).

Cela dit, Qt Designer ne permet pas d'intégrer directement des widgets Maya ce qui est, à la longue, assez chiant quand on créer ses fenetres (ce que vous voyez dans Qt Designer n'est pas ce que vous avez dans Maya). On est obligé de le faire "à la volé" une fois le loadUI fait.

De plus l'inverse est aussi vrai: Il est impossible d'utiliser des widgets de Qt Designer qui n'ont pas d'équivalents dans Maya. Et un widget aussi simple que spinBox n'est pas utilisable dans Maya.

interface_Maya2011_loadUI_011.png

Il est joli le widget? Ouai! Ben vous ne pouvez rien faire avec...

On peut imaginer des choses completement absurdes avec Qt Designer: La spinBox envoit un signal valueChanged avec sa valeur à un widget caché dont on n'est pas capable de récupérer la valeur en MEL... Mais ça serait tellement minable que je n'en parle même pas. :pasClasse:

Voici la liste des widgets Qt Designer et leur équivalents Maya (honteusement pompée sur le très bon tuto de css_maya).

1-push Button (under Buttons group) -------------------Maya command to access------------> button
2-radio button (under Buttons group) ------------------Maya command to access------------> radioButton
3-check box(under Buttons group) --------------------- Maya command to access------------> checkBox
4-combo box (under containers group) ------------------Maya command to access------------> optionMenu
5-line edit (under input widgets group) ---------------Maya command to access------------> textField
6-spin box (under input widgets group) ----------------Maya command to access------------> NONE
7-double spine box (under input widgets group)---------Maya command to access------------> NONE
8-dial (under input widgets group) --------------------Maya command to access------------> NONE
9-list view (under item views model based) ------------Maya command to access------------> textScrollList
10-horizontal slider (under input widgets) ------------Maya command to access------------> intSlider
11-label (under display widgets group) ----------------Maya command to access------------> NONE
12-progress bar (under display widgets group) ---------Maya command to access------------> progressBar
13-vertical slider (under input widgets) --------------Maya command to access------------> intSlider
14-horizontal line (under input widgets) --------------Maya command to access------------> NONE
15-vertical line (under input widgets) ----------------Maya command to access------------> NONE
16-group box (under containers group) -----------------Maya command to access------------> NONE
17-tab widget (under container group) -----------------Maya command to access------------> tabLayout
18-main window ----------------------------------------Maya command to access------------> window

Au final, ça fait peu de widgets Maya supportés (une dizaine) comparé à ce qu'il propose en standard.

Malgrès le fait que cette intégration à du être couteuse en temps, aux vues des solutions possibles, je ne suis pas sûr que la situation évolue énormément. Il est cependant fort probable qu'Autodesk ajoute les widgets standards de Qt dans Maya mais l'inverse me semble compromis.

Maya 2012 arrivant et ne voyant pas de modifications sur ce point, je soupçonne que loadUI était plus une fonction marketing qui pourrira au fil du temps jusqu'à ce qu'ils s'y remettent à fond, dans 5 ans... :lanceUnePierre:

Si le principe est sympa pour faire des bouttons et des checkBoxs, les limitations et bricolages que ça impose pour le reste des widgets me font prendre un peu de recule vis-à-vis de cette "solution miracle".

Je vais continuer à essayer de tirer le maximum de cette méthode et voir jusqu'ou ça peut aller mais si je me retrouve à ajouter, dans mon script, tout ce qui manque à l'interface, il n'est pas exclu que je retourne sur une approche 100% Maya Commands.

En esperant que ce billet vous aura aidé à y voir plus clair.

Si je suis resté un peu flou par endroit, n'hésitez pas à me le signaler! ;)

A bientôt!

Dorian

:marioCours: