I’m creating an add-on which acts as a server and receives client information from an outside application through sockets. The add-on is spawning a thread when the Enable button is clicked in the add-on. However, if you try shutting down Blender when the thread is running, the thread is not killed and Blender still runs in the background (even though it looks like Blender is no longer running).
The add-on code follows. To replicate this behavior, run this add-on, go to the 3D view -> left panel -> Miscellaneous tab, and open up the Kinect to Blender option. Click the Enable button. Now shut down Blender. Even though Blender will have disappeared, it will still be running in the background. (You’ll be able to see it in Windows Task Manager.) If you go through all of these steps but don’t click the Enable button, Blender will shut down correctly.
What do I need to do in my add on so that closing Blender will kill off the thread?
# Code based on pyEphestos (https://github.com/kilon/pyEphestos) by Kilon Alios ([email protected])
bl_info = {
'name': "Simple Carnival Kinect to Blender",
'author': "Jeff Boller",
'version': (2, 0, 9),
'blender': (2, 6, 7),
'api': 44136,
'location': "View3D > Left panel",
'description': "Allows Blender's camera to be controlled by a motion tracked object via Kinect",
'warning': "",
'wiki_url': "3d.simplecarnival.com",
'tracker_url': "",
'category': "Object"}
import sys
import time
import bpy
import threading
import socket
from bpy.props import *
from time import sleep
import atexit
import re
kinectToBlender_running = False
thread_created = False
threadSocket = 0
socketServer = 0
receivedSocket = "none"
listening = False
socketMessages = []
shutDown = False
receivedData = ''
def cleanup():
global kinectToBlender_running, threadSocket, listening, socketServer
kinectToBlender_running = False
listening = False
socketServer.settimeout(0.1)
threadSocket.join()
#socketServer.shutdown(socket.SHUT_RDWR)
socketServer.close()
del socketServer
thread_created = False
shutDown = False
atexit.register(cleanup)
def create_thread():
global threadSocket,listening
threadSocket = threading.Thread(name='threadSocket', target= socket_listen)
listening = True
create_socket_connection()
threadSocket.start()
#threadSocket.join()
def socket_listen():
global receivedSocket,listening, receivedData,socketServer, socketMessages
socketServer.listen(5)
while listening:
(receivedSocket , adreess) = socketServer.accept()
receivedData = (receivedSocket.recv(1024)).decode("utf-8")[:-2]
#exec(receivedData)
socketMessages.append(receivedData)
sleep(0.03)
receivedSocket.close()
def create_socket_connection():
global socketServer
socketServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socketServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
socketServer.bind(('127.0.0.1',4000))
class open_ephestos(bpy.types.Operator):
bl_idname = "ephestos_button.modal"
bl_label = "Enable"
_timer = None
def modal(self, context, event):
global kinectToBlender_running, thread_created, listening, socketServer, socketMessages, shutDown
result = {'PASS_THROUGH'}
if context.area.type == 'VIEW_3D' and kinectToBlender_running and event.type in {'ESC',}:
kinectToBlender_running = False
listening = False
#time.sleep(1)
socketServer.close()
context.window_manager.event_timer_remove(self._timer)
#time.sleep(1)
thread_created = False
result = {'CANCELLED'}
self.report({'WARNING'}, "Kinect to Blender has been disabled")
if context.area.type == 'VIEW_3D' and kinectToBlender_running and event.type == 'TIMER' :
if shutDown:
kinectToBlender_running = False
listening = False
socketServer.settimeout(0.01)
#socketServer.shutdown(socket.SHUT_RDWR)
socketServer.close()
threadSocket.join()
del socketServer
context.window_manager.event_timer_remove(self._timer)
thread_created = False
shutDown = False
result = {'CANCELLED'}
self.report({'WARNING'}, "Kinect to Blender has been disabled")
for msg in socketMessages:
print('******** GOT THIS: ', msg)
socketMessages.remove(msg)
# create_thread()
# thread_created = True
return result
def invoke(self, context, event):
global kinectToBlender_running,thread_created
if context.area.type == 'VIEW_3D' and kinectToBlender_running == False :
self.cursor_on_handle = 'None'
# Add the region OpenGL drawing callback
# draw in view space with 'POST_VIEW' and 'PRE_VIEW'
#self._handle =bpy.types.SpaceView3D.draw_handler_add(draw_ephestos,(self,context), 'WINDOW', 'POST_PIXEL')
self._timer = context.window_manager.event_timer_add(0.01,context.window)
kinectToBlender_running = True
context.window_manager.modal_handler_add(self)
create_thread()
thread_created = True
return {'RUNNING_MODAL'}
else:
global shutDown
shutDown = True
return {'FINISHED'}
class ephestos_panel(bpy.types.Panel):
bl_label = "Kinect to Blender"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
def draw(self, context):
global receivedSocket,listening
sce = context.scene
layout = self.layout
box = layout.box()
if listening:
box.label(text="Enabled")
else:
box.label(text="Disabled")
box.operator("ephestos_button.modal")
box.operator("ephestos.close")
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()