Python in Maya - Query checkbox value - python

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

Related

Getting, Storing, Setting and Modifying Transform Attributes through PyMel

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

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.

How to make forcus highlight for 2 objects at the same time

I want both view below are blue, how to set it? please help me! when i forcus to the second line i want it highlight both of object are blue, not one blue and one grey as below.
Code like this:
ui = twin_gtk_builder('twin.ui', ['dia_support', 'liststore7'])
win = ui.get_object('dia_support')
##### Begin function tree view
liststore = gtk.ListStore(int, int, int)
liststore.append([1,2,3])
liststore.append([2,2,2])
liststore.append([4,4,4])
win.sw = gtk.ScrolledWindow()
win.sm = gtk.TreeModelSort(liststore)
##### Set sort column
n = 1
win.sm.set_sort_column_id(n, gtk.SORT_ASCENDING)
win.tv = gtk.TreeView(win.sm)
win.vbox.pack_start(win.sw)
win.sw.add(win.tv)
win.tv.column = [None] * 3
win.tv.column[0] = gtk.TreeViewColumn('0-1000')
win.tv.column[1] = gtk.TreeViewColumn('0-1000000')
win.tv.column[2] = gtk.TreeViewColumn('-10000-10000')
win.tv.cell = [None] * 3
for i in range(3):
win.tv.cell[i] = gtk.CellRendererText()
win.tv.append_column(win.tv.column[i])
win.tv.column[i].set_sort_column_id(i)
win.tv.column[i].pack_start(win.tv.cell[i], True)
win.tv.column[i].set_attributes(win.tv.cell[i], text=i)
##### End function tree view
win.show_all()
and how it work
Tried one more time with #PM 2Ring help, Thanks so much for your help!
Somebody did it like this, but i can't find his contact...
I had to do a bit of work to get that code to run, Sunshine jp. In future, please try to post code that others can run & test, especially if it's GUI code. Otherwise it can be very hard to work out what the problem is and how to fix it.
I'm not familiar with twin_gtk1_builder(). Is it a GTK1 function?
Anyway, I've modified your code to run on GTK2+. I'm not quite sure what you want your code to do. So I've given row 2 a background color of cyan. Also, I've added the ability to make multiple selections, either using Ctrl or Shift on the keyboard when you select with the mouse; you can also do multiple selection with the keyboard with shift up and down arrows.
When the window loses focus the selected row(s) stays blue on my system. Maybe that's a feature of GTK2 that GTK1 doesn't have. (Or maybe it's due to my window manager - I'm using KDE 4.5.3 on Mepis Linux).
#!/usr/bin/env python
'''
TreeView test
From http://stackoverflow.com/questions/25840091/how-to-make-forcus-highlight-for-2-objects-at-the-same-time
'''
import pygtk
#pygtk.require('2.0')
import gtk
def TreeViewTest():
def delete_event(widget, event, data=None):
gtk.main_quit()
return False
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
win.set_title("TreeView Test")
win.set_size_request(320, 160)
win.connect("delete_event", delete_event)
win.vbox = gtk.VBox()
win.add(win.vbox)
win.sw = gtk.ScrolledWindow()
win.vbox.pack_start(win.sw)
##### Begin function tree view
#Set up liststore data. Column 3 controls whether
# background color of the TreeView is default or special.
liststore = gtk.ListStore(int, int, int, bool)
liststore.append([1, 2, 3, False])
#Tell row 2 to use the special color
liststore.append([2, 2, 2, True])
liststore.append([4, 4, 4, False])
win.sm = gtk.TreeModelSort(liststore)
##### Set initial sort column
n = 1
win.sm.set_sort_column_id(n, gtk.SORT_ASCENDING)
win.tv = gtk.TreeView(win.sm)
win.sw.add(win.tv)
win.tv.column = [None] * 3
win.tv.column[0] = gtk.TreeViewColumn('0-1000')
win.tv.column[1] = gtk.TreeViewColumn('0-1000000')
win.tv.column[2] = gtk.TreeViewColumn('-10000-10000')
#Set up cell renderers
win.tv.cell = [None] * 3
for i in range(3):
win.tv.cell[i] = gtk.CellRendererText()
win.tv.cell[i].set_property('cell-background', 'cyan')
win.tv.append_column(win.tv.column[i])
win.tv.column[i].set_sort_column_id(i)
win.tv.column[i].pack_start(win.tv.cell[i], True)
#win.tv.column[i].set_attributes(win.tv.cell[i], text=i)
win.tv.column[i].set_attributes(win.tv.cell[i], text=i,
cell_background_set=3)
#Allow multiple selection
treeselection = win.tv.get_selection()
treeselection.set_mode(gtk.SELECTION_MULTIPLE)
##### End function tree view
win.show_all()
def main():
TreeViewTest()
gtk.main()
if __name__ == "__main__":
main()
Note that this is NOT a good way to make a GUI. You should be creating a proper class, not adding everything as an attribute to win. Please see the PyGTK 2.0 Tutorial for plenty of code examples.
Edit
Ok. Sorry about my earlier confusion over what your problem is. At least we've now got a nice simple example of a PyGTK program that creates a TreeView. :)
Anyway, it turns out that I was right when I guessed that the blue color of the selection turning to grey when the window loses focus on your computer is due to the behaviour of the window manager. I suppose there may be a way to block that in the application, by playing with Widget attributes, but I'm not sure how to do that. And besides, it's considered rude for programs to ignore the settings in the users' window theme.
So the most appropriate solution to your problem is to make the appropriate change in your window manager's appearance settings.
In KDE the relevant property is called "Inactive selection changes color", as described in Color Scheme Options:
Inactive selection changes color — If checked, the current selection in elements which do not have input focus will be drawn using a different color. This can assist visual identification of the element with input focus in some applications, especially those which simultaneously display several lists.
To change this, open up system settings (ALT+F2 → "systemsettings", or the [K] menu → system settings), then go to "Application appearance" and select "Colors". In the "Options" tab, uncheck the "Inactive selection changes color" setting, and click apply.
... ... ...
If you're not using KDE you'll have to figure out for yourself how to change it; hopefully, other window manager settings interfaces and documentation refer to this property with the same name or a similar name.

How do you modify the current selection length in a Tkinter Text widget?

I would like to be able to double click on test,
in a Tkinter Text widget, and have it select test (and exclude the comma).
Here is what I've tried:
import Tkinter as tk
def selection_mod(event=None):
result = aText.selection_get().find(',')
if result > 0:
try:
aText.tag_add("sel", "sel.first", "sel.last-1c")
except tk.TclError:
pass
lord = tk.Tk()
aText = tk.Text(lord, font=("Georgia", "12"))
aText.grid()
aText.bind("<Double-Button-1>", selection_mod)
lord.mainloop()
The first issue is that <Double-Button-1> seems to trigger the handler before the selection is made, producing:
TclError: PRIMARY selection doesn't exist or form "STRING" not defined
The second issue is that even when using a binding that works,
my selection tag doesn't seem to do anything.
It doesn't even raise an error, and I've tried without the except tk.TclError:.
Your binding is happening before the default bindings occur. Thus, the selection doesn't yet exist when your binding fires. Because your binding tries to get the selection, it fails with the error that you see.
You will need to arrange for your binding to happen after the class bindings. A cheap hack is to use after to execute your code once the default bindings have a chance to work. Or, you can use the bindtag feature to make sure your binding fires after the default bindings.
The second problem is that you don't clear the old selection before setting the new. You'll want to do tag_remove to first remove the existing selection. Otherwise, the comma (if it was somehow selected) will remain selected since all you're doing is re-applying the tag to text that already has the tag.
However, double-click doesn't normally capture the comma so I don't quite understand then point of your code. At least, when I test it on OSX it doesn't include the comma.
Here is what I came up with thanks to Bryan's answer:
import Tkinter as tki # tkinter in Python 3
def selection_mod(event=None):
result = txt.selection_get().find(',')
if result > 0:
fir, sec = txt.tag_ranges("sel")
txt.tag_remove("sel", "sel.first", "sel.last")
txt.tag_add("sel", fir, str(sec)+"-1c")
root = tki.Tk()
txt = tki.Text(root, font=("Georgia", "12"))
txt.grid()
txt.bind("<Double-Button-1>", lambda x: root.after(20, selection_mod))
root.mainloop()
It's worth noting that I'm using Windows 7, and according to Bryan,
OSX doesn't include the comma when you double click a word.

PyQt - How to turn on/off spellcheck highlighting

I have a button which sets/unsets spellcheck highlighting in a QTextEdit box (ref PyQt - How to turn on/off spellchecking) which works fine.
Then I added a language selection QComboBox and tied its signal to the button's property but its highlighting set/unset doesn't work on changing the language. It drives me nuts, there may be something small and stupid I've done, but for the sake of it I can't find anything wrong with it.
The button (action rather) is
self.actionSpellCheck = QAction(QIcon(self.icon_spellcheck),
"Auto &Spellcheck", self,
shortcut=Qt.CTRL + Qt.SHIFT + Qt.Key_O,
triggered=self.spellcheck, checkable=True)
The combobox is
self.cb_lang = QComboBox(tb)
tb.addWidget(self.cb_lang)
lang_list = self.dict_broker.list_languages()
self.cb_lang.addItems(lang_list)
self.cb_lang.currentIndexChanged.connect(self.spellcheck)
and the self.spellcheck is
def spellcheck(self):
pos = self.cursor.position()
if self.actionSpellCheck.isChecked():
lang = self.cb_lang.currentText()
self.dict = self.dict_broker.request_dict(lang)
self.highlighter.setDict(self.dict)
self.setHighlighterEnabled(True)
self.show_status("Spellcheck language is set to " + self.dict.tag, None)
else:
self.setHighlighterEnabled(False)
self.highlighter.setDict(None)
self.show_status("Spellcheck is turned off", None)
self.cursor.setPosition(pos, QTextCursor.MoveAnchor)
self.textEdit.setTextCursor(self.cursor)
self.textEdit.setFocus()
How come the highlighter gets set/unset on clicking the button, but nothing happens on selecting the language (it only happens after I start typing, not immediately on combobox selection)? Thank you.
If you look at the HighLighter.setDict method, you'lll see that it doesn't do much other than reassign the dict attribute.
Also, the SpellTextEdit.setHighlighterEnabled only resets the document.
So you're going to need a method to re-highlight the text whenever the dict changes. Fortunately, HighLighter is a subclass of QSyntaxHighlighter, which already has a rehighlight slot which does what is required.
So you just need to amend your spellcheck method as follows:
def spellcheck(self):
pos = self.cursor.position()
if self.actionSpellCheck.isChecked():
self.setHighlighterEnabled(True)
lang = self.cb_lang.currentText()
self.dict = self.dict_broker.request_dict(lang)
self.highlighter.setDict(self.dict)
self.highlighter.rehighlight()
else:
...

Categories