7
$\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$
5
  • 2
    $\begingroup$ you need a modal operator: see this similar question: blender.stackexchange.com/questions/16588/… $\endgroup$
    – zeffii
    Jun 22, 2016 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$ Jun 23, 2016 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, 2016 at 7:01
  • 1
    $\begingroup$ done !! @zeffii $\endgroup$ Jun 23, 2016 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, 2016 at 11:05

1 Answer 1

4
+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$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .