Need help on creating a simple material library interface


#1

Hi everyone,

i’m actually creating a material library for myself in python in Maya. I’m trying to recreate the same interface as you can find in softwares like “Quixel Bridge”. Of course I don’t want to simply recreate perfectly the same interface, I want to expand my knowledge in programming by doing a intuitive and good-looking user interface.
This is my interface for now :

So I have two principal question :

The first one is that I created a “Shader” class and I want it to be on a separated .py file. But every time I try to import my class I have this error : “ImportError: No module named name of my py file”. I tried lot of things like changing the sys path, adding a empty “init.py” file in the same folder but nothing happens I always have this error. So as you can see below my shader class is located in my main script and I want to change that.

The second one is that I want that by default, when the user open the script, all the shaders are visible, but when the user press the “METAL” button, the ui layout will show only METAL shaders. If the user press the “BRICK” button, the ui layout will show only BRICK shaders. If the user press the “ALL” button, the ui layout will show all the shaders, etc… And I don’t really know how to do that. I tried to do a function who delete the shader layout and recreate a new. For now when I click on a materials button, the right shader appears but the old ones don’t dissapear. Can you help me on that please ?

To finish, sorry for my bad english, i’m sure that I made several mistakes. Moreover I’m a student trying to learn the python programming inside Maya, so please if you see some errors in my script, feel free to point them to me, thank you !

Here is my script :

[i]import maya.cmds as cmds
from functools import partial
import maya.cmds as mc
import maya.mel as mel
import os
import os.path

#CLASS_SHADER
class shader(object):
def init(self, id, name, category, img):
self.id = id
self.name = name
self.category = category
self.img = img

def print_me(self):
    print('I am the {0} shader named {1} from the {2} category'.format(self.id, self.name, self.category))
    
def getCategory(self):
    return self.category
    
def getimg(self):
    return self.img

def show(category, shader_ui, *args):
cmds.setParent( shader_ui)
imageM_Chrome = mc.internalVar(upd = True)+“scripts/HShaders_script/preview/Chrome_preview2.png”
SHD_Chrome = shader(id = 1, name=‘Chrome’, category=‘METAL’, img = imageM_Chrome)

imageM_Brick = mc.internalVar(upd = True)+"scripts/HShaders_script/preview/Chrome_preview4.png"
SHD_Brick = shader(id = 2, name='Brick', category='BRICK', img = imageM_Brick)

if category == 'ALL' or category == 'METAL':
    mc.symbolButton( image= SHD_Chrome.getimg())
if category == 'ALL' or category == 'BRICK':
    mc.symbolButton( image= SHD_Brick.getimg())

def createCustomWorkspaceControlConsole():
#UI_______________________________________
mc.columnLayout(adj = True, w=1540)

#_________________SUPP ERROR MESSAGE
mc.warning()
print '',

uiLayout = mc.rowLayout (  numberOfColumns=2, columnWidth=[ (1,1260), (2,270) ], columnAlign=[ (1, 'center'), (2, 'center') ] )

#SHADERS_UI__________________________________________________________________________________________________________________________________________________________

shader_ui = mc.rowColumnLayout (  numberOfColumns=5, columnWidth=[ (1,250), (2,250), (3,250), (4,250), (5,250) ], parent = uiLayout)
show('ALL', shader_ui) #Show All
#BROWSER_UI__________________________________________________________________________________________________________________________________________________________
browser_ui = cmds.columnLayout( columnAttach=('both', 5), bgc= [0.15, 0.15, 0.15], rowSpacing=10, columnWidth=270, parent=uiLayout)

cmds.text( label ="CATEGORIES :", align='left', fn="boldLabelFont")
cmds.text( label =" ", align='left')
mc.button(l= "ALL", command = partial(show, 'ALL', shader_ui))

mc.button(l= "METAL", command = partial(show, 'METAL', shader_ui))
mc.button(l= "GROUND")
mc.button(l= "BRICK", command = partial(show, 'BRICK', shader_ui))
cmds.text( label =" ", align='left')

mc.workspaceControl("HShaders 1.0 ", retain=False, floating=True, li= True, uiScript=“createCustomWorkspaceControlConsole()”);[/i]


#2

I have done something similar a few days ago. A shaderlibrary where a user can select a shader, select “Export to Shaderlib” in a popup menu and the shader will be exported as .ma file and in background a swatch image will be created. Everything is saved in a simple file hierarchy. So metals are in the folder metals, bark in the folder bark. The user has his favorites where he can add any shader.

So the very first thing I tried was to do it with mayas UI methods. And I desperately failed. I suppose I never really understood the maya UI functions. So I restarted with Qt and pyside. And… it is so great and a lot of fun. It is really object oriented and works as expected.

My first advice: Try Qt and pyside.
Second one: Before you try the workspaceControl, try to build normal windows. This way you have a better control and know exactly what’s happening. Later, if everthing works, you can go back to the workspaceControl workflow.
Third one: Try to encapsulate the UI in classes (and use the usual camelcase and first uppercase letter). This way you can keep all data and references to UI elements in one place. e.g. in my case I had a class for the icons, one for the containing grid layout to be able to show pupup menus and another one for the enclosing window.


#3

Ok so I tried Pyside and Qt and you’re right it’s more usefull and easy to use. But now I encounter another problem :

[i]# RuntimeError: ‘init’ method of object’s base class (shader) not called. //

[/i]This happens when I try to create a simple shader and add it to a layout :

[i]MChrome = shader(id = 1, name=‘Chrome’, category=‘METAL’, img = (QtGui.QIcon(cmds.internalVar(upd=True)+“icons\logo.png”)))
self.GridLayoutLibrary.addWidget(MChrome) #Error Line

[/i]Here is my shader class, can you help me please ? I also tried the “super” function but the problem still here.

[i]from PySide2 import QtCore
from PySide2 import QtWidgets
import maya.cmds as cmds
import sys
import os
import os.path
import re
#CLASS_SHADER
class shader(QtWidgets.QPushButton):
def init(self, id, name, category, img):
super(shader,self).init()
self.id = id
self.name = name
self.category = category
self.img = img

def print_me(self):
    print('I am the {0} shader named {1} from the {2} category'.format(self.id, self.name, self.category))
    
def getCategory(self):
    return self.category
    
def getImg(self):
    return self.img[/i]

#4

I suppose the reason is the missing parent keyword. QtWidgets are usually called with:


class MyWidget(QtWidget):
    def __init__(parent=None):
        super(MyWidget, self).__init__(parent)
...