5
$\begingroup$

i have a hardware with some keys and i want control some objects in blender with those keys. for example if i pushed #1 key... camera rotate 1 degree
i can send, receive and move objects but my problem is freezing !!!!

i want blender receive data in real time ( always ) and process incoming data. I'm sending data unceasing (non stop). is it possible to run a python script (unlimited loop within) in the background to prevent freezing blender ?

for example i have following code :

import bpy
import serial
import mathutils
from time import sleep

ser = serial.Serial("COM7", 9600)

while True:
    data = ser.readline()
    obj = bpy.data.objects["Lamp"]
    # one blender unit in x-direction
    vec = mathutils.Vector((int(data), 0, 0))
    inv = obj.matrix_world.copy()
    inv.invert()
    # vec aligned to local axis
    vec_rot = vec * inv
    obj.location = obj.location - vec_rot
serialport.close()

it's work well outside the loop. but in this case, blender is going to Not Responding. i want object's movement in real time

i am not sure you can understand my problem .... sorry for my bad En language


Edit: i want to run this process in the background .... Modal Operator is working but still freezing blender .... i want do other works and in the background, blender receive serial data and process it

it's my modal operator ( receive degree from serial and rotate an object ). but in this case rotating only works when i move the mouse, if i leave that, rotating will be fully lagged :

import bpy
from bpy import context
import serial
from time import sleep

ser = serial.Serial("COM7", 9600)

class ModalOperator(bpy.types.Operator):
    bl_idname = "object.modal_operator"
    bl_label = "Simple Modal Operator"
    x = 0
    def modal(self, context, event):
        self.x = int(ser.readline())
        if self.x >0:
            value = int(self.x)
            value += 10
            degree = value * 3.14/180
            bpy.context.object.rotation_euler[0] = degree
            print(degree)
        if event.type  == 'LEFTMOUSE':
            ser.close()
            return {'FINISHED'}
        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        if context.object:
            print("hi")
            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "No active object")
            return {'CANCELLED'}


def register():
    bpy.utils.register_class(ModalOperator)

def unregister():
    bpy.utils.unregister_class(ModalOperator)

if __name__ == "__main__":
    register()
    bpy.ops.object.modal_operator('INVOKE_DEFAULT')
$\endgroup$
  • 2
    $\begingroup$ you need a modal operator: see this similar question: blender.stackexchange.com/questions/16588/… $\endgroup$ – zeffii Jun 22 '16 at 11:06
  • $\begingroup$ i used Modal operators too ..... it's word but only worked when we have mouse movement !!!!! if i release the mouse, process will be fully lagged @zeffii $\endgroup$ – Arash Hatami Jun 23 '16 at 5:31
  • 1
    $\begingroup$ there are several ways to write modal operators, paste your modal operator so we can see. Also related (non-modal approach) : blender.stackexchange.com/questions/41533/… $\endgroup$ – zeffii Jun 23 '16 at 7:01
  • 1
    $\begingroup$ done !! @zeffii $\endgroup$ – Arash Hatami Jun 23 '16 at 13:11
  • 2
    $\begingroup$ You may want to look at this approach to piping data from an external running process. $\endgroup$ – sambler Jun 27 '16 at 11:05
3
+50
$\begingroup$

readline is a blocking operation : http://pyserial.readthedocs.io/en/latest/shortintro.html#readline

Try to use something like :

def modal(self, context, event):
    if event.type  == 'LEFTMOUSE':
        ser.close()
        return {'FINISHED'}
    if ser.inWaiting()!=0: # or if ser.in_waiting !=0: (see remarks below)
        self.x = int(ser.readline())
        if self.x >0:
            value = int(self.x)
            value += 10
            degree = value * 3.14/180
            bpy.context.object.rotation_euler[0] = degree
            print(degree)
    return {'RUNNING_MODAL'}

Another edit as I have read that "inWaiting" function may have been changed into a property form from v3.0 of Python :

in_waiting Getter: Get the number of bytes in the input buffer Type: int

Return the number of bytes in the receive buffer.

Changed in version 3.0: changed to property from inWaiting()

So you should replace "if ser.inWaiting()!=0:" by "if ser.in_waiting !=0:".

Edit for a little explanation : the line "if ser.inWaiting()!=0:" wont block as it is just testing if there is something to read. So if nothing is available, the function will directly go through the "return {'RUNNING_MODAL'}" without blocking.

You can also test the behavior, replacing "if ser.inWaiting()!=0:" by "while ser.inWaiting()!=0:". It's hard to know which is better. The difference is "if" will immediately give the hand to Blender and "while" will eventually take into account several serial inputs before giving back the hand to Blender.

For this line : self.x = int(ser.readline()) You should also read data depending on the waiting available bytes you need here (but as I dont know how your device communicates... I cant say).

(not tested)

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.