I am trying to get one boolean attribute (A) to change another(B). The one to be controlled (B) already has a script job running it and so I can't create a set driven key, direct connection, or expression to control it, so I'm trying another script job, since running the script function by itself achieves the desired result. I just can't figure out how to tie that script to run to the attribute change (B) that I want to drive it by (A).
This is placed in a script node set to the open gui trigger (to load when maya opens as I understand it). Here's a screenshot.
What am I missing here?
import maya.cmds as cmds
def togglePicker(pickerAttr):
cmds.setAttr(pickerAttr, not 0)
nameSpace = cmds.ls(sl=True)[0].rpartition(':')[0]
if len(nameSpace) > 0:
pickerAttr = nameSpace + ':Main.picker'
myPickerAttr = nameSpace + ':MoverMain_Cntrl.Picker'
else:
pickerAttr = 'Main.picker'
myPickerAttr = 'MoverMain_Cntrl.Picker'
cmds.scriptJob(attributeChange=[myPickerAttr,togglePicker])
Your script node is executed every time when maya loads a scene, not when it is started, at least that's what the docs say. So every time you load a scene, a new scriptJob is created.
Your script should show an error message since the togglePicker() function is called without an argument, but it requires an argument. Even if it works, it will not work.. what you do at the moment is the following:
As soon as you turn on the MoverMain_Cntrl.Picker attribute, the togglePicker() function is called and turns it on, even if you turn it off. The pickerAttrvariable is not used. So you should have a look at your program logic.
You can solve the agrument problem by using the partial function like this:
import maya.cmds as cmds
from functools import partial
def togglePicker(pickerAttr):
cmds.setAttr(pickerAttr, not 0)
nameSpace = cmds.ls(sl=True)[0].rpartition(':')[0]
if len(nameSpace) > 0:
pickerAttr = nameSpace + ':Main.picker'
myPickerAttr = nameSpace + ':MoverMain_Cntrl.Picker'
else:
pickerAttr = 'Main.picker'
myPickerAttr = 'MoverMain_Cntrl.Picker'
cmds.scriptJob(attributeChange=[myPickerAttr,partial(togglePicker, pickerAttr)])
I got it to work! (previously I had switched to the script node to MEL so I could test the mel command mentioned in the comments that did work, but I forgot to switch back to python when I realized the selection issue I also mentioned in the comments).
So here's what worked, where I know I'll have to manually change the namespace name in case the scene file name changes:
import maya.cmds as cmds
def togglePicker():
cmds.setAttr(pickerAttr, not 0)
if cmds.namespace(exists='ExtremeBallRig_v008'):
pickerAttr = 'ExtremeBallRig_v008:Main.picker'
myPickerAttr = 'ExtremeBallRig_v008:MoverMain_Cntrl.Picker'
else:
pickerAttr = 'Main.picker'
myPickerAttr = 'MoverMain_Cntrl.Picker'
cmds.scriptJob(attributeChange=[myPickerAttr,togglePicker])
Related
Maybe I am completely off track here (and above my paygrade for sure), but what I want to do is to give users of my app (That I am writing in Python since that's the language I know) a python interpreter to control some objects within my app. Something similar like many 3D and VFX softwares have (Maya, Blender, Nuke). This is the code I got so far:
#main.py
import code
import networkx as nx
class Env():
def __init__(self):
self.graph = nx.graph.DiGraph()
# load library with functions that will be availabel for user inside the app.
import mylib
functions = {f: getattr(mylib, f) for f in dir(mylib) if not f.startswith('__')}
self._interpreter = code.InteractiveInterpreter(locals=functions)
def execute_node(self, node=None):
# In IRL the main object to be pass1ed to users' interpreter will be the self.graph object
# but I made it more clear for this question.
self._interpreter.locals['var'] = 42
node = "print(var)\nprint(some_function())\nvar = 67" # Let's pretend node object contains this code.
self._interpreter.runcode(node)
if __name__ == '__main__':
e = Env()
# some code, node creation and so on...
e.execute_code()
print(e.locals['var'])
#mylib.py
var = None # I have to put this here because if there is no variable function fails at import
def some_function():
print(var)
Output:
42 # This prints as expected
None # The print within the function prints the value that was there when module was initialized
67 # The last print returns expected value
So, it is clear that python interprets the functions on first import and "bakes" the global variables that it had at the import time. Now the question is can I somehow easily make it use the globals passed from the code.InteractiveInterpreter() or I should look for a completely different solution (and which one) :)? Of course the idea is that the two python programs should communicate, the user should use a special library to operate the software and the backend code should not be exposed to them. Do I make any sense? Thanks :)
This is the one-ish instance where you do want to use the exec() function, but please remember that the user may be able to run any Python code, including stuff that could run forever, mess up your main program, write (or delete) files, etc.
def run_code(code, add_locals={}):
code_locals = {}
code_locals.update(add_locals) # Copy in the additional locals so that dict could be reused
exec(
code,
{}, # no globals (you may wish to replace this),
code_locals,
)
return code_locals # return updated locals
class Beeper: # define a toy object
def beep(self, times):
print("Beep! " * times)
beeper = Beeper() # instantiate the object to play with
# Some user code...
user_code = """
x = 5
beeper.beep(x)
x += 3
"""
new_locals = run_code(user_code, {"beeper": beeper})
print(new_locals)
This outputs
Beep! Beep! Beep! Beep! Beep!
{'beeper': <__main__.Beeper>, 'x': 8}
So you can see we can use the locals the user has modified if need be.
So I'm new at multiprocessing and subprocessing and I am not sure if I am doing it right.
I have two scripts. One runs the main GUI, and has buttons that run other scrips. I want my entry boxes to be read by my other script, so that it can change the axis of a graph.For now, I simplified it so that it can print it so I can see that the values are being passed to begin with.
When I run the scrips like this:
###class_testing.py### (main script)
class Amplifier_Data_Analysis:
def saving_graph_stuff(self):
global int_startfreq,int_stopfreq,float_steps,float_add_tick
STARTFREQUENCY = self.Start_Freq.get()
int_startfreq = int(STARTFREQUENCY)
STOPFREQUENCY = self.Stop_Freq.get()
int_stopfreq = int(STOPFREQUENCY)
STEPS = self.Steps.get()
float_steps = float(STEPS)
ADD_TICK = self.Add_Tick.get()
float_add_tick = float(ADD_TICK)
print(int_startfreq,int_stopfreq,float_steps,float_add_tick)
return int_startfreq,int_stopfreq,float_steps,float_add_tick
def testreport(self):
subprocess.Popen([sys.executable,'test.py'])
###test.py###
from class_testing import *
int_startfreq,int_stopfreq,float_steps,float_add_tick = Amplifier_Data_Analysis.saving_graph_stuff()
print(startfrequency)
print(stopfrequency)
I get
int_startfreq,int_stopfreq,float_steps,float_add_tick = Amplifier_Data_Analysis.saving_graph_stuff()
TypeError: saving_graph_stuff() missing 1 required positional argument: 'self'
But when I put self, It says it is not defined which makes sense since it's a different script from the main. The GUI is generated form the PAGE app so it's very lengthy, but this is how it looks like: GUI
How do I pass or read variables between two scripts?
It's a class - you have to initialize it
int_startfreq,int_stopfreq,float_steps,float_add_tick = Amplifier_Data_Analysis().saving_graph_stuff()
I'm new to python and am using Spyder's IDE. One feature I appreciate about it is it's variable explorer. However, based on some research, I found that it only shows global variables. A workaround that I found for that is by using the inspect module:
import inspect
local_vars = {}
def main():
global local_vars
a = 2
b = 4
c = a+b
local_vars = inspect.currentframe().f_locals
return c
main()
This works well, however, I have other functions that are called from within main() and I'd like to see those variables in the variable explorer as well. I mimicked what was done for the variables in the main function and the dict does not appear. I noticed that when I disable the setting to "exclude unsupported data types" in Spyder's variable explorer options, the second dict appears with the right size attribute, however, I am unable to open/view it. Any ideas on a possible work around? This is my first time posting BTW.
Thanks!!
Here is a working example of my issue and I've traced it down to pylab subplots.
import inspect, pylab
mainVars = {}
def main():
global mainVars
a = 1
b = 2
fig = pylab.figure()
subPlot = fig.add_subplot(211) ## line of interest
pylab.close('all')
mainVars = inspect.currentframe().f_locals
main()
When the line of interest is commented out, the dict is created successfully and can be viewed. It appears that the object created using fig.add_subplot() is not being handled properly by the dict. It seems to be an unsupported datatype.
Hope this helps clarify the issue.
Thanks again.
To view the contents of local variables when some of them are unsupported, you have to follow these steps:
Go to the options menu of the Variable Explorer (the last icon from left to right on it).
Select the option called Exclude unsupported data types.
Then you'll see all local variables saved in the f_locals dict, even if you're unable to double click on it.
All of these workarounds are making your code significantly harder to read for outsiders. You have two options to inspect the values of the variables inside your function. First, you could just return the variables you are interested in:
def main():
a = 2
b = 4
c = a+b
return a, b, c
a, b, c = main()
Second, if you just want to check that the function works as expected or debug it, you can debug the function and step into it. So select Run|Debug from the menu instead of running the file directly. Then you can step into the function - the values of the variables will be visible in the variable explorer when the execution is inside the function.
I'd like to do the exact same thing that is explained here :
How to continuously monitor rhythmbox for track change using python
but with Clementine instead of Rhythmbox.
Problem is, I couldn't find the equivalent of playingUriChanged to give to the connect_to_signal method.
The only thing I could find with qdbus that seemed relevant was
signal void org.freedesktop.MediaPlayer.TrackChange(QVariantMap)
but it takes a parameter.
I'm not familiar with DBus so any help is appreciated.
Thanks
It does not take a parameter, it returns parameter (hash map)
code extracted from this script:
def TrackChange(Track):
# use Track["URI"], Track["title"], Track["artist"] etc
def Connect(name):
global root, player, tracklist
# first we connect to the objects
root_o = bus.get_object(name, "/")
player_o = bus.get_object(name, "/Player")
tracklist_o = bus.get_object(name, "/TrackList")
# there is only 1 interface per object
root = dbus.Interface(root_o, "org.freedesktop.MediaPlayer")
tracklist = dbus.Interface(tracklist_o, "org.freedesktop.MediaPlayer")
player = dbus.Interface(player_o, "org.freedesktop.MediaPlayer")
# connect to the TrackChange signal
player_o.connect_to_signal("TrackChange", TrackChange, dbus_interface="org.freedesktop.MediaPlayer")
I am writing an external Python/comtypes script (in PythonWin) that needs to get a reference to the current ArcGIS 10.0 ArcMap session (through the ArcObjects COM). Because the script is outside the application boundary, I am getting the application reference through the AppROT (running object table). The first code snippet below is the main Python driver module. In it is a function GetApp() to grab an application reference from the AppROT. This code works fine and returns IApplication on the singleton AppRef object. Makes sense, and that's what the ArcObjects reference seems to indicate. Now, my main goal is to get to an IMxDocument. In the main loop, I get to an IDocument successfully and can print the title. The next line, though, a Query Interface, throws an error - NameError: name 'esriArcMapUI' is not defined. The error occurs consistently, even after closing PythonWin and reopening (which you always want to try before you conclude that you have a problem). [BTW, the second code snippet is the CType() function for QI, defined in and imported from the SignHelpers.py module.] So, here are my questions:
(1) What COM object is the IDocument on?
(2) How do I get from my IDocument to the intended IMxDocument? Do I need to create a new MxDocument object first? [Sorry. Two questions there.]
(3) If I don't have to create a new object, then how do I do the QI?
I did a lot of ArcObjects work in VB6 quite a few years ago, so explicit QI's and namespaces are putting the screws to me at the moment. Once I can get to an IMxDocument I will be home free. I would appreciate any help anyone can give me with this.
Also, I apologize for the formatting of the code below. I could not figure out how to get Python code to format correctly. Indentation doesn't work, and some of the Python code is interpreted as formatting characters.
=== code: main py module ===
import sys, os
sys.path.append('C:\GISdata\E_drive\AirportData\ATL\Scripts')
import comtypes
from SignHelpers import *
def GetApp(app):
"""Get a hook into the current session of ArcMap; \n\
Execute GetDesktopModules() and GetStandaloneModules() first"""
print "GetApp called" ####
import comtypes.gen.esriFramework as esriFramework
import comtypes.gen.esriArcMapUI as esriArcMapUI
import comtypes.gen.esriCarto as esriCarto
pAppROT = NewObj(esriFramework.AppROT, esriFramework.IAppROT)
iCount = pAppROT.Count
print "appROT count = ", iCount ####
if iCount == 0:
print 'No ArcGIS application currently running. Terminating ...'
return None
for i in range(iCount):
pApp = pAppROT.Item(i) #returns IApplication on AppRef
if pApp.Name == app:
print "Application: ", pApp.Name ####
return pApp
print 'No ArcMap session is running at this time.'
return None
if __name__ == "__main__":
#Wrap needed ArcObjects type libraries (.olb)...
... code omitted ...
GetDesktopModules(dtOLB) #These force comtypes to generate
GetStandaloneModules(saOLB) #the Python wrappers for .olb's
#Connect to ArcMap
pApp = GetApp("ArcMap")
pDoc = pApp.Document #IDocument on current Document object
print pDoc.Title
pMxDoc = CType(pDoc, esriArcMapUI.IMxDocument) #QI to IMxDocument on MxDocument
=== code for CType() ===
def CType(obj, interface):
try:
newobj = obj.QueryInterface(interface)
return newobj
except:
return None
Scoping error (per the comments):
The import comtypes.gen.esriArcMapUI as esriArcMapUI statement needed to define the esriArcMapUI namespace was being run within the GetApp() function, so the namespace was local to the function.