Since hours I try to solve a problem I have with Maya / MEL / Python.
I have a script to set values of a fluid container.
E.g. setAttr "fluidShape1.densityDissipation" 0.2
Works great...
My problem: Actually it is not possible to change the value using the interface (see image). Is there a way to find out if the "text box" is enabled?
Thanks!!
P.S. I cant upload the image :(. But I hope you guys know what i mean
To find out if the attribute is settable, use
getAttr -settable your_object.your_attribute
it will return 1 if you can set the attribute using setAttr and 0 if you can't.
If the value is grayed out in the UI the attribute is locked, you can unlock it with
setAttr -lock 0 your_object.your_attribute
If the value is purple in the UI it's driven by a connection of some kind, you'll need to use the hypergraph or the listConnections command to find out what's driving it and decide if you want to override the connection.
I already tried the -settable flag, but for some reason this is not working in my case.
Ok, lets say I create a FluidContainer. The density is set to zero, by using this command:
setAttr "fluidShape1.densityMethod" 0;
By using the -settable flag
getAttr -settable "fluidShapeq.densityScale"
the result is 1. But I can not change the corresponding slider.
But still it is possible to change the values by using setAttr... That is confusing for me!
ok I think I found a "solution" for my problem, but I think I can do better.
I use the following command to get the "grougID" of the slider and the field:
import maya.cmds as cmds
txt = "attrFieldSliderGrp214"
cmds.attrFieldSliderGrp( txt, q=True, l=True ) # Density Scale
And now I can usethe enable field by:
gray = cmds.attrFieldSliderGrp(txt, q=True, en=True ) # True/False
This works fine for me.
def gui():
cmds.window()
cmds.columnLayout( adjustableColumn=True )
#since density support only upto 3 setting max to 3
getCurrentInd = int(cmds.getAttr("fluidShape1.densityMethod"))
cmds.intSlider( min=0, max=3, value=getCurrentInd, step=1, cc=updateDens)
cmds.showWindow()
def updateDens(value):
cmds.setAttr("fluidShape1.densityMethod", value)
gui()
By Interface if I understand that you mean the AttributeEditor, you could try this.
import pymel.core as pc
def findAEFieldByLabel(label='Dissipation', field_type=pc.ui.AttrFieldSliderGrp):
for i in pc.ui.PyUI('MainAttributeEditorLayout').walkChildren():
if isinstance(i, pc.ui.RowLayout):
name = i.name()
try:
grp = field_type(name)
if grp.getLabel() == label:
return grp
except:
pass
print findAEFieldByLabel().getEnable()
I used pymel here because it helps you find the type of a python ui. Well ... not quite however! because it recognized attrFieldSliderGrps as RowLayouts
Related
I think I missing something very basic but I cannot make this function work.
I am running python 3.6.12 on Redhat with npyscreen 4.10.5. My expectation is that the safe_to_exit function could be used to validate any input immediately when the user exits the field (using the tab key) but I cannot make it fire at all. Possibly I have misunderstood this completely. Here is my test code, cut down to make it as simple as possible:
import npyscreen
def test1(*args):
F = npyscreen.Form(name='My Test Application')
Field1 = F.add(npyscreen.TitleText, name="My Test Field")
Field1.safe_to_exit = Field1_Validations
F.edit()
return Field1.value
def Field1_Validations():
npyscreen.notify_confirm("Safe to exit")
return True
if __name__ == '__main__':
onefield = npyscreen.wrapper_basic(test1)
print("Results are : {} ".format(onefield))
Any pointers would be greatly appreciated.
I'm still digging, but I have discovered that it appears safe_to_exit is not implemented on all widgets. Changing the widget from "TitleText" to "Textfield" resulted in the validations code firing.
After much hair pulling I worked it out. They syntax is (applying to the code sample above):
self.Field1.entry_widget.safe_to_exit = Field1_Validations
I am using PyObjC bindings to try to get a spoken sound file from phonemes.
I figured out that I can turn speech into sound as follows:
import AppKit
ss = AppKit.NSSpeechSynthesizer.alloc().init()
ss.setVoice_('com.apple.speech.synthesis.voice.Alex')
ss.startSpeakingString_toURL_("Hello", AppKit.NSURL.fileURLWithPath_("hello.aiff"))
# then wait until ve.isSpeaking() returns False
Next for greater control I'd like to turn the text first into phonemes, and then speak them.
phonemes = ss.phonemesFromText_("Hello")
But now I'm stuck, because I know from the docs that to get startSpeakingString to accept phonemes as input, you first need to set NSSpeechSynthesizer.SpeechPropertyKey.Mode to "phoneme". And I think I'm supposed to use setObject_forProperty_error_ to set that.
There are two things I don't understand:
Where is NSSpeechSynthesizer.SpeechPropertyKey.Mode in PyObjC? I grepped the entire PyObjC directory and SpeechPropertyKey is not mentioned anywhere.
How do I use setObject_forProperty_error_ to set it? I think based on the docs that the first argument is the value to set (although it's called just "an object", so True in this case?), and the second is the key (would be phoneme in this case?), and finally there is an error callback. But I'm not sure how I'd pass those arguments in Python.
Where is NSSpeechSynthesizer.SpeechPropertyKey.Mode in PyObjC?
Nowhere.
How do I use setObject_forProperty_error_ to set it?
ss.setObject_forProperty_error_("PHON", "inpt", None)
"PHON" is the same as NSSpeechSynthesizer.SpeechPropertyKey.Mode.phoneme
"inpt" is the same as NSSpeechSynthesizer.SpeechPropertyKey.inputMode
It seems these are not defined anywhere in PyObjC, but I found them by firing up XCode and writing a short Swift snippet:
import Foundation
import AppKit
let synth = NSSpeechSynthesizer()
let x = NSSpeechSynthesizer.SpeechPropertyKey.Mode.phoneme
let y = NSSpeechSynthesizer.SpeechPropertyKey.inputMode
Now looking at x and y in the debugger show that they are the strings mentioned above.
As for how to call setObject_forProperty_error_, I simply tried passing in those strings and None as the error handler, and that worked.
I am trying to create a script that would help me automate the creation of a spine rig, but I am running into a problem. I am following the tutorial provided here and I am working on the step where you skin the curve to the IK joints.
However, when I try to use mc.bindSkin(), I keep getting an error:
Error: RuntimeError: file[directory]/maya/2016.5/scripts\createRigSpine.py line 200: Maya command error)
It's too late right now to for me to do much experimenting, but I was hoping someone could help me, or tell me if I'm using the wrong commands.
mc.select(crvSpine, jntIkMidSpine, jntIkChest)
mc.bindSkin(crvSpine, jntIkMidSpine, jntIkChest, tsb=True)
(have also tried mc.bindSkin() and mc.bindSkin(tsb=True))
Ideally, I want the settings to be:
Bind To: Selected Joints
Bind Method: Closest Distance
Skinning Method: Classic Linear
Normalize Weights: Interactive
Edit: I wanted to use skinCluster, not bindSkin.
you should use the skinCluster command to bind your curve to the joints - and you can actually do it without selecting anything!
Try this:
import maya.cmds as mc
influences = [jntIkMidSpine, jntIkChest]
scls = mc.skinCluster(influences, crvSpine, name='spine_skinCluster', toSelectedBones=True, bindMethod=0, skinMethod=0, normalizeWeights=1)[0]
# alternatively, if you don't want such a long line of code:
#
influences = [jntIkMidSpine, jntIkChest]
kwargs = {
'name': 'spine_skinCluster', # or whatever you want to call it...
'toSelectedBones': True,
'bindMethod': 0,
'skinMethod': 0,
'normalizeWeights': 1
}
scls = mc.skinCluster(influences, crvSpine, **kwargs)[0]
# OR just use the short names for the kwargs...
#
influences = [jntIkMidSpine, jntIkChest]
scls = mc.skinCluster(influences, crvSpine, n='spine_skinCluster', tsb=True, bm=0, sm=0, nw=1)[0]
If you wanted to, you could also explicitly set the weights you want for each cv of the curve. You could use the skinPercent command, or even just use setAttr for the various weight attrs in the skinCluster (that's a little more difficult, but not much)
cmds.bindSkin() command made for binding bones to geometry. It's not suitable for binding to IK's only. So you need to assign what joint you need to bind to.
For example:
import maya.cmds as mc
mc.select('ikHandle1','nurbsCircle1','joint5')
mc.bindSkin('ikHandle1','nurbsCircle1','joint5')
# the order of selection is vital
For constraining selected objects use the commands like this:
mc.pointConstraint('ikHandle1','nurbsCircle1', weight=5.0)
To find out what constraints are available to you, use Rigging module – Constrain menu – Parent, Point, Orient, Scale, Aim, Pole Vector.
I was using the wrong command. mc.skinCluster is what I wanted to use, not mc.bindSkin.
Here's the code that I'm trying to run:
import pyautogui
r=pyautogui.locateOnScreen('C:\Users\David\Desktop\index.png',grayscale=False)
print r
It has to be a pixel-perfect match in order to be found. To allow for any sort of deviance you can invoke a confidence parameter.
For example:
loc = pyautogui.locateOnScreen(image, grayscale=True, confidence=.5)
However, in order to use the confidence parameter you have to have opencv_python installed. This is easy to install with pip:
./python -m pip install opencv_python
After that is in place, you should be able to account for minor differences.
I was encountering the same problem, what I did is
import pyautogui
r= None
while r is None:
r=pyautogui.locateOnScreen('C:\Users\David\Desktop\index.png',grayscale=False)
print r
I think its just because that it takes time to locate image. If you found a better solution share with me :)
I had the similar problem.
My fault was I had saved the compare picture as jpg first and then as png in MS paint.
Be sure to save the compare picture as png format. After this the Locate function worked for me.
I had same issue and kept returning None value.
I did several trials and found the solution for me.
OS : MacOS
I saved photo with my system screenshot tool ( command+shift+5) and saved. it seems it's different pixel info as what it's displayed in my screen.
Therefore I used pyautogui screenshot instead to save the photo which I wanted to.
pyautogui.screenshot('num7_.png', region=(260,360, 110, 100))
After that, it's working good regardless of grayscale parameter.
pyautogui.locateOnScreen('num7_.png')
Box(left=260, top=360, width=110, height=100)
The locateOnScreen() function returns None if the image wasn't found on the screen. Remember, the match has to be pixel-perfect in order to match it, so be sure to crop index.png to the smallest recognizable size to prevent extra details from ruining your match. Also, make sure the thing you are looking for is not obscured by any other windows on top of it.
I got this working by using the following:
r = None
while r is None:
r = pyautogui.locateOnScreen('rbin.PNG', grayscale = True)
print icon_to_click + ' now loaded'
The key is to make grayscale = True.
The official documentation says;
The Locate Functions
NOTE: As of version 0.9.41, if the locate functions can’t find the provided image,
they’ll raise ImageNotFoundException instead of returning None.
So you can decide whether an exception was raise or not. Also you should try for a finite number of times not a While True loop.
retry_counter = 0
while retry_counter < 5:
try:
result = pyautogui.locateOnScreen(IMAGE_PATH_TO_FIND)
if result:
time.sleep(1)
retry_counter = 10 # to break the loop
except:
time.sleep(1) # retry after some time, i.e. 1 sec
retry_counter += 1
I found that if you program a pause of 1 or 2 seconds (using time.sleep), then it is able to locate the image. It also takes time for python to locate the image (my computer took about 5 seconds).
I had that problem but then i crop the photo into specific part then it was locating and yes it takes time.
or this can also work.
b = pyautogui.center('calc7key.png')
I found a way to fix my problem. Only search for an image as small as possible. A picture that is only 1 pixel is found after 3 seconds. And when i try to search for an image over 500x500 then it won't find anything.
I think that the library pyautogui needs several recognition points. For example, Number seven on calculator:
The number seven on the calculator of windows 10. In this format, I´ve got the location on the screen.
Thank you for your comments
My problem was I was trying to take a snip of the calculator button. That must make a different pixel match because I tried every other option in here and nothing was working. I did a print screen then cropped it to the button I wanted and it worked.
Before
import pyautogui
image = '9.png'
loc = pyautogui.locateOnScreen(image, grayscale=True, confidence=.5)
print (loc)
Error:
None
>>>
Solution
import pyautogui
import time
time.sleep(5)
image = '9.png'
loc = pyautogui.locateOnScreen(image, grayscale=True, confidence=.5)
print (loc)
Summary: Just add this two lines:
import time
time.sleep(5)
Output
Box(left=1686, top=248, width=70, height=47)
>>>
import time
time.sleep(5)
If you have taken screenshot with snipping tool it wont work, so take screenshot with "prt sc" or command prompt. This worked for me!
I am sure most of you guys know this, but if you forget to run your python script or IDE (wether that be Visual Studio, Python IDLE, etc.) as an administrator, then pyautogui will not be able to use the click command. It will still be able to move the mouse around, but it just won’t be able to click (this is true for all operating systems, they prevent any software that dose not have administrative privileges from clicking and i think it also prevents the software from using the keyboard but I am not sure. The OS dose this as a security measure so no software can do malicious things on your computer without your consent, like for instance: prompting the user to allow the software administrator access, and then taking control of the mouse and keyboard to click the “Yes” button for the user). If you’re using Linux or Mac then I am sure you know: you would have to use the “sudo” command. Hope this helps someone ;)
I want to randomly rotate a list of objects on a given axis with a random amount retrieved from a specified range.
This is what I came up with:
import pymel.core as pm
import random as rndm
def rndmRotateX(targets, axisType, range=[0,180]):
for obj in targets:
rValue=rndm.randint(range[0],range[1])
xDeg='%sDeg' % (rValue)
#if axisType=='world':
# pm.rotate(rValue,0,0, obj, ws=1)
#if axisType=='object':
# pm.rotate(rValue,0,0, obj, os=1)
pm.rotate(xDeg,0,0,r=True)
targetList= pm.ls(sl=1)
randRange=[0,75]
rotAxis='world'
rndmRotateX(targetList,rotAxis,randRange)
Im using pm.rotate() because it allows me to specify whether I want the rotations done in world or obj space (unlike setAttr, as far as I can tell).
The problem is, it raises this error when I try to run this:
# Error: MayaNodeError: file C:\Program Files\Autodesk\Maya2012\Python\lib\site-packages\pymel\internal\pmcmds.py line 140: #
It must be something with they way I enter the arguments for pm.rotate() (Im assuming this due to the line error PyMel spits out, which has to do with its arguments conversion function), but I cant figure out for the life of me wth I did wrong. :/
I think the problem is in this line
pm.rotate(rValue,0,0, obj, os=1)
obj should be the first argument, so it should be
pm.rotate(obj, (rValue,0,0), os=1)
but to make it even prettier you could use
obj.setRotation((rValue,0,0), os=1)
And also. Use pm.selected() instead of pm.ls(sl=1). It looks better
Another way to go about doing this..
from pymel.core import *
import random as rand
def rotateObjectsRandomly(axis, rotateRange):
rotateValue = rand.random() * rotateRange
for obj in objects:
PyNode(str(selected()) + ".r" + axis).set(rotateValue)
objectRotation = [[obj, obj.r.get()] for obj in selected()]
print "\nObjects have been rotated in the {0} axis {1} degrees.\n".format(axis, rotateValue)
return objectRotation
rotateObjectsRandomly("z", 360)
Since rand.random() returns a random value between 0 - 1, I just multiplied that by the rotateRange specified by the user..or in my preference I would just do away with that all together and just multiply it by 360...
You also don't need all the feedback I just think it looks nice when ran..
Objects have been rotated in the z axis 154.145898182 degrees.
# Result: [[nt.Transform(u'myCube'), dt.Vector([42.6541437517, 0.0, 154.145898182])]] #
Just as a straight debug of what you've got...
Issue 01: it's case sensitive
pm.rotate("20deg",0,0) will work fine, but pm.rotate("20Deg",0,0) will fail and throw a MayaNodeError because it thinks that you're looking for a node called '20Deg'. Basically, you want to build your string as per: xDeg='%sdeg' % (rValue)
Issue 02: you're relying on pm.rotate()'s implicit "will apply to selected objects" behaviour
You won't see this til you apply the above fix, but if you have two selected objects, and ran the (patched) rndmRotateX function on them, you'd get both objects rotating by the exact same amount, because pm.rotate() is operating on the selection (both objects) rather than a per-object rotation.
If you want a quick fix, you need to insert a pm.select(obj) before the rotate. And you possibly want to save the selection list and restore it...however IMHO, it's a Really Bad Idea to rely on selections like this, and so I'd push you towards Kim's answer.