Asynchronous IPC: Initial implementation
This commit adds a mixin which sets up a modal operator which: * Starts a subprocess * Polls for messages from that subprocesses
This commit is contained in:
12
__init__.py
12
__init__.py
@@ -9,11 +9,18 @@ import bpy
|
|||||||
class PackageSettings(bpy.types.PropertyGroup):
|
class PackageSettings(bpy.types.PropertyGroup):
|
||||||
url = bpy.props.StringProperty(name="URL")
|
url = bpy.props.StringProperty(name="URL")
|
||||||
|
|
||||||
|
# class PackageManager:
|
||||||
|
# # For some reason accessing 'settings' PointerProperty via wm.package_manager.settings gives a value error
|
||||||
|
# # but accessing it when not stored in this class (wm.package_manager_settings) is fine?
|
||||||
|
# # settings = bpy.props.PointerProperty(type=PackageSettings)
|
||||||
|
# pipes = []
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
from . import (pkg_ops, pkg_ui)
|
from . import (pkg_ops, pkg_ui)
|
||||||
|
|
||||||
bpy.utils.register_class(PackageSettings)
|
bpy.utils.register_class(PackageSettings)
|
||||||
bpy.types.WindowManager.PackageManagerSettings = bpy.props.PointerProperty(type=PackageSettings)
|
# bpy.types.WindowManager.package_manager = PackageManager()
|
||||||
|
bpy.types.WindowManager.package_manager_settings = bpy.props.PointerProperty(type=PackageSettings)
|
||||||
pkg_ops.register()
|
pkg_ops.register()
|
||||||
pkg_ui.register()
|
pkg_ui.register()
|
||||||
|
|
||||||
@@ -22,5 +29,6 @@ def unregister():
|
|||||||
|
|
||||||
pkg_ops.unregister()
|
pkg_ops.unregister()
|
||||||
pkg_ui.unregister();
|
pkg_ui.unregister();
|
||||||
del bpy.types.WindowManager.PackageManagerSettings
|
bpy.utils.unregister_class(PackageSettings)
|
||||||
|
del bpy.types.WindowManager.package_manager
|
||||||
|
|
||||||
|
@@ -21,10 +21,20 @@ SCHEMA_VERSION = 1
|
|||||||
class BadAddon(Exception):
|
class BadAddon(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def fetch(url):
|
def fetch(url, pipe):
|
||||||
# TODO: do conditional request
|
pipe[0].close()
|
||||||
re = requests.get(url)
|
try:
|
||||||
print(re.json())
|
# TODO: do conditional request
|
||||||
|
re = requests.get(url)
|
||||||
|
# print(re.json())
|
||||||
|
pipe[1].send(re.headers)
|
||||||
|
finally:
|
||||||
|
pipe[1].close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# class Package():
|
||||||
|
# def __init__(self):
|
||||||
|
|
||||||
|
|
||||||
def parse_blinfo(source: str) -> dict:
|
def parse_blinfo(source: str) -> dict:
|
||||||
|
69
pkg_ops.py
69
pkg_ops.py
@@ -1,21 +1,72 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from multiprocessing import Process
|
import logging
|
||||||
|
from multiprocessing import Process, Pipe
|
||||||
from . import blenderpack
|
from . import blenderpack
|
||||||
|
|
||||||
class PACKAGE_OT_fetch_lists(bpy.types.Operator):
|
class SubprocessOperatorMixin:
|
||||||
bl_idname = "package.fetch_lists"
|
timer = None
|
||||||
bl_label = "Update package list(s)"
|
|
||||||
|
# run once in invoke
|
||||||
|
def setup(self):
|
||||||
|
pass
|
||||||
|
# run on receipt of data from subprocess
|
||||||
|
def handle_response(self, resp):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.pipe = Pipe()
|
||||||
|
self.subprocess = None
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
return self.invoke(context, None)
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
self.subprocess.start()
|
||||||
|
# we have to explicitly close the end of the pipe we are NOT using,
|
||||||
|
# otherwise no exception will be generated when the other process closes its end.
|
||||||
|
self.pipe[1].close()
|
||||||
|
|
||||||
|
wm = context.window_manager
|
||||||
|
wm.modal_handler_add(self)
|
||||||
|
self.timer = wm.event_timer_add(.01, context.window)
|
||||||
|
|
||||||
|
self.setup()
|
||||||
|
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
def modal(self, context, event):
|
||||||
|
print("polling")
|
||||||
|
try:
|
||||||
|
if self.pipe[0].poll():
|
||||||
|
newdata = self.pipe[0].recv()
|
||||||
|
else:
|
||||||
|
newdata = None
|
||||||
|
except EOFError:
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
if newdata is not None:
|
||||||
|
self.handle_response(newdata)
|
||||||
|
return {'PASS_THROUGH'}
|
||||||
|
|
||||||
|
class PACKAGE_OT_fetch(SubprocessOperatorMixin, bpy.types.Operator):
|
||||||
|
bl_idname = "package.fetch"
|
||||||
|
bl_label = "Update package list(s)"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
SubprocessOperatorMixin.__init__(self)
|
||||||
|
settings = bpy.context.window_manager.package_manager_settings
|
||||||
|
self.subprocess = Process(target=blenderpack.fetch, args=(settings.url, self.pipe))
|
||||||
|
|
||||||
|
def handle_response(self, resp):
|
||||||
|
print("your response:", resp)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
settings = context.window_manager.PackageManagerSettings
|
|
||||||
proc = Process(target=lambda: blenderpack.fetch(settings.url))
|
|
||||||
proc.start()
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
bpy.utils.register_class(PACKAGE_OT_fetch_lists)
|
bpy.utils.register_class(PACKAGE_OT_fetch)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.utils.unregister_class(PACKAGE_OT_fetch_lists)
|
bpy.utils.unregister_class(PACKAGE_OT_fetch)
|
||||||
|
|
||||||
|
@@ -16,8 +16,8 @@ class USERPREF_PT_packages(bpy.types.Panel):
|
|||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(context.window_manager.PackageManagerSettings, "url")
|
row.prop(context.window_manager.package_manager_settings, "url")
|
||||||
row.operator(pkg_ops.PACKAGE_OT_fetch_lists.bl_idname, text="Fetch")
|
row.operator(pkg_ops.PACKAGE_OT_fetch.bl_idname, text="Fetch")
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
bpy.utils.register_class(USERPREF_PT_packages)
|
bpy.utils.register_class(USERPREF_PT_packages)
|
||||||
|
Reference in New Issue
Block a user