Jump to content
KrzyM

Maya UI - Python - "Dynamiczne" ładowanie klasy.

Recommended Posts

Hej,

Dopiero zaczynam uczyć się pythona i utknąłem w punkcie.

 

Mam plik toolsUI.py który przeszukuje folder Modules i dla każdego tworzy przycisk - problem jest że chciałbym żeby każdy przycisk wywoływał odpowiednie okno.

Udało mi się już zaimportować każdy moduł do skryptu. np. ToolboxUI.py, jeśli w przycisku wywołam UI = mod.ToolboxUI() to bez problemu otworzy mi się Toolbox. Jednak nie mogę znaleźć sposobu jak automatycznie wywoływać wszystkie skrypty

np. coś w stylu

plugin = "ToolboxUI()"
UI = mod.plugin

 

Mam nadzieje że ktoś jest w stanie mi pomóc ;)

Pozdro,

Krzysiek

 

Poniżej kod:

 

kTools.py

import maya.cmds as cmds
import os
import System.utils as utils
reload(utils)
import sys
from functools import partial

class Ktools_UI:
   def __init__(self):
       #uiElements dict
       self.UIElements = {}
       if cmds.window("Ktools_UI_Window", exists = True):
           cmds.deleteUI("Ktools_UI_Window")    

       windowWidth = 200
       #Main Window
       self.UIElements["window"] = cmds.window(
       "Ktools_UI_Window", menuBar=False, title="KTools", mnb=False, mxb=False
       )

       self.UIElements["columnLayout"] = cmds.columnLayout (adjustableColumn = True, columnAlign="center")

       self.createPluginsButtons()

       cmds.showWindow(self.UIElements["window"])
       cmds.window(self.UIElements["window"], edit=True, w = windowWidth, sizeable=True)

   def createPluginsButtons(self):
       windowHeight = 0
        #Plugin folder path
        systemPath = "System"
        pluginPath = os.environ["RIGGING_TOOL_ROOT"] + "/modules/"
        plugins = os.listdir(pluginPath)
        plugin_list = []            
        #find modules
        for plugin in plugins:
            if ((plugin != "__init__.py") and (plugin != systemPath)):
                plugin_list.append(plugin)            
        #create buttons
        for plugin_name in plugin_list:
           plugin_UI = plugin_name + "UI"

       #check for UI file
           for module in utils.findAllModules("Modules/"+ plugin_name):
               if (module == plugin_UI):
                   #print "UI file found: " + module

                   self.UIElements[(plugin_name + "_button")] = cmds.button(
                   label = plugin_name,
                   h = 64,
                   parent = self.UIElements["columnLayout"],
                   bgc=utils.randomBGC(),
                   c = partial(self.runPlugin, plugin_name)
                   )
                   windowHeight = windowHeight + 64
       cmds.window(self.UIElements["window"], edit=True, h = windowHeight, sizeable=True)

   def runPlugin(self, plugin, *args):
       plugin_UI = plugin + "UI"

       mod = __import__(plugin+"."+plugin_UI, {},{},[plugin])
       #btw. class_name is the same as plugin_UI
       #just testing
       class_name = mod.CLASS_NAME

       plugin_UI = plugin + "UI"

       mod = __import__(plugin+"."+plugin_UI, {},{},[plugin])
       reload(mod)
       class_name = mod.CLASS_NAME

       #this works
       UI = mod.ToolboxUI()

       #e.g.  pluginUI == ToolboxUI()
       #how to run 
       #UI = mod.pluginUI()    

toolboxUI.py

import maya.cmds as cmds
import os
import maya.mel as mel
import toolbox_utils as tU
reload(tU)
from functools import partial
import System.utils as utils
reload(utils)

CLASS_NAME="ToolboxUI"
class ToolboxUI:
   def __init__(self):
       self.UIElements = {}
       windowHeight = 450
       windowWidth = 212
       if cmds.window("Toolbox", exists = True):
           cmds.deleteUI("Toolbox")
       self.UIElements["window"] = cmds.window("Toolbox", mnb=False, mxb=False)
       self.UIElements["scrollLayout"] = cmds.scrollLayout(hst=0)
       self.UIElements["mainLayout"] = cmds.columnLayout(adj = True, parent=self.UIElements["scrollLayout"])

       cmds.showWindow(self.UIElements["window"])
       cmds.window(self.UIElements["window"], edit = True, w=windowWidth, h=windowHeight, sizeable=False)
       windowHeight3 = self.populateIcons()

Share this post


Link to post
Share on other sites

Chetnie bym Ci pomogl, ale totalnie nie rozumiem Twojego pytania i problemu ;/

 

"problem jest że chciałbym żeby każdy przycisk wywoływał odpowiednie okno. "

Dlaczego to jest problem?

Share this post


Link to post
Share on other sites

Mam problem żeby to zrobić.

Załóżmy że mamy moduły: Toolbox, PoseSaver, AutoRig - każdy z nich znajduje się w folderze o tej samej nazwie np. Modules/Toolbox

w każdym z tych folderów niezależnie jaki to będzie skrypt, przyjąłem że będzie plik główny nazwaModułu_UI.py i klasą o nazwie modułu

 

teraz skrypt KTools.py tworzy poprostu przycisk dla każdego z tych modułów i wywołuje funkcje runPlugin

 

udało mi się "dynamicznie" zaimportować pluginy w pętli

#np dla import Toolbox/Toolbox as mod; reload(mod)

mod = __import__(plugin+"."+plugin_UI, {},{},[plugin])
	reload(mod)

- i tutaj zaczyna się problem.

 

żeby np. odpalić Toolbox.py muszę napisać

mod.Toolbox()

i analogicznie do innych pluginów.

nazwę pluginu mam w zmiennej, ale szukam sposobu jak sprawić żeby zadziałało

mod.Plugin_UI()

 

Coś jak shelf - przycisk uruchamiający dany skrypt.

Ale przy założeniu że mamy N skryptów.

 

 

Próbowałem coś z exec, partial ale bez skutku.

 

Sory że tak bez składnie to piszę, ale wynika to raczej z mojej niewiedzy o Pythonie - zacząłem się bawić kilka dni temu i jeszcze nie ogarniam wielu elementów.

Share this post


Link to post
Share on other sites

Hm, mozesz przeleciec cale mod np. tak:

 

for i in dir(mod):
   print i

 

 

ten przypadek oczywiscie wypisze nazwy, ale moze wystarczy zrobic z tego eval:

 

 

for i in dir(mod):
eval("mod.{0}()".format(i))

Share this post


Link to post
Share on other sites

Nie wiem czy rozumiem co chcesz zrobic (bo to co robisz jest dziwne), ale chodzi o cos takiego?

 

mod = __import__("os")

getenv_function = getattr(mod, "getenv")

getenv_function("MAYA_PLUG_IN_PATH", "NotSet")

Share this post


Link to post
Share on other sites

Reanimator, dzięki - działa super. Muszę przestudiować funkcje wbudowane w Pythona ;)

Podrzucisz mi jakieś hasło pod którym znajdę jakieś wytłumaczenie dla tych nawiasów: mod.{0}() Szczerze to nie do końca rozumiem.

 

Praetorian - Reanimator rozwiązał problem. Nie bardzo rozumiem twój kod, ale domyślam się że chodzi o ścieżkę?

Ja stworzyłem sobie zmienną w env i przez shelfa główny skrypt wywołuje tak:

import os

try:
   riggingToolRoot = os.environ["RIGGING_TOOL_ROOT"]
except:
   print "RIGGING_TOOL_ROOT environment variable not correctly configured"
else:
   import sys
   print "Running: " + riggingToolRoot
   path = riggingToolRoot + "/Modules"

   if not path in sys.path:
       sys.path.append(path)

   import System.ktoolsUI as kUI
   reload(kUI)

   UI = kUI.Ktools_UI()

 

może to jakoś uzupełni moje poprzednio 2 wklejony kody.

Share this post


Link to post
Share on other sites

poszukaj string.format :)

 

uzywasz numerow w klamrach, a pozniej podmieniasz je wartosciami

 

zmienna = "cos innego"
polaczone = "Lorem {0}, albo {1}".format("Ipsum", zmienna)

 

print polaczone da: "Lorem Ipsum, albo cos innego"

 

 

W Twoim przypadku zebralem nazwy funkcji z modolow (stringi) i pozniej uzylem ich zeby wywolac kazda z nich za pomoca eval w loopie.

Share this post


Link to post
Share on other sites

Praetorian - Reanimator rozwiązał problem. Nie bardzo rozumiem twój kod, ale domyślam się że chodzi o ścieżkę?

 

Nie o ścieżkę. To samo inaczej (może jaśniej):

 

ty napisałeś:

plugin = "ToolboxUI()"

UI = mod.plugin

 

ja piszę:

plugin = "ToolboxUI()"

UI = getattr(mod, plugin)

Share this post


Link to post
Share on other sites

a nie lepiej jeszcze tak?

 

plugins.py

class PluginBase(object):
   def generate_ui(self):
       # ui creation
       pass

class RiggingTool(PluginBase):
   def generate_ui(self):
       # override this here

 

__init__.py


import plugins
from plugins import PluginBase

def plugin_factory(plugin_name):
   result = getattr(plugins, plugin_name)
   if issubclass(result, PluginBase):
       return result

plugin = plugin_factory("RiggingTool")

 

PS. Polecam Ci jednak robić takie rzeczy w PySide a nie w standardowym majkowym UI

Share this post


Link to post
Share on other sites

Dziękuje wam ;) PySide mam na liście ;)

Moje rozwiązanie z góry było skazane na porażkę bo to pierwszy skrypt i tylko forma nauki.

 

Znacie jakieś dobre źródła wiedzy? Obecnie przeglądam tylko manual i czytam "Maya Python for games and film"

Share this post


Link to post
Share on other sites

Ja uczylem sie z "Python 3 - kompletne wprowadzenie do programowania" z Heliona. Bardzo dokladne i szczegolowe sie wydaje.

Share this post


Link to post
Share on other sites
PS. Polecam Ci jednak robić takie rzeczy w PySide a nie w standardowym majkowym UI

 

Ogólnie robić całe UI w PySide? Nie używać Maykowego?

A co z PyQT? Jeszcze nie bardzo widzę różnicę między PySide a PyQt.

Share this post


Link to post
Share on other sites
Znacie jakieś dobre źródła wiedzy? Obecnie przeglądam tylko manual i czytam "Maya Python for games and film"

 

"Python od podstaw" - fajna książka i jeszcze o pythonie 2.x (Python 3 nie jest na razie nigdzie implementowany oprócz Blendera chyba).

 

Ogólnie robić całe UI w PySide? Nie używać Maykowego?

 

Tak.

 

PySide to open source PyQt. Ma kilka ograniczeń ale jest wpychane wszędzie ponieważ nie trzeba za niego płacić.

Oba wrappery tak naprawdę robią to samo - wywołują kod z bibliotek Qt.

Share this post


Link to post
Share on other sites

Python 3 nie jest zaimplementowany, ale osobiście nie miałem żadnych problemów (w majce i motionbuilderze) ucząc sie z tej ksiazki. Najwyraźniej różnice nie sa aż tak duże.

 

Anyway: powodzenia!:)

Share this post


Link to post
Share on other sites

Hej,

odgrzebuje jeszcze ten temat - zmieniłem UI na pySide i nie mogę ogarnąć tego samego ;)

 

W skrócie sytuacja: Mam skrypt "_shelf", który tworzy przyciski dla wszystkich skryptów które spełniają kryteria (odpowiednie nazwy itp).

Kliknięcie przycisku przekazuje Nazwę pluginu do funkcji _pluginFactory i ten importuje UI.

http://pastebin.com/UpEupcEj

 

W sumie to mi obojętne jak uruchomić te skrypty, byle poprawnie.

Zakładam wstępnie że tak chciałbym żeby wyglądał każdy główny plik danego plugina.

http://pastebin.com/eXThzDnY

 

I teraz jeśli uruchamiam ten skrypt z majkowego shelfa to robię to tak:

http://pastebin.com/4hH5RYnX

 

Dlatego próbuje coś wykąbinować na podstawie wpisu kroopsona, i stworzyłem plik pluginFactory do którego ręcznie będę dodawał classy dla każdego plugina? (No chyba że nie potrzebuje, bo zakładam że uruchamiać je będę tylko poprzez _shelf)

http://pastebin.com/u7gjvjJA

 

Próbowałem poprzez getattr, exec ale dostaję wtedy jedynie wynik w stylu:


natomiast jeśli uruchomię skrypt tak jak przez majkowy shelf to otrzymuję bardzo podobny wynik

 
Nie znalazłem jeszcze na to rozwiązania, niby wywołuję tą funkcję z powodzeniem ale nie udaje mi się wywołać UI.
Napotykam się np na:  [code]TypeError: descriptor 'show' of 'PySide.QtGui.QWidget' object needs an argument

Share this post


Link to post
Share on other sites

O ile dobrze rozumiem twoj problem to potrzebujesz dwoch funkcji.

1) zaimportuj moduly do slownika.

2) ze slownika stworz panel z guzikami

 

Polomorfizm zastosuj na poziomie modulu i zasadniczo to tyle. Niech kazdy modul ma funkcje rejestrujaca, przekaz do niej uchwyt/obiekt glownego okna i v'oila. Nie potrzeba tutaj jakichs cudow na kiju. To nie java, ludzie :-)

Share this post


Link to post
Share on other sites

Ja już wczytałem wszystkie moduły i zrobiłem z nich przycisk. Każdy przycisk wywołuje funkcje _pluginFactory i przekazuje jej nazwe modułu.

Chciałbym żeby ta funkcja wywołała mi okno pySide z moim modułem czyli modul_UI.py

 

http://pastebin.com/UpEupcEj

Na dole zakomentowane jak ręcznie zrobie import modulu, i pozniej modul.show() to dziala, ale jak juz zrobię coś jak:

__import__(folder+"."+plugin, {},{},[plugin])

To a następnie eval, exec lub getattr to średnio rusza.

 

Nadal szukam jakiś info na ten temat, jeszcze nie do końca rozumiem różnicy między tym jak udało się wywołać te moduły (strona 1), a teraz po przejściu na pySide.

 

Może problem leży w samym uruchomianiu pySide, ale to poruszyłem w tym temacie: http://max3d.pl/forum/threads/98755-PySide-pytania

I szukam info dot. rozwiązania Kroopsona.

 

v32l9h.png

Share this post


Link to post
Share on other sites

Kiedy wywolujesz import z tymi klamrami, usuwasz caly kontekst dla importowanego modulu.

Sprobuj tak: __import__(folder+"."+plugin).

Napisz prosze co zwraca ta funkcja po imporcie.

Dlaczego nie dziala automatyczny import ? Jakie sa objawy ? I ne kombinuj z getattr itp.. na zaimportowanym module wywowal funkcje polimorficzna i to powinno dzialac. Mozesz.ewemtualnie sprawdzic czy modul ma te funkcje. Pousuwaj wszystkie "__" to niepotrzebne.

Share this post


Link to post
Share on other sites

Wydaje mi się że własnie import jest w miarę ok. Teoretycznie jestem wstanie wywołać skrypt z tego importu jak ręcznie podam import.nazwa() przy UI. (komentarze w kodzie)

Jak wywołać tą funkcję? Czytałem o tym już i rozumiem koncept, ale w praktyce?

 

Sory za nie ogarniecie ;)

#plugin factory
def _pluginFactory(self, plugin, *args):
	self.UIPlugins = {}
	folder = "__"+plugin.strip('_UI')

	##Co mysle ze musze napisac dla plugin = toolBox_UI
	## i to otwiera okno poprawnie
	# import __toolBox.toolBox_UI as tb
	# reload(tb)
	# self.UIPlugins["toolBox_UI"] = tb.toolBox_UI()
	# self.UIPlugins["toolBox_UI"].show()	
	##tb = 

	##import
	importPlugin = __import__(folder+"."+plugin)
	print "import metoda 1:" + str(importPlugin)

	importPlugin2 = __import__(folder+"."+plugin, {},{},[plugin])
	print "import metoda 2:" + str(importPlugin2)	
	##import metoda 1:
	##import metoda 2:
	##teoretycznie metoda 2 zdaje rezultat

	result = getattr(pFactory, plugin)
	##get attr result: 


	##To czego mi brakuje to wstawienia zmiennej plugin zamiast toolBox_UI()	
	# self.UIPlugins[plugin] = importPlugin2.toolBox_UI()
	# self.UIPlugins[plugin].show()
	##^ teraz tez dziala

	self.UIPlugins[plugin] = importPlugin.toolBox_UI()
	self.UIPlugins[plugin].show()
	## TypeError: 'module' object is not callable


	# # # if __name__ == "__main__":
		# # # try:
			# # # self.UIPlugins[plugin+"_UI"].close()
		# # # except:
			# # # pass

Share this post


Link to post
Share on other sites

Czytałem to i próbowałem różnych opcji jak importowałem to na samym początku, możesz mi wytłumaczyć w czym tkwi różnica i czemu lepiej importować __init__.py i jak wtedy dobrać się do tego pliku? Mam w init.py dodać funkcję która to uruchomi?

 

konkrednie dla zmiennej plugin = "toolBox_UI" wygląda to tak i uruchamia się:

	import __toolBox.toolBox_UI as tb
	reload(tb)
	self.UIPlugins["toolBox_UI"] = tb.toolBox_UI()
	self.UIPlugins["toolBox_UI"].show()

I import daje mi wynik:


 

Dlatego ja importowałem to tak:

importPlugin2 = __import__(folder+"."+plugin, {},{},[plugin])

i dawało to taki sam wynik:


Dlatego też próbuję wywołać okno przez:

Tylko nie wiem jak to wywołać żeby podać za XXXXXX np. plugin15_UI()

	self.UIPlugins[plugin] = importPlugin2.XXXXXX
	self.UIPlugins[plugin].show()

 

 

Natomiast jak importuje w sposób który podałeś

importPlugin = __import__(folder+"."+plugin)

dostaje wynik:


Share this post


Link to post
Share on other sites

W imporcie bez klamr pomijasz [plugin] i dlatego funkcja zwraca module ale nie "plugin".

Czyli wszystko juz dziala jak nalezy ?

Share this post


Link to post
Share on other sites

Tak, dzięki - miałem jakieś zaćmienie z tym znowu ;)

 

Użyłem import jak radziłeś, eval i poszło ;)

 

Jak możesz, zajrzyj proszę do drugiego mojego tematu, tam też mam kilka pytań ;)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

We are using cookies. Read about our Privacy Policy