Python Sourcing scripts and nesting UIs


#1

Hi.

As an original MEL scripter I have some issues understanding the ways of python in Maya.
Maybe I am expecting it to behave too much like MEL (I know it will never behave like mel. That is the beauty of Python, but I have some issues I am trying to wrap my brain around)

Let me explain the scenario.
I am building a UI which will work as a Main UI. A UI that I want to hook other layouts to. The way I used to do this with mel is like this

  1. build main Window. let’s call this “A_ui.mel”
  2. in a separate script create a UI, typically columnLayout and parent that into the main GUI if it exists. if not, source it and then hook it up. let’s call this script “B_ui.mel”
  3. in my main window script file, A_ui.mel I have a button which checks if the columnLayout exists, if not, source B_ui.mel and then it will hook itself up.

I am used to being able to source a script

source "path/path/script.mel"

and when I do that the whole script is sourced, the hole thing is passed to Memory so I can access anything I like there.

Now with python, if I import B_ui.py it does not seem to behave in that way.
The code below shows what I am trying to do (I don’t have kill UI etc here. It is more to show what I am thinking)

A_ui.py:


import maya.cmds as cmds
import B_ui

def buildUI():
    cmds.window()
    cmds.columnLayout('masterLayout')
    cmds.button(c='B_ui.buildUI()')


B_ui.py:


import maya.cmds as cmds

def B_ui.buildUI():
    cmds.columnLayout('b_uiColumn' , p='masterLayout')
    cmds.button(c='b_function.createCube()')



and the b_function.py would look like this



import maya.cmds as cmds

def createCube():
    print 'creating cube for you, sir!'
    cmds.polyCube()


Now…
If I want to create a shelf button for this and I add


import a_ui
A_ui.buildUI

it builds the UI, but it seems like it ignores “import B_ui” at the start of the script.
The ui is built, but when I click the button to add the B_ui.buildUI() it does not execute.

If this was MEL I would know that if I source a script it would run through the entire thing so I would know that it would run through
all imports at the start of the script, but since I do not know python that well I suspect I am overlooking something and maybe I am ignoring something important. Maybe I can’t think like this at all.

The thought is that I should be able to just add new scripts to the MAIN UI whenever the need for a new script arises. So if we have 3 scripts hooked up to the main UI I should be able to write a script in a separate file and just hook it up to that UI. This way I do not destroy any code in the main UI file. Except that I would add import whateverNewScript at the start.

Can someone give me any pointers as to how you would attack this in python. If what I am trying to execute is unclear I can write some proper testScripts so we could break it down properly till it works. I am just trying to understand if I can even do what I am trying to do in python like this :slight_smile:

thanks,
M


#2

You should read some python tutes to get the hang of the importing and reloading of scripts. An important concept to understand is python objects - functions, modules, packages, classes etc and how you refer to them in a script.

In your specific example in your question when you did this

cmds.button(c=‘b_function.createCube()’)

You passed a string to the button command. It contains the name of a function, but not the function object. When you click the button, the command is not found because the scope of the command is now outside your script so it doesnt know about the import. What you need to do instead is pass in the python object that is the function, rather than just its name. Thats easy. No quotes, no (). Just like this.

cmds.button(c=b_function.createCube)

The next problem is that when you click the button maya sends an argument to the specified function. So your function needs to deal with that. If you dont care about the arg (and often you will not), then just use *args to catch a variable argument list, which you can then just ignore.

def createCube(*args):

Like I said some reading up on these ideas will help out a lot. Sure different to mel, but way more useful.

David


#3

Thanks djx.

Your reply already got me further.
What I have found hard to get is that if you import moduleA in scriptB and then you import scriptB it doesn’t care that you imported moduleA in scriptB.

scriptA.py:

import randomModule
import anotherRandomModule

def doStuff();
    stuff...

scriptB.py

import scriptA

stuff...

When I import scriptA in scriptB it does not seem to import randomModule and anotherRandomModule which scriptA is doing.
This might make sense, but I am struggling a bit with the concept. Looking more into this as we speak.

thanks again :slight_smile:


#4

It does import it. Just not the way you expected. Think of it like namespaces. When you import scriptA into scriptB, I’m guessing that inside scriptB you then tried to access randomModule. Infact randomModule exists in the namespace of scriptA, so you can access it withing scriptB as scriptA.randomModule.

My preference would be to simply import what modules you need into scriptB as well. If you need randomModule in both scripts, import it into both scripts.

You are importing scriptA because you want doStuff() right?

In my code I’d either do…

import scriptA
scriptA.doStuff()

or more likely…

from scriptA import doStuff
doStuff()

and if I anticipated name clashes…

from scriptA import doStuff as doStuffFromA
doStuffFromA()

David


#5

Hi David.

Thanks again for helping me out here.

I found that I can probably use functools partial to execute commands with arguments, but I would like to run this through you first.

I have two scripts now:
createCubeWithName.py which basically just creates a cube:

import maya.cmds as cmds

def makeCube(name, *args):
    cmds.polyCube(name=name , sx=1, sy=2 , sz=3)

Then I have another one (cubeCreator.py) which builds the GUI and calls the makeCube command from createCubeWithName:

#import all you need
import maya.cmds as cmds
import createCubeWithName
from functools import partial

#create a Maya GUI which tries to access a command from another script
def createUI():
    if cmds.window('cubeCreator', exists=True):
        cmds.deleteUI('cubeCreator')
    cmds.window('cubeCreator', t='createCube' , w=252 , h=152 , s=True)
    cmds.columnLayout(adj=True)
    cmds.button(l='createCube', w=250 , h=150 , c=partial(createCubeWithName.makeCube, 'superName'))

    cmds.showWindow('cubeCreator')

For my shelf button, or just to execute this I have these lines:

import createCubeWithName
import cubeCreator

cubeCreator.createUI()

This seems to execute properly.
Would this be the way to go ?

I think some of my problems may occur because I have a main UI file and then I have a ui file pr. script/tool that I write which then again references to the functions
Heriarchy would look a bit like this:

\-main_ui.py
  \-tool_ui.py
     \-tool_funct.py

Maybe it’s just too much and I should just write all the UI in the main UI.,
The thought was to make it easy to add and remove tools froim the main UI by writing a UI attach pr. tool and just hook that up to the main GUI. This way more people could just write tools side by side and it would hook up to the main non destructively.


#6

I’m trying to import a script (like sourcing) but I’m seeing its not finding any of my functions within the script i’m importing…

My script is named biscuits.py … The import code I’m using is…

import biscuits

biscuits.main()

It executes the script becuase I see the UI popup. The problem is none of my internal functions work.

in my script I have

def modifyTextField():

When my script runs that function I get this error.

Error: NameError: file <maya console> line 1: name ‘modifyTextField’ is not defined

How do i source/import the script with it bringing all my definitions/functions with it???

Seth


#7

Your modifyTextField() is defined in the biscuits module as well? So you have to run it the same way at biscuits.main():

biscuits.modifyTextField()