Créer une interface rapidement pour Maya 2011 via loadUI
Par Narann le mercredi, 23 mars 2011, 00:44 - Script et code - Lien permanent
Aujourd'hui je vous propose un petit tuto sur une nouvelle méthode, depuis dans Maya 2011, pour faire des interfaces avec Qt Designer (via la commande loadUI).
Je vous montrerai le principe, et on attaquera avec un exemple concret. Nous verrons donc les avantages et inconvénients de cette technique.
Vous allez voir que la vérité autour de cette nouvelle feature n'est pas aussi jolie qu'elle le laisse croire.
J'espère que vous apprécierez! :sourit:
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.
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: ).
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.
Et ça va même plus loin! :injures:
Démonstration:
Notez l'utilisation d'un QStakedWidget (les deux petites flèches noires en haut à droite)
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. #
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?
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".
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".
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:
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:
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:
# 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:
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
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.
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
Commentaires
Tiens, je viens de voir qu'on peut utiliser la commande :
'cmds.control(blabla)'
pour controler les widgets 'incontrôlables'.
Bon, il y a pas 50 paramètres mais je m'en sert pour enable/disable des groupeBox, etc etc
Héhé!
Genre cmds.control( myGroupBox, visible=True )
Woaaaah! ^^
Ce qui serait intéressant ça serait de pouvoir récupérer les valeurs d'un contrôleur (genre spinBox...).
Mais nan... -_-
Bah c'est clair que y'a que dalle, mais étant donné que les widgets dans un groupe "hérite" du enable, quand j'ai vu ça, quel bonheur de pouvoir disable juste le groupe, et ne pas disable tout les enfants dessous (vu qu'il le sont automatiquement par le groupe parent).
Je suis sur qu'en fouillant un peu il y a moyen de faire un truc...
Intéressant...
Un truc pour quoi? :D
Salut après avoir vu ton site pour m'aider a créer mon interface j'ai
un petit soucis je n'arrive pas a lier mes checkbox a mon code mel. Après je sais qu'il est surement plus intéressant de faire du python (je compte m'y mettre)
J'arrive a uploader mon interface sur maya mais impossible de cliquer sur mes checkbox
Voila mon code Mel:
proc on_func()
{
// DeclarationVar
}
proc off_func()
{
// DeclarationVar
}
//LoadUI$MonUI= `loadUI -uiFile "C:/Users/Julien/PROG/QT/AlignToolMaya/AlignTool.ui"`;
showWindow $MonUI;
donc dans QtDesigner je met -OnCommand en propriété et je met
"on_func" dans le texte.
Merci pour l'aide.
bonne journée
Il aurait été intéressant de savoir ce que le script editor revoit quand tu clique sur ta checkbox.
Ça fait des années que je n’ai plus fais de MEL donc je vais peut être dire des bêtises.
Je pense que tes fonctions "on_func" et "off_func" ne sont pas globales et sont donc "oubliées" par Maya une fois le script exécuter.
Essais de changer tes:
proc on_func
par:
global proc on_func
Je pense que ça devrait marcher.
MovX est le nom de ma checkbox sur qtdesigner
Et en ajoutant "global" devant tes "proc" ça fonctionne?
désole ça n'avait pas pris tout mon message, voila ce que j'avais deja comme erreur avant et le fait de mettre global ne change pas. Je devrais peut être passer sur du python ?
// Error: checkBox -e -OnCommand "on_func"; "AlignTool|MovX"; //
// Error: Line 1.50: Syntax error //
Tente en mettant "on_func()" dans QtDesigner et non "on_func".
idem, je vais essayer de convertir mon fichier en python pour arriver à mieux suivre ta procédure. De tte façon il va bien falloir que je me lance. Merci tout de même. Je repasse par ici dès que j'ai une version en python.
je viens de refaire dans qtdesigner
en propriete dynamique j'ai
-OnCommand (avec a coté) "on_func()"
voila le message d'erreur que j'ai lorsque je load mon ui sur maya:
// Error: line 1: Invalid flag '-OnCommand' //
D'après la doc c'est "onCommand" et non "OnCommand".
Bon alors plus aucun message d'erreur (merci) mais les boutons de mon interface ne sont pas utilisables. comme inactifs.
Bizarre. Fait un:
print "hello world";
Dans ta fonction pour voir si elle affiche quelque chose quand tu clique sur le bouton. Si elle affiche quelque chose c'est que ta fonction est bien appelé.
Bonjour, ça ne change pas, mes boutons ne sont pas utilisables, c'est vraiment étrange j'ai regardé aussi sur ce site:
http://www.cvman.net/civiwiki/index...
Je respecte pourtant bien la manipulation. Je vais tester avec une toute petite interface pour voir si ça change quelque chose.
Je viens de tester avec une interface à un bouton et une checkbox, je n'ai pas de problème. Il me sort bien mes fonctions et je peux cliquer sur mon interface.
Je vais surement reprendre mon code et regarder petit à petit.
Bon je viens de trouver le soucis, des que je met des groupbox sur qt designer mes boutons deviennent inutilisables. Je dois faire une manip spéciale par rapport a ces groupbox ?
Dsl du délai. :)
C'est très bizarre que les groupbox rendent les boutons inutilisables. En principe ça ne devrait rien changer.
Ça fait très longtemps que je n'ai pas fais d'UI comme ça. Maintenant j'utilise PyQt (ou PySide) dans Maya. C'est plus simple et ça évite les bricolages. :D
Salut désolé du temps de réponse aussi. En fait PyQt c'est le python de maya en gros ?
Non pas du tout. ^^'
Lis bien le début de l'article. Tout est expliqué justement. :)