comparison blender/time_sync/time_from_graph.py @ 2454:405abed9a45c

fix up asyncio-in-bg-thread sorcery
author drewp@bigasterisk.com
date Mon, 19 May 2025 21:25:32 -0700
parents b23afde50bc2
children 2d454737a916
comparison
equal deleted inserted replaced
2453:b23afde50bc2 2454:405abed9a45c
1 import asyncio
1 import threading 2 import threading
2 import time 3 import time
4 from typing import Coroutine, cast
3 5
4 import bpy # type: ignore 6 import bpy # type: ignore
5 from bpy.app.handlers import persistent # type: ignore 7 from bpy.app.handlers import persistent # type: ignore
6 from light9_sync.asyncio_thread import startLoopInThread 8 from ..asyncio_thread import startLoopInThread
7 from rdfdb.syncedgraph.syncedgraph import SyncedGraph 9 from rdfdb.syncedgraph.syncedgraph import SyncedGraph
10 from rdflib import URIRef
11 from rdflib.graph import _ContextType
8 12
9 from light9 import networking 13 from light9 import networking, showconfig
10 from light9.namespaces import L9 14 from light9.namespaces import L9
15 from light9.newtypes import decimalLiteral
11 from light9.run_local import log 16 from light9.run_local import log
12 from light9.typedgraph import typedValue 17 from light9.typedgraph import typedValue
13 18
14 19
15 def clamp(lo, hi, x): 20 def clamp(lo, hi, x):
29 34
30 def __init__(self): 35 def __init__(self):
31 # main thread 36 # main thread
32 self.lastSetFrame = -1 37 self.lastSetFrame = -1
33 self.lastGraphFrame = -1 38 self.lastGraphFrame = -1
34 startLoopInThread(self.task()) 39 show = showconfig.showUri()
40 self.ctx = cast(_ContextType, (URIRef(show + '/ascoltami')))
41 log.debug('🚋3 startLoopInThread')
42 self._loop = startLoopInThread(self.task())
43
35 # need persistent because `blender --addons ...` seems to start addon 44 # need persistent because `blender --addons ...` seems to start addon
36 # before loading scene. 45 # before loading scene.
37 bpy.app.timers.register(self.update, persistent=True) 46 bpy.app.timers.register(self.update, persistent=True)
38 bpy.app.handlers.frame_change_post.append(self.on_frame_change_post) 47 bpy.app.handlers.frame_change_post.append(self.on_frame_change_post)
48 log.info('🚋10 Sync initd')
39 49
40 ## updates from graph -> self 50 ## updates from graph -> self
41 51
52 def runInBackgroundLoop(self, f: Coroutine):
53 asyncio.run_coroutine_threadsafe(f, self._loop)
54
55
42 async def task(self): 56 async def task(self):
43 # bg thread with asyncio loop 57 # bg thread with asyncio loop
58 log.info('🚋11 start SyncedGraph')
44 self.graph = SyncedGraph(networking.rdfdb.url, "time_sync") 59 self.graph = SyncedGraph(networking.rdfdb.url, "time_sync")
45 self.graph.addHandler(self.syncFromGraph) 60 self.graph.addHandler(self.syncFromGraph)
46 61
47 def syncFromGraph(self): 62 def syncFromGraph(self):
48 # bg thread 63 # bg thread
61 log.warn('no time data') 76 log.warn('no time data')
62 return None 77 return None
63 78
64 ## graph time -> blender time 79 ## graph time -> blender time
65 80
66 # @persistent 81 @persistent
67 def update(self): 82 def update(self):
68 # main thread? wherever blender runs timers 83 # main thread? wherever blender runs timers
69 if self.playing: 84 if self.playing:
70 with self.lock: 85 with self.lock:
71 t = self.currentTime() 86 t = self.currentTime()
88 def on_frame_change_post(self, scene, deps): 103 def on_frame_change_post(self, scene, deps):
89 if scene.frame_current != self.lastSetFrame: 104 if scene.frame_current != self.lastSetFrame:
90 # self.setGraphTime(scene.frame_current / scene.render.fps, self.duration) 105 # self.setGraphTime(scene.frame_current / scene.render.fps, self.duration)
91 self.lastSetFrame = scene.frame_current 106 self.lastSetFrame = scene.frame_current
92 t = scene.frame_current / scene.render.fps 107 t = scene.frame_current / scene.render.fps
93 self.setInGraph(t) 108 self.runInBackgroundLoop(self.setInGraph(t, bpy.context.screen.is_animation_playing))
94 109
95 def setInGraph(self, t: float): 110 async def setInGraph(self, t: float, isBlenderPlaying: bool):
96 log.warning(f'todo: set graph to {t:.2f}') 111 log.info(f'set graph time to {t:.2f}')
112 if isBlenderPlaying:
113 log.info(' playing mode')
114 p = self.graph.getObjectPatch(self.ctx, L9['ascoltami'], L9['wallStartTime'], decimalLiteral(t))
115 else:
116 log.info(' paused mode')
117 p = self.graph.getObjectPatch(self.ctx, L9['ascoltami'], L9['pausedSongTime'], decimalLiteral(t))
118
119 await self.graph.patch(p)