Handling classes for UI and storing lists of selections

So, after a lot of trial and error I have this code that creates a UI that lets a user enter name, which creates a set of widgets in two tabs. One for selecting joint chains and one that will move those joint chains (right now it just prints a combined list of the joint chains).

My issue is that even though the user can create multiple sets of widgets, I can only work with the last set that was created. If you try to edit the first set of selections ( in the My Tab A) or print the first set of selections(in My Tab B), it only applies to the last ones generated in the UI.

import pymel.core as pm
import maya.cmds as cmds
class TabWindow(pm.ui.Window):    
    def __init__(self):
        super(TabWindow, self).__init__()
        self.title = "TabWindow"
        self.tabA = None
        self.tabB = None                      
        self.buildUI()


    def modifySomethingInTabB(self):
        pm.button(label="TestButton", parent=self.tabB)

    def appendJointChain(self, *args):
        print "button pressed"
        self.set_up_Tab()
        self.tool_Tab()
        
        

    def fillTabA(self, parent):
        pm.setParent(parent)
        pm.text(label = 'Root Joint Selection', align ='center')
        pm.separator(h=5)
        pm.button(label = 'Add Selector', c=lambda event: self.name_DialogBox())
    
    def fillTabB(self, parent):    
        pm.setParent(parent)
        pm.text(label = 'FK IK Switch', align ='center')
        pm.separator(h=5)
    
    def Remove(InstanceName):
        pm.deleteUI(self)
          
        
    def buildUI(self):
        tabLayout = None
        children = []
        pm.setUITemplate("DefaultTemplate", pushTemplate=True)
        with pm.columnLayout(adjustableColumn=True,width=300) as cl:
            with pm.tabLayout() as tabLayout:
                for childName in ["My Tab A", "My Tab B"]:
                    with pm.columnLayout(adjustableColumn=True) as cla:
                        children.append((cla, childName))

        self.fillTabA(children[0][0])
        self.fillTabB(children[1][0])
        self.tabA = children[0][0]
        self.tabB = children[1][0]

        pm.tabLayout(tabLayout, edit=True, tabLabel=children)
        pm.setUITemplate("DefaultTemplate", popTemplate=True)      
    
    def name_DialogBox(self):
        result = cmds.promptDialog(
            title='Rename Object',
            message='Enter Name:',
            button=['OK', 'Cancel'],
            defaultButton='OK',
            cancelButton='Cancel',
            dismissString='Cancel')

        if result == 'OK':
            self.InstanceName = cmds.promptDialog(query=True, text=True)
            ###Create New Tab using text as name####
            self.appendJointChain()
            return self.InstanceName
            
    
    
    def set_up_Tab(self):
        
        cmds.frameLayout (width = 300, label = self.InstanceName,  collapse = True, collapsable = True, marginWidth = 5, parent = self.tabA);
        
        self.nametab =cmds.text(label = self.InstanceName, align ='center')
        
        self.tsL0 =pm.textFieldButtonGrp(ed=False, adj=1,cal=(1,"left"),cw3=(10,100,25), cl3=("left","left","left") , 
                                    buttonLabel='Root   FK', bc= lambda : self.select_joints_afk())
        self.tsL1 = pm.textFieldButtonGrp(ed=False, adj=1,cal=(1,"left"),cw3=(10,100,25), cl3=("left","left","left") , 
                                    buttonLabel='Wrist   IK',bc= lambda : self.select_joints_aikw())
        self.tsL2 = pm.textFieldButtonGrp(ed=False, adj=1,cal=(1,"left"),cw3=(10,100,25), cl3=("left","left","left") , 
                                    buttonLabel='Elbow IK',bc= lambda : self.select_joints_ikpv())

        pm.separator(h=5)
        pm.button( label = 'Remove ' + self.InstanceName, command = lambda :Remove(), width=100)
        pm.separator
        
        return self.tsL0, self.tsL1, self.tsL2
    

    
    def tool_Tab(self):
        
        cmds.frameLayout (width = 300, label = self.InstanceName,  collapse = True, collapsable = True, marginWidth = 5, parent = self.tabB);
        cmds.separator(h=5)
        self.row=cmds.rowLayout(numberOfColumns=2,width = 300)
        
        
        self.IKbutton=cmds.button(label='IK', command = '', width=100)
        self.FKbutton=cmds.button(label='FK', command = '', width=100)
        self.parent=cmds.setParent('..')
        self.row2=cmds.rowLayout(numberOfColumns=2,width = 300)
        
        self.Fk2IK_Button = pm.button(label='FK 2 IK', command = lambda _: self.fk_2_ik(), width=100)
        self.Ik2FK_Button = pm.button(label='IK 2 FK', command = lambda _: self.ik_2_fk(), width=100)
        
        self.parent
        
    def select_joints_afk(self):
        if cmds.ls(selection = True,type=("transform",'nurbsCurve')):
            sel = cmds.ls(sl=True)
            fkCtrls = cmds.listRelatives(sel, allDescendents=True, type=("transform",'nurbsCurve'))
            self.Fks = [nurbsCurve for nurbsCurve in fkCtrls if nurbsCurve.startswith('FK') & nurbsCurve.endswith('_Ctrl')]
            cmds.textFieldButtonGrp(self.tsL0, edit = True, tx ='' .join(sel),buttonLabel='IK OK',backgroundColor = (.5,.8,.2))
            self.Fks.extend(sel)
            print self.Fks
            return self.Fks,self.tsL0
        else:
            text = cmds.confirmDialog( title='Error', message='Must select joint', button=['OK'], 
                                        defaultButton='Ok', dismissString='No' )


    def select_joints_aikw(self):
        if cmds.ls(selection = True,type=("transform",'nurbsCurve')):
            
            sel=cmds.ls(selection = True)
            ikwrist = sel
            self.Ikw = [nurbsCurve for nurbsCurve in ikwrist if nurbsCurve.startswith('IK') & nurbsCurve.endswith('Ctrl')]
            cmds.textFieldButtonGrp(self.tsL1, edit = True, tx ='' .join(ikwrist),buttonLabel='IK OK',backgroundColor = (.5,.8,.2))
            cmds.select(ikwrist)
            print self.Ikw
            return self.Ikw
        else:
            text = cmds.confirmDialog( title='Error', message='Must select joint', button=['OK'], defaultButton='Ok', 
                                        dismissString='No' )

    def select_joints_ikpv(self):
        if cmds.ls(selection = True,type=("transform",'nurbsCurve')):
            sel = cmds.ls(sl=True)
            ikPvsel = sel
            self.ikpv = [nurbsCurve for nurbsCurve in ikPvsel if nurbsCurve.endswith('Pv_Ctrl')]
            cmds.textFieldButtonGrp(self.tsL2, edit = True, tx ='' .join(ikPvsel),buttonLabel='IK OK',backgroundColor = (.5,.8,.2))        
            cmds.select(ikPvsel)
            print self.ikpv
            return self.ikpv
        else:
            text = cmds.confirmDialog( title='Error', message='Must select joint', button=['OK'], 
                                        defaultButton='Ok', dismissString='No' )
                                        
    def ik_2_fk(self):

        #import combined selection list
        match_selection = self.Fks + self.Ikw + self.ikpv
        print("List created" + str(match_selection))
 
                                            
                                            
    def fk_2_ik(self):
         #import combined selection list
        match_selection = self.Fks + self.Ikw + self.ikpv
        print("List created" + str(match_selection))

tw = TabWindow()
# if pm.window(tw, exists = True):
#     pm.deleteUI(tw)
tw.show()

I’m really believing the solution lies in creating a class for the tab creation and the selection lists.

To that end I have the below version that I think should work:

import pymel.core as pm
import maya.cmds as cmds
class TabWindow(pm.ui.Window):
    def __init__(self):
        super(TabWindow, self).__init__()
        self.title = "TabWindow"
        self.tabA = None
        self.tabB = None
        self.buildUI()


    def modifySomethingInTabB(self):
        pm.button(label="TestButton", parent=self.tabB)

    def appendJointChain(self, *args):
        print "button pressed"
        self.joint_chain_select = Create_Selection_Chains( self, self.InstanceName , "foo", "bar", "baz")
        Create_Selection_Chains.set_up_Tab(self.joint_chain_select)
        Create_Selection_Chains.tool_Tab(self.joint_chain_select)
        
        

    def fillTabA(self, parent):
        pm.setParent(parent)
        cmds.text(label = 'Root Joint Selection', align ='center')
        cmds.separator(h=5)
        pm.button(label = 'Add Selector', c=lambda event: self.name_DialogBox())
    
    def fillTabB(self, parent):    
        pm.setParent(parent)
        cmds.text(label = 'FK IK Switch', align ='center')
        cmds.separator(h=5)
    
    def Remove(InstanceName):
        cmds.deleteUI(self)
          
    
        
    def buildUI(self):
        tabLayout = None
        children = []
        pm.setUITemplate("DefaultTemplate", pushTemplate=True)
        with pm.columnLayout(adjustableColumn=True,width=300) as cl:
            with pm.tabLayout() as tabLayout:
                for childName in ["My Tab A", "My Tab B"]:
                    with pm.columnLayout(adjustableColumn=True) as cla:
                        children.append((cla, childName))

        self.fillTabA(children[0][0])
        self.fillTabB(children[1][0])
        self.tabA = children[0][0]
        self.tabB = children[1][0]

        pm.tabLayout(tabLayout, edit=True, tabLabel=children)
        pm.setUITemplate("DefaultTemplate", popTemplate=True)      
    
    def name_DialogBox(self):
        result = cmds.promptDialog(
            title='Rename Object',
            message='Enter Name:',
            button=['OK', 'Cancel'],
            defaultButton='OK',
            cancelButton='Cancel',
            dismissString='Cancel')

        if result == 'OK':
            self.InstanceName = cmds.promptDialog(query=True, text=True)
            ###Create New Tab using USER TEXT entered as name####
            self.appendJointChain()
            return self.InstanceName    

# if pm.window(tw, exists = True):
#     pm.deleteUI(tw)

tw = TabWindow()
tw.show()

class Create_Selection_Chains():
    
    def __init__(self, name ,InstanceName, Fks, Ikw, ikpv):
        self.name=name
        self.Fks = Fks
        self.Ikw = Ikw
        self.ikpv = ikpv
        self.InstanceName= InstanceName
        print  Fks + Ikw + ikpv
        

    def set_up_Tab(self):
        
        self.setupTab = cmds.frameLayout (width = 300, label = self.InstanceName,  collapse = True, collapsable = True, marginWidth = 5, parent = buildUI(tabA));
        
        self.nametab =cmds.text(label = self.InstanceName, align ='center')
        
        self.tsL0 =pm.textFieldButtonGrp(ed=False, adj=1,cal=(1,"left"),cw3=(10,100,25), cl3=("left","left","left") , 
                                    buttonLabel='Root   FK', bc= lambda : Create_Selection_Chains.select_joints_afk(self.joint_chain_select))
        self.tsL1 = pm.textFieldButtonGrp(ed=False, adj=1,cal=(1,"left"),cw3=(10,100,25), cl3=("left","left","left") , 
                                    buttonLabel='Wrist   IK',bc= lambda : Create_Selection_Chains.select_joints_aikw(self.joint_chain_select))
        self.tsL2 = pm.textFieldButtonGrp(ed=False, adj=1,cal=(1,"left"),cw3=(10,100,25), cl3=("left","left","left") , 
                                    buttonLabel='Elbow IK',bc= lambda : Create_Selection_Chains.select_joints_ikpv(self.joint_chain_select) )


        pm.separator(h=5)
        pm.button( label = 'Remove ' + self.InstanceName, command = lambda :Remove(), width=100)
        pm.separator
        
        return self.tsL0, self.tsL1, self.tsL2,
    

    
    def tool_Tab(self):
        
        self.toolTab = cmds.frameLayout (width = 300, label = self.InstanceName,  collapse = True, collapsable = True, marginWidth = 5, parent = self.tabB);
        cmds.separator(h=5)
        self.row=cmds.rowLayout(numberOfColumns=2,width = 300)
        ###################################################################################
        #  IK FK Switch                                 #  IK FK Switch
        self.IKbutton=pm.button(label='IK', command = '', width=100)
        self.FKbutton=pm.button(label='FK', command = '', width=100)
        self.parent=cmds.setParent('..')
        self.row2=cmds.rowLayout(numberOfColumns=2,width = 300)
        ###################################################################################
        #  IK FK Snapping                               #  IK FK Snapping
        self.Fk2IK_Button=pm.button(label='FK 2 IK', command = lambda _: Create_Selection_Chains.fk_2_ik(joint_chain_select), width=100)
        self.Ik2FK_Button=pm.button(label='IK 2 FK', command = lambda _: Create_Selection_Chains.ik_2_fk(joint_chain_select), width=100)
        
        self.parent



        
    def select_joints_afk(self):
        if cmds.ls(selection = True,type=("transform",'nurbsCurve')):
            sel = cmds.ls(sl=True)
            fkCtrls = cmds.listRelatives(sel, allDescendents=True, type=("transform",'nurbsCurve'))
            self.Fks = [nurbsCurve for nurbsCurve in fkCtrls if nurbsCurve.startswith('FK') & nurbsCurve.endswith('_Ctrl')]
            cmds.textFieldButtonGrp(TabWindow().tsL0, edit = True, tx ='' .join(sel),buttonLabel='IK OK',backgroundColor = (.5,.8,.2))
            self.Fks.extend(sel)
            print self.Fks
            return self.Fks 
        else:
            text = cmds.confirmDialog( title='Error', message='Must select joint', button=['OK'], 
                                        defaultButton='Ok', dismissString='No' )

    def select_joints_aikw(self):
        if cmds.ls(selection = True,type=("transform",'nurbsCurve')):
            
            sel=cmds.ls(selection = True)
            ikwrist = sel
            self.Ikw = [nurbsCurve for nurbsCurve in ikwrist if nurbsCurve.startswith('IK') & nurbsCurve.endswith('Ctrl')]
            cmds.textFieldButtonGrp(Set_up_Tab().tsL1, edit = True, tx ='' .join(ikwrist),buttonLabel='IK OK',backgroundColor = (.5,.8,.2))
            cmds.select(ikwrist)
            print self.Ikw
            return self.Ikw
        else:
            text = cmds.confirmDialog( title='Error', message='Must select joint', button=['OK'], defaultButton='Ok', 
                                        dismissString='No' )

    def select_joints_ikpv(self):
        if cmds.ls(selection = True,type=("transform",'nurbsCurve')):
            sel = cmds.ls(sl=True)
            ikPvsel = sel
            self.ikpv = [nurbsCurve for nurbsCurve in ikPvsel if nurbsCurve.endswith('Pv_Ctrl')]
            cmds.textFieldButtonGrp(self.tsL2, edit = True, tx ='' .join(ikPvsel),buttonLabel='IK OK',backgroundColor = (.5,.8,.2))        
            cmds.select(ikPvsel)
            print self.ikpv
            return self.ikpv
        else:
            text = cmds.confirmDialog( title='Error', message='Must select joint', button=['OK'], 
                                        defaultButton='Ok', dismissString='No' )
                                                                                
    def ik_2_fk(self):

        #import combined selection list
        match_selection = self.Fks + self.Ikw + self.ikpv
        print("List created" + str(match_selection))
 
                                            
                                            
    def fk_2_ik(self):
         #import combined selection list
        match_selection = self.Fks + self.Ikw + self.ikpv
        print("List created" + str(match_selection)

However, I can’t seem to get this to work. I try to add the setup tab to My UI I recieve the following errors :

self.setupTab = cmds.frameLayout (width = 300, label = self.InstanceName,  collapse = True, collapsable = True, marginWidth = 5, parent = TabWindow.buildUI.tabA);
# Error: AttributeError: file <maya console> line 91: 'function' object has no attribute 'tabA' #
# Error: NameError: file <maya console> line 91: global name 'buildUI' is not defined #

when I try this:

self.setupTab = cmds.frameLayout (width = 300, label = self.InstanceName,  collapse = True, collapsable = True, marginWidth = 5, parent = buildUI.tabA);

I get:

# Error: NameError: file <maya console> line 91: global name 'buildUI' is not defined #

Any assistance with this would be greatly appreciated, as Im not really sure what I’m doing wrong.

I think I finally got it! As far as adding to the UI is concerned I was able to get this working by
simply setting the parents by using the instance of the UI that was created with tw = TabWindow when I parent my tabs it should be parent = tw.tabA and parent = tw.tabB

I really should have read through this more carefully

Edit - you appear to have it, i’m going to post my reply anyhow.

Hi @hanaharu,

    def __init__(self):
        super(TabWindow, self).__init__()
        self.title = "TabWindow"
        self.tabA = None
        self.tabB = None
        self.buildUI()

Ok,

You are trying to query a member variable on the class and not the bound instance. Because you set the property on the self in the constructor, it wont exist until its instance has been bound to a variable in memory.

Here’s a guide on classes that may give you some pointers:

https://www.learnpython.org/en/Classes_and_Objects

Python mantra dictates in general terms to keep your functions small and your classes smaller - IMO classes should exist to store and change state. With user interfaces a general approach is the VMC or view, model controller - in specific terms its really for server/client systems but the general rule is you keep data away from functionality.

In the case of user interfaces, essentially all they should be doing with data is reflecting changes back to the user and doing request via a middle man of some sort. How I approach this is to have an underlying framework that talks to a middle man api, which the front-end (your interface) basically reflects and sends calls to. This way all your functionality that the interface is calling can happen in a headless mode i.e. without a UI and preferably batch-able.

So an api does all the low level switching, then an middle man calls this via the front end returning what the front end needs to reflect.

1 Like

Thank you very much for your help! I’m going mark reply as the answer. Also thank you for the link I’m going to read through it carefully and not try to rush through