Skip to content

Commit 4d9db35

Browse files
committed
- Cleanup
- Improved exception on connection error - command port is now opened between scenes
1 parent 673d43e commit 4d9db35

File tree

4 files changed

+81
-85
lines changed

4 files changed

+81
-85
lines changed

__init__.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717

1818
from .command_port import register as register_command_port
1919
from .command_port import unregister as unregister_command_port
20-
from .command_port import open_command_port
21-
from .tools import close_command_port
20+
from .command_port import CommandPortOperator
2221

2322

2423
class OpenCommandPortOperator(bpy.types.Operator):
@@ -27,7 +26,7 @@ class OpenCommandPortOperator(bpy.types.Operator):
2726

2827
# noinspection PyMethodMayBeStatic,PyUnusedLocal
2928
def execute(self, context):
30-
open_command_port()
29+
bpy.ops.object.command_port('INVOKE_DEFAULT')
3130
return {'FINISHED'}
3231

3332

@@ -37,7 +36,14 @@ class CloseCommandPortOperator(bpy.types.Operator):
3736

3837
# noinspection PyMethodMayBeStatic
3938
def execute(self, context):
40-
close_command_port()
39+
try:
40+
if not CommandPortOperator.instance.is_alive():
41+
print("Port is not running")
42+
return False
43+
CommandPortOperator.instance.do_run = False
44+
print("Command port closed")
45+
except NameError:
46+
print("Port is not running. It was never initialized.")
4147
return {'FINISHED'}
4248

4349

@@ -54,19 +60,19 @@ class BLENDERCOMMANDPORT1_PT_Panel(Panel):
5460

5561
def draw(self, context):
5662
layout = self.layout
57-
scene = context.scene
58-
layout.prop(scene, 'bcp_queue_size')
59-
layout.prop(scene, 'bcp_timeout')
60-
layout.prop(scene, 'bcp_port')
61-
layout.prop(scene, 'bcp_buffersize')
62-
layout.prop(scene, 'bcp_max_connections')
63-
layout.prop(scene, 'bcp_return_result')
64-
layout.prop(scene, 'bcp_result_as_json')
65-
layout.prop(scene, 'bcp_redirect_output')
66-
layout.prop(scene, 'bcp_share_environ')
63+
window_manager = context.window_manager
64+
layout.prop(window_manager, 'bcp_queue_size')
65+
layout.prop(window_manager, 'bcp_timeout')
66+
layout.prop(window_manager, 'bcp_port')
67+
layout.prop(window_manager, 'bcp_buffersize')
68+
layout.prop(window_manager, 'bcp_max_connections')
69+
layout.prop(window_manager, 'bcp_return_result')
70+
layout.prop(window_manager, 'bcp_result_as_json')
71+
layout.prop(window_manager, 'bcp_redirect_output')
72+
layout.prop(window_manager, 'bcp_share_environ')
6773
row = layout.row()
6874
try:
69-
port_running = bpy.context.window_manager.keep_command_port_running
75+
port_running = CommandPortOperator.instance.is_alive()
7076
except AttributeError:
7177
port_running = False
7278

command_port.py

Lines changed: 56 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1-
import os
21
from collections import namedtuple
32
from contextlib import AbstractContextManager
43
import json
54
import socket
6-
import sys
75
import threading
86
from queue import Queue, Empty
97

108
import bpy
11-
from bpy.types import Scene
129
from bpy.props import BoolProperty
1310
from bpy.props import FloatProperty
1411
from bpy.props import IntProperty
1512

1613
# --- Stores result of command. Created to make easier detecting result from lines of stdout
1714
ResultContainer = namedtuple("ResultContainer", ["value"])
15+
COMMAND_PORT = None
16+
17+
import sys
18+
sys.path.append(r"/home/pawel/git/tmp2")
19+
import pydevd_pycharm
20+
pydevd_pycharm.settrace('localhost', port=6666, stdoutToServer=True, stderrToServer=True)
1821

1922

2023
class OutputDuplicator(AbstractContextManager):
@@ -122,60 +125,64 @@ class CommandPortOperator(bpy.types.Operator):
122125
bl_idname = "object.command_port"
123126
bl_label = "Blender Command Port"
124127
timer = None
128+
instance = None
129+
keep_command_port_running = False
125130

126131
# noinspection PyUnusedLocal
127132
def __init__(self):
128133
super(CommandPortOperator, self).__init__()
129134
try:
130-
self.command_port = CommandPort(queue_size=bpy.context.scene.bcp_queue_size,
131-
timeout=bpy.context.scene.bcp_timeout,
132-
port=bpy.context.scene.bcp_port,
133-
buffersize=bpy.context.scene.bcp_buffersize,
134-
max_connections=bpy.context.scene.bcp_max_connections,
135-
return_result=bpy.context.scene.bcp_return_result,
136-
result_as_json=bpy.context.scene.bcp_result_as_json,
137-
redirect_output=bpy.context.scene.bcp_redirect_output,
138-
share_environ=bpy.context.scene.bcp_share_environ)
135+
try:
136+
if not CommandPortOperator.instance.is_alive():
137+
raise AttributeError # Hacky, but works
138+
self.command_port = CommandPortOperator.instance
139+
except AttributeError:
140+
self.command_port = CommandPort(queue_size=bpy.context.window_manager.bcp_queue_size,
141+
timeout=bpy.context.window_manager.bcp_timeout,
142+
port=bpy.context.window_manager.bcp_port,
143+
buffersize=bpy.context.window_manager.bcp_buffersize,
144+
max_connections=bpy.context.window_manager.bcp_max_connections,
145+
return_result=bpy.context.window_manager.bcp_return_result,
146+
result_as_json=bpy.context.window_manager.bcp_result_as_json,
147+
redirect_output=bpy.context.window_manager.bcp_redirect_output,
148+
share_environ=bpy.context.window_manager.bcp_share_environ)
149+
CommandPortOperator.instance = self.command_port
150+
self.command_port.start()
139151
except AttributeError as e:
140152
try:
141153
# ---- Make sure that properties are not missing and did not cause this exception
142-
queue_size = bpy.context.scene.bcp_queue_size,
143-
timeout = bpy.context.scene.bcp_timeout,
144-
port = bpy.context.scene.bcp_port,
145-
buffersize = bpy.context.scene.bcp_buffersize,
146-
max_connections = bpy.context.scene.bcp_max_connections,
147-
return_result = bpy.context.scene.bcp_return_result,
148-
result_as_json = bpy.context.scene.bcp_result_as_json,
149-
redirect_output = bpy.context.scene.bcp_redirect_output
150-
bcp_share_environ = bpy.context.scene.bcp_share_environ
154+
queue_size = bpy.context.window_manager.bcp_queue_size,
155+
timeout = bpy.context.window_manager.bcp_timeout,
156+
port = bpy.context.window_manager.bcp_port,
157+
buffersize = bpy.context.window_manager.bcp_buffersize,
158+
max_connections = bpy.context.window_manager.bcp_max_connections,
159+
return_result = bpy.context.window_manager.bcp_return_result,
160+
result_as_json = bpy.context.window_manager.bcp_result_as_json,
161+
redirect_output = bpy.context.window_manager.bcp_redirect_output
162+
bcp_share_environ = bpy.context.window_manager.bcp_share_environ
151163
except AttributeError:
152164
# ---- properties are missing
153165
raise AttributeError("Properties are not registered. "
154166
"Run 'register_properties' function before opening the port")
155167
# ---- If properties are working, then re-raise an exception
156168
raise e
157-
self.command_port.start()
158169
print("Command port opened")
159170

160-
def __del__(self):
161-
self.close_port()
162-
del bpy.context.window_manager.keep_command_port_running
163-
164171
def check_property(self):
165-
if not bpy.context.window_manager.keep_command_port_running:
172+
if not CommandPortOperator.keep_command_port_running:
166173
self.close_port()
167174

168175
def close_port(self):
169176
if self.timer is not None:
170177
bpy.context.window_manager.event_timer_remove(self.timer)
171178
print("Waiting for command port thread to end....")
172179
self.command_port.do_run = False
173-
while self.command_port.isAlive():
180+
while self.command_port.is_alive():
174181
pass
175182
print("Command port thread was stopped.")
176183

177184
def execute(self, context):
178-
if not self.command_port.isAlive():
185+
if not self.command_port.is_alive():
179186
return {'FINISHED'}
180187
try:
181188
command = self.command_port.commands_queue.get_nowait()
@@ -210,23 +217,19 @@ def modal(self, context, event):
210217

211218
def invoke(self, context, event):
212219
try:
213-
bpy.context.window_manager.keep_command_port_running = True
220+
CommandPortOperator.keep_command_port_running = True
214221
except AttributeError:
215222
# --- Registering it here, because it has to run "check_property method
216223
bpy.types.WindowManager.keep_command_port_running = bpy.props.BoolProperty(
217224
name="My Property",
218225
update=lambda o, c: self.check_property()
219226
)
220-
bpy.context.window_manager.keep_command_port_running = True
227+
CommandPortOperator.keep_command_port_running = True
221228
self.execute(context)
222229
context.window_manager.modal_handler_add(self)
223230
return {'RUNNING_MODAL'}
224231

225232

226-
def open_command_port():
227-
bpy.ops.object.command_port('INVOKE_DEFAULT')
228-
229-
230233
def register(queue_size=0, timeout=.1, port=5000, buffersize=4096, max_connections=5,
231234
return_result=True, result_as_json=False, redirect_output=True, share_environ=True):
232235
"""
@@ -252,34 +255,34 @@ def register(queue_size=0, timeout=.1, port=5000, buffersize=4096, max_connectio
252255
:param share_environ: Indicates if executed commands should operate on new dict instance, or os.environ of program
253256
:type share_environ: bool
254257
"""
255-
Scene.bcp_queue_size: IntProperty() = IntProperty(default=queue_size,
258+
bpy.types.WindowManager.bcp_queue_size: IntProperty() = IntProperty(default=queue_size,
256259
name="Queue size",
257260
description="Size of commands queue: max number of "
258261
"commands that are qaiting to be executed. "
259262
"0 == no limit", )
260-
Scene.bcp_timeout: FloatProperty() = FloatProperty(default=timeout,
263+
bpy.types.WindowManager.bcp_timeout: FloatProperty() = FloatProperty(default=timeout,
261264
name="Timeout",
262265
description="Maximum connection timeout, in seconds")
263-
Scene.bcp_port: IntProperty = IntProperty(default=port,
266+
bpy.types.WindowManager.bcp_port: IntProperty = IntProperty(default=port,
264267
name="Port",
265268
description="Port for the socket")
266-
Scene.bcp_buffersize: IntProperty = IntProperty(default=buffersize,
269+
bpy.types.WindowManager.bcp_buffersize: IntProperty = IntProperty(default=buffersize,
267270
name="Buffersize",
268271
description="Buffersize, in bytes, for socket")
269-
Scene.bcp_max_connections: IntProperty = IntProperty(default=max_connections,
272+
bpy.types.WindowManager.bcp_max_connections: IntProperty = IntProperty(default=max_connections,
270273
name="Max connections",
271274
description="\"backlog\" parameter of socket \"listen\" method")
272-
Scene.bcp_return_result: BoolProperty = BoolProperty(default=return_result,
275+
bpy.types.WindowManager.bcp_return_result: BoolProperty = BoolProperty(default=return_result,
273276
name="Return result",
274277
description="Indicates if result of command should be returned")
275-
Scene.bcp_result_as_json: BoolProperty = BoolProperty(default=result_as_json,
278+
bpy.types.WindowManager.bcp_result_as_json: BoolProperty = BoolProperty(default=result_as_json,
276279
name="Result as json",
277280
description="Indicates if result of command should be "
278281
"returned as a json string")
279-
Scene.bcp_redirect_output: BoolProperty = BoolProperty(default=redirect_output,
282+
bpy.types.WindowManager.bcp_redirect_output: BoolProperty = BoolProperty(default=redirect_output,
280283
name="Redirect output",
281284
description="Indicates if output should be copied and sent")
282-
Scene.bcp_share_environ: BoolProperty = BoolProperty(default=share_environ,
285+
bpy.types.WindowManager.bcp_share_environ: BoolProperty = BoolProperty(default=share_environ,
283286
name="Share environment",
284287
description="Indicates if current environment should share an "
285288
"application environment,\n"
@@ -291,17 +294,16 @@ def register(queue_size=0, timeout=.1, port=5000, buffersize=4096, max_connectio
291294
"and forcommands executed later.")
292295
bpy.utils.register_class(CommandPortOperator)
293296

294-
295297
def unregister():
296-
del Scene.bcp_queue_size
297-
del Scene.bcp_timeout
298-
del Scene.bcp_port
299-
del Scene.bcp_buffersize
300-
del Scene.bcp_max_connections
301-
del Scene.bcp_return_result
302-
del Scene.bcp_result_as_json
303-
del Scene.bcp_redirect_output
304-
del Scene.bcp_share_environ
298+
del bpy.types.WindowManager.bcp_queue_size
299+
del bpy.types.WindowManager.bcp_timeout
300+
del bpy.types.WindowManager.bcp_port
301+
del bpy.types.WindowManager.bcp_buffersize
302+
del bpy.types.WindowManager.bcp_max_connections
303+
del bpy.types.WindowManager.bcp_return_result
304+
del bpy.types.WindowManager.bcp_result_as_json
305+
del bpy.types.WindowManager.bcp_redirect_output
306+
del bpy.types.WindowManager.bcp_share_environ
305307
bpy.utils.unregister_class(CommandPortOperator)
306308

307309

execute_file_in_blender.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def send_command(command, host='localhost', port=None):
1010
port = sys.argv[2]
1111

1212
clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
13-
e = Exception
13+
e = ConnectionError("Max retries reached!")
1414
for i in range(3):
1515
# Retry 3 times, wait from 0 to .5 s. before retries, raise last Exception, if no success
1616
# noinspection PyBroadException

tools.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import socket
22
import bpy
3+
from command_port import CommandPortOperator
34

45

56
def queue_command(command, buffersize=None, port=None):
@@ -17,9 +18,9 @@ def queue_command(command, buffersize=None, port=None):
1718
:type port: int
1819
"""
1920
if port is None:
20-
port = bpy.Scene.bcp_port
21+
port = bpy.context.window_manager.bcp_port
2122
if buffersize is None:
22-
buffersize = bpy.Scene.bcp_buffersize
23+
buffersize = bpy.context.window_manager.bcp_buffersize
2324

2425
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2526
soc.connect(("127.0.0.1", port))
@@ -29,16 +30,3 @@ def queue_command(command, buffersize=None, port=None):
2930
result_string = result_bytes.decode("ascii")
3031

3132
print("Result from server is {}".format(result_string))
32-
33-
34-
def close_command_port():
35-
try:
36-
if not bpy.context.window_manager.keep_command_port_running:
37-
print("Port is not running")
38-
return False
39-
bpy.context.window_manager.keep_command_port_running = False
40-
print("Command port closed")
41-
return True
42-
except NameError:
43-
print("Port is not running. It was never initialized.")
44-
return False

0 commit comments

Comments
 (0)