Getting, Storing, Setting and Modifying Transform Attributes through PyMel - python

I'm working on something that gets and stores the transforms of an object moved by the user and then allows the user to click a button to return to the values set by the user.
So far, I have figured out how to get the attribute, and set it. However, I can only get and set once. Is there a way to do this multiple times within the script running once? Or do I have to keep rerunning the script? This is a vital question for me get crystal clear.
basically:
btn1 = button(label="Get x Shape", parent = layout, command ='GetPressed()')
btn2 = button(label="Set x Shape", parent = layout, command ='SetPressed()')
def GetPressed():
print gx #to see value
gx = PyNode( 'object').tx.get() #to get the attr
def SetPressed():
PyNode('object').tx.set(gx) #set the attr???
I'm not 100% on how to do this correctly, or if I'm going the right way?
Thanks

You aren't passing the variable gx so SetPressed() will fail if you run it as written(it might work sporadically if you tried executing the gx = ... line directly in the listener before running the whole thing -- but it's going to be erratic). You'll need to provide a value in your SetPressed() function so the set operation has something to work with.
As an aside, using string names to invoke your button functions isn't a good way to go -- you code will work when executed from the listener but will not work if bundled into a function: when you use a string name for the functions Maya will only find them if they live the the global namespace -- that's where your listener commands go but it's hard to reach from other functions.
Here's a minimal example of how to do this by keeping all of the functions and variables inside another function:
import maya.cmds as cmds
import pymel.core as pm
def example_window():
# make the UI
with pm.window(title = 'example') as w:
with pm.rowLayout(nc =3 ) as cs:
field = pm.floatFieldGrp(label = 'value', nf=3)
get_button = pm.button('get')
set_button = pm.button('set')
# define these after the UI is made, so they inherit the names
# of the UI elements
def get_command(_):
sel = pm.ls(sl=True)
if not sel:
cmds.warning("nothing selected")
return
value = sel[0].t.get() + [0]
pm.floatFieldGrp(field, e=True, v1= value[0], v2 = value[1], v3 = value[2])
def set_command(_):
sel = pm.ls(sl=True)
if not sel:
cmds.warning("nothing selected")
return
value = pm.floatFieldGrp(field, q=True, v=True)
sel[0].t.set(value[:3])
# edit the existing UI to attech the commands. They'll remember the UI pieces they
# are connected to
pm.button(get_button, e=True, command = get_command)
pm.button(set_button, e=True, command = set_command)
w.show()
#open the window
example_window()
In general, it's this kind of thing that is the trickiest bit in doing Maya GUI -- you need to make sure that all the functions and handlers etc see each other and can share information. In this example the function shares the info by defining the handlers after the UI exists, so they can inherit the names of the UI pieces and know what to work on. There are other ways to do this (Classes are the most sophisticated and complex) but this is the minimalist way to do it. There's a deeper dive on how to do this here

Related

passing data to tkinter dnd (dragndrop) vs event binding problem

tkinterDnD 2.8 - I managed to get it to detect a drop onto a tkinter Entry widget and event.data does give up the dropped text, but I have an array of entry widgets. I used Page gui builder to get the boilerplate code. Page generated code for a LabelFrame that contains the Entry and other widgets and I just looped the code to get a bunch of them.
My question is how to go about passing the widget list index to the tkinterDnD drop handler. I get all kinds of weird errors or just no joy when I try using lambda function tricks and maybe the only answer is to use the good old event binds for this. You'll see what I mean below (second code box) when I show my output.
Also note that a couple code comments have questions about things that didn't work and maybe some implied comments so I'd appreciate comments on those, too.
class Toplevel1:
def __init__(self, top=None):
self.section_Labelframe1s = []
self.section_Entry1s = []
#self.sz_text = [] # ugly hack attempt - no joy
for i in range(0, 8):
self.section_Labelframe1s.append(None)
self.section_Labelframe1s[i] = tk.LabelFrame(top)
# blah set attributes for LabelFrame[i]
self.section_Entry1s.append(None)
self.section_Labelframe1s[i] = tk.LabelFrame(top)
# self.sz_text.append # and what ever i did with it - perhaps setting it to my index i so I could do an
# add_trace which python3 doesn't seem to recognize
# blah
# Entry widgets don't have a command attribute (oh joy, so I gave it a name)
self.section_Entry1s[i] = tk.Entry(self.section_Labelframe1s[i], name='dnd_demo_entry_' + str(i))
# naming it didn't help or maybe it caused the prob to which I hinted at at the end
# which I can't duplicate now, sigh.
# previous attempts:
# self.section_Entry1s[i].configure(command=lambda i=i: dnd_demo_support.drop(i))
# RESULT: no attribute "command" in tk.Entry -- BUGGERS!
# self.section_Entry1s[i].configure(textvariable = self.sz_text[i])
# IndexError: list index out of range.
# Index error? What? WHY?
# more widgets
# and I get a nice window with 8 of the above and I want to do something when I drag some text into the Entry widget, so:
self.section_Entry1s[i].drop_target_register(DND_TEXT)
self.section_Entry1s[i].dnd_bind('<<Drop>>', lambda i=i : dnd_demo_support.drop(i))
So at this point, dropping text onto the Entry widget works. I handle the drop event in the dnd_demo_support.py file that Page generated where I also call a function print_event_info() which does:
print('Widget:', event.widget, '(type: %s)' % type(event.widget))
# which prints
Widget: .!labelframe.dnd_demo_entry_0 (type: <class 'tkinter.Entry'>)
All that would be good except rather than looking for the "0" at the end of the above, I'd like to just pass the index of the widget and now it gets nearly hair-pulling ugly for me.
The drop handler is like:
def drop(event):
global w, top_level, root # w is key. it's from Page and it comes from the whole w = tk.Toplevel (root) shebang (no. not "#!" lol)
if event.data:
print('Dropped data:\n', event.data)
print('event.widget: ', event.widget)
if event.widget == w.section_Entry1s[0]: # i'm still forcing a 0 index to the Event widget while degubbing this mess
I can't find my other problem, but something I tried caused the script to print a line for every i in the for loop. Dang I wish I could reproduce that. For now, I'd like to find a way to get my i index in my drop(event) callback without having to pluck it from event.widget which as I said previously, gives me:
.!labelframe.dnd_demo_entry_0
and once I unforce the index, I end up with
self.section_Entry1s[i].dnd_bind('<<Drop>>', lambda i=i : dnd_demo_support.drop(i))
# in the GUI creation (?? no sleep) __init__ part
# and the drop handler - something like:
def drop(event, i):
#blah
TypeError: drop() missing 1 required positional argument: 'i'
ARRGGGHHHHH!!!! see what i mean 8) I messed with that positional arg thing forfreakin' too long. No fix using lambda self, i : func nor lambda event, i, : func because obviously, event isn't defined in the first statement. Maybe using self.something is the ticket and maybe that's how I got the aforementioned effect that I couldn't reproduce which gave me 8 print outputs. I'll hve to try that after no sleep/work/etc
Thanks again for your time and any help ^3
Not sure why this didn't work the first time I tried it:
Change to:
lambda i=i: dnd_demo_support.drop(i) => lambda event, i=i: dnd_demo_support.drop(event, i)

PyQT - list QWidgets/Windows

I have a
class Main(QtGui.QMainWindow):
That is able to click>spawn a x number of windows that are:
class dataWindow(QtGui.QWidget)
Is there a way in PyQt to now find all spawned dataWindow's and get their objectName?
each window has unique objectName.
I tried going via :
a= self.findChild(QWidget, self.newDataWids[0]["window_name"]) - as I have all names stored in dict upon creation
but it only returns None. I think its because the dataWindow are not parented to Main window class I believe... so I either have to parent them - not sure how. Or somehow find them out in the "wild"...
Any ideas would be great.
Regards, Dariusz
Edit_1: A glitch in my code bugged out my current attempt. After relooking I managed to get it to work. I simply stored the window in temporary dictionary and then used that to retrieve access to window.
You parent objects by passing in the parent to their constructor. You'll have to check the documentation for each widget to get the correct argument position.
widget = QtGui.QWidget(self)
btn = QtGui.QPushButton('Button Text', self)
But really, you shouldn't have to do a search for children to get the child windows. Your main window should be keeping handles to them.
def __init__(...)
...
self._windows = []
def createSubWindow(self):
window = WindowClass(self)
self._windows.append(window)

Is there an option to recieve the data from a UI window to a variable?

I saw that it is possible to print the data that has been written in the user interface window, however, when I searched in the internet, none of the options were like that, to retrieve the data into a variable.
This is a very simple window code -
'
def printTxtField ( fieldID ):
print cmds.textField( fieldID, query=True, text=True)
winID = 'kevsUI'
if cmds.window(winID, exists=True):
cmds.deleteUI(winID)
cmds.window(winID)
cmds.columnLayout()
whatUSay = cmds.textField()
cmds.button(label='Confirm', command='printTxtField(whatUSay)')
cmds.showWindow()
'
I want to retrieve the data from the text field into a variable, once the confirm button is pressed.
Up in the cmds.button line, you can see in the command - 'print TxtField'.
I know that if there is an option to print what was written in the text field, so there must be an option to put it in a variable instead. However, I didn't find it.Does anybody knows how to do it?
Sorry for the prev. question.
You have to use partial module to pass variable through button command (or lambda function)
from functools import partial
Here is the same question : maya python + Pass variable on button press
From Python doc:
print evaluates each expression in turn and writes the resulting
object to standard output. If an object is not a string,
it is first converted to a string using the rules for string
conversions. The (resulting or original) string is then written.
To sum up in a few words:
The print statement expects a expression after writing it. This can be a string, int, object...
In your case:
What you are printing in your printTxtField funtion is the return value of a function (cmds.textField( fieldID, query=True, text=True)).
When writing this :
cmds.textField( fieldID, query=True, text=True) you are telling Maya to:
Find the textField called fieldID's value
Do a query on it (the query flag is set to True
Query it's text (what's written in it)
Returns this value
To conclude:
Instead of printing a returned value, you can easily assigned this value to a variable: myVar = cmds.textField( fieldID, query=True, text=True)
Your modified code:
def printTxtField ( fieldID ):
#here is the modification made
myVar = cmds.textField( fieldID, query=True, text=True)
I've commented and reorganized your code to have something more clean:
import maya.cmds as cmds
KEVS_TEXTFIELD_VALUE = None
####################################################
# This function retrieve the value of Kevs_TextField
# and set this value to KEVS_TEXTFIELD_VALUE
####################################################
def retrieveData():
# We use the query flag on the text attribute
KEVS_TEXTFIELD_VALUE = cmds.textField("Kevs_TextField", query=True, text=True)
print u"KEVS_TEXTFIELD_VALUE =" + KEVS_TEXTFIELD_VALUE
####################################################
# This function will create a show a new UI
####################################################
def drawUI():
winID = 'kevsUI'
if cmds.window(winID, exists=True): #If the window exists
cmds.deleteUI(winID) #Delete it
cmds.window(winID) #Then create a new one
cmds.columnLayout("Kevs_ColLayout") #Create a columnLayout to sort our widgets
# Now let's add some widgets in our columnLayout
cmds.textField("Kevs_TextField") #Add a textfied in the columnLayout
#Then create a button that calls the retrieveData function when pressed
cmds.button("Kevs_Button", label="Confirm", command=retrieveData)
cmds.showWindow() #Show the window
drawUI() # Let's create a new UI
It's possible, but there are a couple of issues in the way you're going about it. #DrHaze's example shows the right thing to do, which is to use the actual python functions instead of strings.
You will also need to think about the visibility of different functions: it's easy to get things working in the listener where all the code is in one place, but once there are multiple functions or modules involved keeping track of gui widget names becomes harder.
For small tools you can define the callback function -- the one that gets used by the button, in this case -- right in the same place that the gui widgets are created. This will use python's rules for closures to keep track of the widgets for you:
def window_example(title):
if cmds.window(title, exists=True):
cmds.deleteUI(title)
win = cmds.window(title)
cmds.columnLayout()
whatUSay = cmds.textField()
#defining this hear gives it access to 'whatUSay'
def print_text_contents(ignore):
print cmds.textField( whatUSay, query=True, text=True)
cmds.button(label='Confirm', command=print_text_contents)
cmds.showWindow(win)
window_example('kevsUI')
for longer tools you probably want to learn about doing this with classes.
Here's much more background info with some different strategies to check out.

Importing tkinter button from separate module

We have a functioning program that uses Tkinter as its GUI. Everything works fine however different branches of the code are now using different hardware which realistically need different buttons. Hence we'd like to have the main GUI import modules representing the buttons depending on what hardware is being used.
I've cut out some of the code below, I'm interested in removing the makemenu() function to a separate module, hence when it is called in the Application __init__ (self.makemenu(master)) I would like to make that a reference to a separate module. I've tried doing this and am having trouble. Is this even possible?
I'm a little confused on the parent structure, what needs to be passed to my button module, etc.? I know this is a poorly constructed question but if anyone is able to advise if this is possible and put my on the right track that would be great. For example if someone could show how to modify this code to have the buttons defined in a separate module I could figure out how to do the same in my module.
# Import necessary libraries
import sys
import os
import Tkinter as tk
class Application(tk.Frame):
##################################################################
## Final functions are designed to initialize the GUI and
## connect various mouse movements to useful functions.
##################################################################
def definevars(self):
'''Original definition of all of the key variables that
we need to keep track of while running the GUI
'''
self.disable = True
self.savimgstatus = 'off'
self.mode = 'Standby'
self.status = 'Not Ready'
def makemenu(self,master):
''' Function to create the main menu bar across
the top of the GUI.
'''
self.menubar = tk.Menu(master)
## Motor Submenu
motormenu = tk.Menu(self.menubar,tearoff=1)
motormenu.add_command(label='ALT',state='disabled')
motormenu.add_command(label='error check',
command=lambda: self.geterror('alt'))
motormenu.add_separator()
motormenu.add_command(label='AZ',state='disabled')
motormenu.add_command(label='error check',
command=lambda: self.geterror('az'))
self.menubar.add_cascade(label='Tracker Motors',menu=motormenu)
## Set the big menu as the main menu bar.
master.config(menu=self.menubar)
def __init__(self,tcpconn,DOME,TRACKERSTAGE, master=None):
'''Main function to initialize the GUI. Will scale
the size of the GUI to fit any size screen... to a
point. It will not allow it to be smaller than
600x800.
'''
self.buf = 1024
## Check resolution of screen. Make GUI 2/3rds of size
## unless that means under 600x800.
fh = round(master.winfo_screenheight()*2./3.)
fw = round(master.winfo_screenwidth()*2./3.)
if fh < 600: fh = 600
if fw < 800: fw = 800
print 'GUI resolution set to {0} x {1}'.format(fw,fh)
self.fw = fw
self.fh = fh
self.imwidth = int(0.45*self.fw)
self.imheight = int(0.45*self.fh)
self.imcentx = self.imwidth/2
self.imcenty = self.imheight/2this
## Initialize Frame
tk.Frame.__init__(self, master, height=fh,width=fw)
self.grid()
self.grid_propagate(0)
## Initialize Various variables.
self.definevars()
## Create buttons, etc.
self.createWidgets()
self.makemenu(master)
self.disableall()
## Main Loop function
self.checkoutput()
###################################################################
# Initialize GUI window.
root = tk.Tk()
root.title('Hardware') # window title
app = Application(master=root)
app.mainloop() # go into the main program loop
sys.exit()
If you want to move makemenu to a separate module, that should be pretty simple. However, you'll need to change a few things.
Since makemenu no longer has a reference to self (or has a different reference, if you implement it as a separate class), you need to replace calls like command=lambda: self.geterror('alt')) to be command=lambda: master.geterror('alt')).
The other thing I recommend is to remove the call to add the menu to the root. I believe that modules shouldn't have side effects like this -- the function should make a menu and return it, and let the caller decide how to use it, ie:
self.menubar=makemenu(master)
master.configure(menu=self.menubar)
Roughly speaking, this is a variation of the MVC (model/view/controller) architectural pattern where the Application instance is your controller (and also part of the view unless you make modules of all your UI code). The menu is part of the view, and forwards UI functions to the controller for execution.
Your application then looks something like this:
from makemenu import makemenu
class Application(...):
def __init__(...):
...
self.menubar = makemenu(master)
master.config(menu=self.menubar)
...

Python in Maya - Query checkbox value

Im super new to python and i have this little spare time project going on.
And i cant find a solution to the following problem:
I set up a GUI like this:
flWin = mc.window(title="Foot Locker", wh=(210,85))
mc.columnLayout()
mc.text(label='Frame Range')
rangeField = mc.intFieldGrp(numberOfFields=2,value1=0, value2=0)
mc.rowColumnLayout(numberOfRows=2)
translateBox = mc.checkBox(label='Translation',value=True)
mc.button(label="Bake it!", w=60, command="Bake()")
rotateBox = mc.checkBox(label='Rotation',value=True)
mc.button(label='Key it!', w=60, command='Key()')
scaleBox = mc.checkBox(label='Scale')
mc.showWindow(flWin)
and then later, inside the function 'Bake'
id like to query the checkboxes to do different stuff, depending on what boxes are checked... like this:
translateValue = mc.checkBox(translateBox, query=True)
rotateValue = mc.checkBox(rotateBox, query=True)
scaleValue = mc.checkBox(scaleBox, query=True)
if scaleValue = True:
if rotateValue = True:
if translateValue = True:
mc.parentConstraint ('LockCator', Selection, n='LockCatorConstraint')
mc.scaleConstraint('LockCator', Selection, n='selectionScale')
else:
mc.parentConstraint ('LockCator', Selection, n='LockCatorConstraint', skipTranslate=True)
mc.scaleConstraint('LockCator', Selection, n='selectionScale')
bla bla bla... you get the trick...
when i try to run the script, i get a error saying that there is invalid syntax on the line if scaleValue = True:
i also tried using this:
mc.attributeQuery(translateBox,value=True)
but that gives me an error, saying that 'value' is an invalid flag... i dont know what that means.
Some help here would be greatly appreciated!!
Thanks guys!
You were close, the query flag simply tells the command you want to get the data, rather than set, whatever you're queering, has to also appear in the same command, you're just missing the v=True flag for the fields.
translateValue = mc.checkBox(translateBox, query=True, value=True)
rotateValue = mc.checkBox(rotateBox, query=True, value=True)
scaleValue = mc.checkBox(scaleBox, query=True, value=True)
Also, where you're chaining your if commands, seeing as your value can only be true or false, you can simply write if (scaleValue): which is the same as writing if scaleValue == True:
if (scaleValue):
if (rotateValue):
if (translateValue):
mc.parentConstraint ('LockCator', Selection, n='LockCatorConstraint')
mc.scaleConstraint('LockCator', Selection, n='selectionScale')
else:
mc.parentConstraint ('LockCator', Selection, n='LockCatorConstraint', skipTranslate=True)
mc.scaleConstraint('LockCator', Selection, n='selectionScale')
Better yet, seeing as you're doing basically the same thing for these chains, we can simplify this:
skipTrans = True if scaleValue and rotateValue and translateValue else False
mc.parentConstraint ('LockCator', Selection, n='LockCatorConstraint', skipTranslate=skipTrans)
mc.scaleConstraint('LockCator', Selection, n='selectionScale')
The above is exactly the same as the code above this code.
Hope this helps, as #jonathon has also provided, the way you've written your UI can get very messy and hard to read, definitely read into QT Designer, it's a brilliant program.
If I understand your question correctly all you need to do is include both query and value flags, e.g:
import maya.cmds as mc
flWin = mc.window(title="Foot Locker", wh=(210,85))
mc.columnLayout()
mc.text(label='Frame Range')
rangeField = mc.intFieldGrp(numberOfFields=2,value1=0, value2=0)
mc.rowColumnLayout(numberOfRows=2)
translateBox = mc.checkBox(label='Translation',value=True)
mc.button(label="Bake it!", w=60, command="Bake()")
rotateBox = mc.checkBox(label='Rotation',value=True)
mc.button(label='Key it!', w=60, command='Key()')
scaleBox = mc.checkBox(label='Scale')
mc.showWindow(flWin)
print mc.checkBox(scaleBox, q=True, v=True)
returns True
when querying a UI element you need to put the command in query mode and then also supply the value to query, in this case the value. So you had all the elements there just not at the same time!
This behaviour is weird I know but when you understand how MEL and its equivalent command works it makes more sense.
Also if I remember correctly you can now use PySide (a python Qt Library) inside Maya python which sounds like a much better idea for programatically creating a ui. If you are after a simpler way to create maya ui's you can also use Qt Designer to build a .ui file that maya can load at runtime.
For example to create a window from a ui file:
# first delete window if it already exists
if (cmds.window('window_name', exists=True)):
cmds.deleteUI('window_name')
window = cmds.loadUI('my_window.ui'))
cmds.showWindow(window)
To query the ui just make sure to give the ui elements unique names inside Qt Designer then query them like you have been doing so far.
For more info on using Qt Designer with maya see this great page:
http://www.creativecrash.com/maya/tutorials/scripting/mel/c/using-qt-designer-for-mel-interfaces

Categories