少し前なのですが、こちらの続きです。
マウスカーソル位置を表現したい1 - C#ATIA
どうしても気になっていたので。
最近読んだ本で、JavascriptのsetTimeoutの意味合いを知りました。
ひょっとしたら知らない間に使っているかも・・・。
前回のクラッシュの原因は、イベントが短時間に発生し過ぎる事と
感じていたので、正にsetTimeoutが合致すると。
pythonではどうするの? と思い探した所、こちらを見つけました。
pythonでJSのsetTimeoutみたいなこと - Qiita
・・・asyncio理解出来ませんでした。
Fusion360のイベントの発生自体を制御する事は難しいですが、
イベントハンドラー内の処理を何らかの方法で制御すれば良い
はず。
極論、非同期処理でフラッグを切り替えちゃえば良いのでは?
と思い、threadingを利用する事にしました。
この様な感じです。
# Fusion360API Python script import traceback import adsk.fusion import adsk.core import threading _app: adsk.core.Application = None _ui: adsk.core.UserInterface = None _handlers = [] _selIpt: adsk.core.SelectionCommandInput = None _cgFact: 'CustomGraphicsFactry' = None # スレッド停止用 _stopFlag = None # イベントを有効にさせる間隔(単位 秒) EVENT_TIMER = 0.5 # イベントを有効にするフラッグ _eventCoordination = False class MyThread(threading.Thread): def __init__(self, event, timer): threading.Thread.__init__(self) self.stopped = event self.timer = timer def run(self): while not self.stopped.wait(self.timer): global _eventCoordination _eventCoordination = False adsk.core.Application.get().log('-----Thread-----') class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler): def __init__(self): super().__init__() def notify(self, args: adsk.core.CommandCreatedEventArgs): adsk.core.Application.get().log(args.firingEvent.name) try: global _handlers cmd: adsk.core.Command = adsk.core.Command.cast(args.command) inputs: adsk.core.CommandInputs = cmd.commandInputs onDestroy = MyCommandDestroyHandler() cmd.destroy.add(onDestroy) _handlers.append(onDestroy) onExecute = MyExecuteHandler() cmd.execute.add(onExecute) _handlers.append(onExecute) onPreSelect = MyPreSelectHandler() # cmd.preSelect.add(onPreSelect) cmd.preSelectMouseMove.add(onPreSelect) _handlers.append(onPreSelect) global _selIpt _selIpt = inputs.addSelectionInput( 'selIptId', 'select', 'select' ) global _cgFact _cgFact = CustomGraphicsFactry('test') # イベント抑制用のスレッド global _stopFlag _stopFlag = threading.Event() myThread = MyThread(_stopFlag, EVENT_TIMER) myThread.start() except: _ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) class MyExecuteHandler(adsk.core.CommandEventHandler): def __init__(self): super().__init__() def notify(self, args: adsk.core.CommandEventArgs): adsk.core.Application.get().log(args.firingEvent.name) class MyCommandDestroyHandler(adsk.core.CommandEventHandler): def __init__(self): super().__init__() def notify(self, args: adsk.core.CommandEventArgs): adsk.core.Application.get().log(args.firingEvent.name) try: global _stopFlag _stopFlag.set() except: pass adsk.terminate() def run(context): try: global _app, _ui _app = adsk.core.Application.get() _ui = _app.userInterface cmdDef: adsk.core.CommandDefinition = _ui.commandDefinitions.itemById( 'test_cmd' ) if not cmdDef: cmdDef = _ui.commandDefinitions.addButtonDefinition( 'test_cmd', 'Test', 'Test' ) global _handlers onCommandCreated = MyCommandCreatedHandler() cmdDef.commandCreated.add(onCommandCreated) _handlers.append(onCommandCreated) cmdDef.execute() adsk.autoTerminate(False) except: if _ui: _ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) class MyPreSelectHandler(adsk.core.SelectionEventHandler): def __init__(self): super().__init__() self.count = 1 def notify(self, args: adsk.core.SelectionEventArgs): adsk.core.Application.get().log(f'{args.firingEvent.name}:{self.count}') self.count += 1 global _eventCoordination if _eventCoordination: return adsk.core.Application.get().log('*** event on') tmpMgr: adsk.fusion.TemporaryBRepManager = adsk.fusion.TemporaryBRepManager.get() entity = args.selection.entity if not entity.objectType == adsk.fusion.BRepBody.objectType: if hasattr(entity, 'body'): entity = entity.body else: return clone = tmpMgr.copy(entity) tmpMgr.booleanOperation( clone, tmpMgr.createSphere( args.selection.point, 0.5 ), adsk.fusion.BooleanTypes.IntersectionBooleanType ) global _cgFact _cgFact.updateBody(clone) _eventCoordination = True class CustomGraphicsFactry(): def __init__( self, id :str,): self.app: adsk.core.Application = adsk.core.Application.get() self.cgGroup: adsk.fusion.CustomGraphicsGroup = None self.color = adsk.fusion.CustomGraphicsSolidColorEffect.create( adsk.core.Color.create(255,0,0,255) ) self.id = id self.refreshCG() def __del__( self): self.removeCG() def removeCG( self): des :adsk.fusion.Design = self.app.activeProduct cgs = [cmp.customGraphicsGroups for cmp in des.allComponents] cgs = [cg for cg in cgs if cg.count > 0] if len(cgs) < 1: return for cg in cgs: gps = [c for c in cg] gps.reverse() for gp in gps: if not hasattr(gp, 'id'): continue if not gp.id == self.id: continue gp.deleteMe() def refreshCG( self ): self.removeCG() des :adsk.fusion.Design = self.app.activeProduct comp :adsk.fusion.Component = des.activeComponent self._cgGroup: adsk.fusion.CustomGraphicsGroup = comp.customGraphicsGroups.add() self._cgGroup.id = self.id def updateBody( self, body: adsk.fusion.BRepBody, isRefresh :bool = True ): if isRefresh: self.refreshCG() cgBody: adsk.fusion.CustomGraphicsBRepBody = self._cgGroup.addBRepBody(body) cgBody.color = self.color
こちらは前回の状態です。あっさりクラッシュします。
こちらはスレッドで抑え込んだ方です。
赤い点がマウスの動きに対して少し遅れ気味で、
処理を間引いている事を感じます。
結構粘りましたが、結局はクラッシュしました。
イケルと思ってたんですけどね。ハンドラー内の処理が重すぎかな?