UI - getting button to call module method, which prints UI text field


#1

Hi all,

I have a module called ‘module_test’ shown below:


import maya.cmds as cmds

def method_test(string_test, *args):
    print string_test

The script below imports ‘method_test’ from this module, then creates a UI containing a button and a text field.

I want the button to print out whatever is in the text field.


from functools import partial
import maya.cmds as cmds
from module_test import method_test

# create UI
def create_ui():
    if cmds.window('my_window', exists=True):
        cmds.deleteUI('my_window')
    cmds.window('my_window')
    cmds.columnLayout()
    text_field = cmds.textField('my_text_field')
    button_test = cmds.button(label='test_button', command = partial(method_test, cmds.textField(text_field, query=True, text=True)))
    cmds.showWindow()

create_ui()

I’ve tried to get the button to call the ‘method_test’ method, and pass into it a query of the text field, however nothings gets printed.

Please can anyone tell me how this should be done?

Thanks a lot.


#2

with the partial you are using your are baking in the value of the textField query into the method_test when the UI gets created. For the behaviour you expect you need to query the value every time the button is clicked not just at the time of the button creation.

button_test = cmds.button(label='test_button', command=lambda *args: method_test(cmds.textField(text_field, q=True, text=True)))
   

The lambda is more or less making a 1 liner function that gets called when ever you click the button.


#3

You can go with partial with an handler function, i.e. :

from functools import partial
import maya.cmds as cmds
from module_test import method_test

# create UI

# special handler
def ui_refreshTextField(myFunction, textfieldName, *args):
	text = cmds.textField(textfieldName, query=True, text=True)
	myFunction(text)# could input args here

def create_ui():
	if cmds.window('my_window', exists=True):
		cmds.deleteUI('my_window')
	cmds.window('my_window')
	cmds.columnLayout()
	text_field = cmds.textField('my_text_field')
	button_test = cmds.button(label='test_button', command = partial(ui_refreshTextField, method_test, text_field))
	cmds.showWindow()

#4

Thanks for the replies.

On a similar note, what would be the best technique to use if I wanted the module method to be able to access one or more UI fields?

eg how would ‘method_test’ be aware of the variable name ‘text_field’ in the UI, to be able to query it?

I understand that in this code enables the ‘method_test’ method to be executed every time the button is pressed, but what if I needed the method in the module to access the data from several UI elements?


button_test = cmds.button(label='test_button', command=lambda *args: method_test(cmds.textField(text_field, q=True, text=True)))

Thanks.


#5

After reading a few things online, as a test I’ve managed to structure a UI module this way:

A UI module which contains a UI_Class and this class contains a ‘get_values’ method(which queries the various UI fields)

This way any functions in other modules can create a new ‘UI_Class’ object, and that way these external functions can use the 'get_values method to get access to the fields in the UI.

Is this a good way to do things?

Thanks.


#6

This is where it becomes good to use classes

import maya.cmds as cmds
 
 name = 'PySandbox'
 
 
 class UI(object):
 	def __init__(self):
 		if cmds.window(name, exists=True):
 			cmds.deleteUI(name)
 
 		self.window = cmds.window(name, title=name, mnb=False, mxb=False, sizeable=False)
 		cmds.columnLayout()
 		self.mytestfield = cmds.textField()
 		cmds.button(l='Print', c=self.button_command)
 
 		cmds.showWindow(self.window)
 
 	def button_command(self, *args):
 		print cmds.textField(self.mytestfield, q=True, text=True)
 

 if __name__ == "__main__":
 	ui = UI()
 

you can store anything needed for method that act on the UI in a instance of the UI object


#7

I need to start experimenting a bit more with classes to get my head around them.

I was getting an error running your code though, any idea what the problem is?

UI instance has no attribute 'button_command'

Thanks.


#8

sounds like you were trying to use a method before defining it in the class


#9

The ‘button_command’ method (that the error is about) is defined in this code though isn’t it?

def button_command(self, *args):
 		print cmds.textField(self.mytestfield, q=True, text=True)

The error happens when trying to instantiate the class instance with this:

ui = UI()

#10

Thanks for the help so far.

To try and get my head around how classes and modules work( and how best to pass data from the UI to external module methods), I’ve created a module containing a UI class(ui_class_module_test.py), and a separate module containing a method(module_test.py).

The module method should simply print out the contents of the text field in the UI.

And I want a button in the UI to call this module method.

This is the UI class:

# ui class module 

import maya.cmds as cmds

class UiTest():
    def __init__(self):
        self.UIName = "TestUI"
        
    def create_ui_method(self):
        cmds.window()
        cmds.columnLayout()
        self.button_text = cmds.button(label='test_button', command=lambda *args: module_test.method_test())
        self.field_text = cmds.textField()
        cmds.showWindow()
    
    def get_values(self, *args):
        return cmds.textField(self.field_text, query=True, text=True)
        
import module_test

This is the module method:

# module method test 
import maya.cmds as cmds
import ui_class_module_test

class_instance = ui_class_module_test.UiTest()

def method_test(*args):
    # query the text field in the UI
    text_field = class_instance.get_values()
    print text_field
    

Finally this is the ui class instantiation.

# ui method call
import ui_class_module_test
test_instance = ui_class_module_test.UiTest()
test_instance.create_ui_method()

At the moment, if I try and press the button in the Ui, then I get an error message saying:

Error: AttributeError: file C:/Users/gbdavies/Documents/maya/2013.5-x64/scripts\ui_class_module_test.py line 15: UiTest instance has no attribute ‘field_text’

However if I run the ‘get_values()’ function separately like this, then it does successfully get the text from the text field in the UI:

test_instance.get_values()

Anyone know how to get this working?

Any help appreciated thanks.


#11

you got a few issues, you were importing module_test too late in your UI class all imports should be at the top.

  In the button command you were including self in method test, you don't need to do so since self is implicit.
  
  Than in your module_test it will never actually work or return the proper text since you are creating a new instance of the UI class which means even if it worked it would give you the wrong data. But also you are creating the new instance with out calling your create_ui_method so the self.field_text attribute your get_values method uses would not have been created yet.
  
  Think of classes as a blueprint or a template, the class is defining how a object should be made. So each time you make a instance of a class you are a creating a new object that contains its own data and has no relationship with other objects created from the same class.
 
 Edit: you are really making it more complicated than it needs to be, and the responsibility for how data gets accessed should be reversed for this case. If the ui is calling a method of a other module it should send the data that method needs as augments of it.
import maya.cmds as cmds
   import module_test
   reload(module_test)
   
   
   class UiTest(object):
   	def __init__(self):
   		self.UIName = "TEstUI"
   		cmds.window()
   		cmds.columnLayout()
   		cmds.button(l="test_button", command=self.button_command)
   		self.field_text = cmds.textField()
   		cmds.showWindow()
   
   	def button_command(self, *args):
   		data = cmds.textField(self.field_text, q=True, text=True)
   		module_test.method_test(data)
   
def method_test(data):
 	print data
 
import ui_class_module_test
    reload(ui_class_module_test)
    ui = ui_class_module_test.UiTest()
     

also notice i was able to skip the lambda by creating a method that takes the same arguments that a command is executed with and use it directly instead of a lambda or partial.

also if you wanted to be able to access all the data on the object in the module method instead of passing in just the text of the textField like i did a option could be to pass in the whole UI object using self. But in generally you are best off to only pass the data you need


#12

Great, thanks for going through those issues.

It’s good to know the right way of doing things…yes I did have it completely the wrong way around :banghead:

I thought each external module would have to import the UI to have access to the UI data, now I see that the module methods get imported into the same scope as the UI and then data can be passed to them.