Blender not executing python in real time - python

Okay, I think I have better way to ask this question.
Say I want to scale up my object by 10 in some axis with some time interval gap between each scaling up, so I think I should use this script...
import bpy
import time
obj = bpy.context.active_object
for i in range(10):
time.sleep(0.5)
obj.scale[0]+=0.1
but this is showing the result (scaled object) once the whole script has been executed, what should I do to work it like updating as script goes on.

Got one of the solutions, but this might not be the best one I guess.
Add bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) after a transformation has been applied.
So updated code will look like:
import bpy
import time
obj = bpy.context.active_object
for i in range(10):
obj.scale[0]+=0.1
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
time.sleep(0.5)
Also officially blender does not consider this a good practice so this might not work later. https://www.blender.org/api/blender_python_api_2_76_2/info_gotcha.html#can-i-redraw-during-the-script

Related

Getting a selection in 3ds Max into a list in Python

I am writing in Python, sometimes calling certain aspects of maxscript and I have gotten most of the basics to work. However, I still don't understand FPValues. I don't even understand while looking through the examples and the max help site how to get anything meaningful out of them. For example:
import MaxPlus as MP
import pymxs
MPEval = MP.Core.EvalMAXScript
objectList = []
def addBtnCheck():
select = MPEval('''GetCurrentSelection()''')
objectList.append(select)
print(objectList)
MPEval('''
try (destroyDialog unnamedRollout) catch()
rollout unnamedRollout "Centered" width:262 height:350
(
button 'addBtn' "Add Selection to List" pos:[16,24] width:88 height:38
align:#left
on 'addBtn' pressed do
(
python.Execute "addBtnCheck()"
)
)
''')
MP.Core.EvalMAXScript('''createDialog unnamedRollout''')
(I hope I got the indentation right, pretty new at this)
In the above code I successfully spawned my rollout, and used a button press to call a python function and then I try to put the selection of a group of objects in a variable that I can control through python.
The objectList print gives me this:
[<MaxPlus.FPValue; proxy of <Swig Object of type 'Autodesk::Max::FPValue *' at 0x00000000846E5F00> >]
When used on a selection of two objects. While I would like the object names, their positions, etc!
If anybody can point me in the right direction, or explain FPValues and how to use them like I am an actual five year old, I would be eternally grateful!
Where to start, to me the main issue seems to be the way you're approaching it:
why use MaxPlus at all, that's an low-level SDK wrapper as unpythonic (and incomplete) as it gets
why call maxscript from python for things that can be done in python (getCurrentSelection)
why use maxscript to create UI, you're in python, use pySide
if you can do it in maxscript, why would you do it in python in the first place? Aside from faster math ops, most of the scene operations will be orders of magnitude slower in python. And if you want, you can import and use python modules in maxscript, too.
import MaxPlus as MP
import pymxs
mySel = mp.SelectionManager.Nodes
objectList = []
for each in mySel:
x = each.Name
objectList.append(x)
print objectList
The easiest way I know is with the
my_selection = rt.selection
command...
However, I've found it works a little better for me to throw it into a list() function as well so I can get it as a Python list instead of a MAXscript array. This isn't required but some things get weird when using the default return from rt.selection.
my_selection = list(rt.selection)
Once you have the objects in a list you can just access its attributes by looking up what its called for MAXscript.
for obj in my_selection:
print(obj.name)

python autopy problems/confusion

so im trying to make a bot script that when a certain hex color is on a certain pixel it will execute some code to move the mouse,click etc. and i have it to where it takes a screenshot every 1 second to the same png file and updates the png file's pic. i have the hex color for the pixel cords print to the console so i can see if its updating or not. it never updates it just stays the same. ive tried writing this script many ways and sadly i only have one version to show you but hopefully you will understand what i was trying to accomplish. im on python 2.7 btw. thank you all for your time!!!!
import autopy
from time import sleep
color_grabber = hex(autopy.bitmap.Bitmap.open("screen1.png").get_color(292,115))
def color_checker():
global color_grabber
color_grabber = color_grabber
return
def mouse_move_click():
autopy.mouse.smooth_move(433,320)
autopy.mouse.click()
def screen_grab():
autopy.bitmap.capture_screen().save("screen1.png")
def the_ifs(mouse_move_click):
if color_checker == "0xffcb05":
mouse_move_click()
while 1==1:
sleep(1)
screen_grab()
color_checker()
the_ifs(mouse_move_click)
print color_grabber
from autopy.mouse import LEFT_BUTTON
autopy.mouse.click(LEFT_BUTTON)
autopy.mouse.toggle(True, LEFT_BUTTON)
autopy.mouse.toggle(False, LEFT_BUTTON)
I see the need to do this in other people's code, but I don't understand why want to use the up and down after the click.In fact when I test on Windows 7, click is effective, but is not very correct, feel more like the down to my operation
I believe your problem is how you're using color_grabber. Saying color_grabber = color_grabber does nothing. What's happening in your code is that when you run it, after the imports, you define the value of color_grabber to be the color in your image. Then your while loop executes and in that loop you call color_checker. This function brings in the variable color_grabber from the global namespace and then you set that variable equal to itself. You're not re-executing the command you used to define color_grabber in the first place. You're just storing the color value back into itself so clearly its not going to change.
You also have a problem in how you're calling your mouse_move_click function. You don't want to pass in the function name, as that isn't really necessary. However, you also performed the check color_checker == "0xffcb05" which was comparing your function (the function itself, not the returned value) to the hex code. That doesn't do you any good. You want to compare the color. The solution is to pass the color into the_ifs and use that color to compare to the hex code. I should note though that you don't need to make the_ifs into its own function. Just put that if statement in your while loop. I left it how you had it though.
What you want is something like this.
import autopy
from time import sleep
def color_checker():
color_grabber = hex(autopy.bitmap.Bitmap.open("screen1.png").get_color(292,115))
return color_grabber
def mouse_move_click():
autopy.mouse.smooth_move(433,320)
autopy.mouse.click()
def screen_grab():
autopy.bitmap.capture_screen().save("screen1.png")
def the_ifs(color):
if color == "0xffcb05":
mouse_move_click()
while 1==1:
sleep(1)
screen_grab()
color = color_checker()
the_ifs(color)
print color
Note that I have not run this code myself so I can't guarantee it works, but I believe it should.

3dsmax python add float script to sub animations

I have the next setup: I have a sphere which has a morpher modifier. This morpher modifier has a certain amount of channels filled with morph targets aka sub animations. Now I want to add a controller to each of these subanimations, more specifically a controller with a float script. I have the code snippet that should work but when I go to the curve editor, the morph channels/ sub animations did not change controller, nor is the value of their controller changed.
import MaxPlus
target = MaxPlus.INode.GetINodeByName('Sphere001')
#Retrieve the morpher modifier
mod = target.GetModifier(0)
#ID of a float script controller
id = MaxPlus.Class_ID(1233584870,1911625032)
#Create float controller
float_co = MaxPlus.Factory.CreateFloatController(id)
#Retrieve the first morph channel / sub animation
sub = mod.GetSubAnim(1)
#Controller is assigned to the sub animation
sub.AssignController(float_co,1)
#Basic test which assigns 20 to the sub animation
float_co.ParameterBlock.Script.Value = '20'
When I add a wrong value to the script, for example:
float_co.ParameterBlock.Script.Value = '=20'
I receive an error and the usual window when you manually add a controller to an object or node. However the strange thing is that at the top of the window: the name of the object to which it is connected, does not show. See figure for clarification:
Can someone tell me what I'm doing wrong? Thank you!
I solved it by using the ugly way:
import MaxPlus
test = MaxPlus.FPValue()
success = MaxPlus.Core.EvalMAXScript(string_with_command,test)
This is used twice: first to create the float script controller and a second time to add the script to the controller. Be careful if anyone wants to try this, the script for the controller needs to be a string. Do not use
x as string
with the expressing you want to use as script for the float script controller since maxscript will evaluate x on the timeframe you are currently in 3ds max and will convert this value to a string. This value will be used as script for all time frames which clearly is not what you want. A small hack I used was:
script_value_example = '"amax #(0, ($sphere.position.x - cube.position.y))"'
This is still a string for python and maxscript will see the " " and will interpret it as a string. The other way around doesn't work, maxscript does not interpret ' ' as string.
Maybe this will help someone in the future. Also if someone knows the proper way to do it using the code in the question, please leave a reply, I'm interested to know.

How to use IPython.parallel for functions with multiple inputs?

This is my first attempt at using IPython.parallel so please bear with me.
I read this question
Parfor for Python
and am having trouble implementing a simple example as follows:
import gmpy2 as gm
import numpy as np
from IPython.parallel import Client
rc = Client()
lview = rc.load_balanced_view()
lview.block = True
a = 1
def L2(ii,jj):
out = []
out.append(gm.fac(ii+jj+a))
return out
Nloop = 100
ii = range(Nloop)
jj = range(Nloop)
R2 = lview.map(L2, zip(ii, jj))
The problems I have are:
a is defined outside the loop and I think I need to do something like "push" but am a bit confused by that. Do I need to "pull" after?
there are two arguments that are required for the function and I don't know how to pass them correctly. I tried things like zip(ii,jj) but got some errors.
Also,, I assume the fact that I'm using a random library gmpy2 shouldn't affect things. Is this correct? Do I need to do anything special for this?
Ideally I would like your help so on this simple example the code runs error free.
If you think it would be beneficial to post my failed attempts at #2 let me know. I'm in the dark with #1.
I found two ways that make this work:
One is pushing the variable to the cores. There is no need to pull it. The variable will simply be defined in the namespace of each process-engine.
rc.client[:].push({'a':a})
R2 = lview.map(L2, ii, jj)
The other way is as to redefine L2 to take a as an input and pass an array of a's to the map function:
def L2(ii,jj,a):
out = []
out.append(gm.fac(ii+jj+a))
return out
R2 = lview.map(L2, ii, jj, [a]*Nloop)
With regards to the import as per this website:
http://ipython.org/ipython-doc/dev/parallel/parallel_multiengine.html#non-blocking-execution
You simply import the required libraries in the function:
Note the import inside the function. This is a common model, to ensure
that the appropriate modules are imported where the task is run. You
can also manually import modules into the engine(s) namespace(s) via
view.execute('import numpy')().
Or you can do as per this link
http://ipython.org/ipython-doc/dev/parallel/parallel_multiengine.html#remote-imports

Reusing module references in Python (Matplotlib)

I think I may have misunderstood something here... But here goes.
I'm using the psd method in matplotlib inside a loop, I'm not making it plot anything, I just want the numerical result, so:
import pylab as pyl
...
psdResults = pyl.psd(inputData, NFFT=512, Fs=sampleRate, window=blackman)
But that's being looped 36 times every time I run the function it's in.
I'm getting a slow memory leak when I run my program over time, so used 'heapy' to monitor this, and every time I run the function, it adds 36 to these 3 heaps:
dict matplotlib.line.Line26
dict matplotlib.transforms.CompositeAffine2D
dict matplotlib.path.Path
I can only conclude that each time I use the psd method it merely adds it to some dictionary somewhere, whereas I want to effectively wipe the memory - i.e. reset pylab each loop so it doesn't store anything.
I could be misinterpreting heapy but it seems pretty clear that pylab is just growing each loop even though I only want to use it's psd method, I don't want it saving the results anywhere itself !
Cheers
Try this:
from matplotlib import mlab
psdResults = mlab.psd(inputData, NFFT=512, Fs=sampleRate, window=blackman)
Does that improve the situation?

Categories

Resources