class EventPlayer(object): def __init__(self, playback_speed=None, comment_display=None): self._playback_speed = playback_speed self._timer = Timer() self._timer.unpause() if comment_display is None: self._comment_display = self._default_comment_display else: self._comment_display = comment_display def play_script(self, path, finish_callback=None): """ Start execution of the given script in a separate thread and return immediately. Note: You should handle any exceptions from the playback script via sys.execpthook. """ _globals = {} _locals = {} """ Calls to events in the playback script like: player.post_event(obj,PyQt4.QtGui.QMouseEvent(...),t) are/were responsible for the xcb-error on Ubuntu, because you may not use a Gui-object from a thread other than the MainThread running the Gui """ execfile(path, _globals, _locals) def run(): _locals['playback_events'](player=self) if finish_callback is not None: finish_callback() th = threading.Thread(target=run) th.daemon = True th.start() def post_event(self, obj, event, timestamp_in_seconds): if self._playback_speed is not None: self._timer.sleep_until(timestamp_in_seconds / self._playback_speed) assert threading.current_thread().name != "MainThread" event.spont = True QApplication.postEvent(obj, event) assert QApplication.instance().thread() == obj.thread() flusher = EventFlusher() flusher.moveToThread(obj.thread()) flusher.setParent(QApplication.instance()) signaler = Signaler() signaler.sig.connect(flusher.set, Qt.QueuedConnection) signaler.sig.emit() flusher.wait() flusher.clear() def display_comment(self, comment): self._comment_display(comment) def _default_comment_display(self, comment): print "--------------------------------------------------" print comment print "--------------------------------------------------"
def __init__(self, parent=None, ignore_parent_events=True): QObject.__init__(self, parent=parent) self._ignore_parent_events = False if parent is not None and ignore_parent_events: self._ignore_parent_events = True self._parent_name = get_fully_qualified_name(parent) self._captured_events = [] self._timer = Timer()
def __init__(self, playback_speed=None, comment_display=None): self._playback_speed = playback_speed self._timer = Timer() self._timer.unpause() if comment_display is None: self._comment_display = self._default_comment_display else: self._comment_display = comment_display
class EventPlayer(object): def __init__(self, playback_speed=None, comment_display=None): self._playback_speed = playback_speed self._timer = Timer() self._timer.unpause() if comment_display is None: self._comment_display = self._default_comment_display else: self._comment_display = comment_display def play_script(self, path, finish_callback=None): """ Start execution of the given script in a separate thread and return immediately. Note: You should handle any exceptions from the playback script via sys.execpthook. """ _globals = {} _locals = {} """ Calls to events in the playback script like: player.post_event(obj,PyQt4.QtGui.QMouseEvent(...),t) are/were responsible for the xcb-error on Ubuntu, because you may not use a Gui-object from a thread other than the MainThread running the Gui """ execfile(path, _globals, _locals) def run(): _locals['playback_events'](player=self) if finish_callback is not None: finish_callback() th = threading.Thread( target=run ) th.daemon = True th.start() def post_event(self, obj, event, timestamp_in_seconds): if self._playback_speed is not None: self._timer.sleep_until(timestamp_in_seconds / self._playback_speed) assert threading.current_thread().name != "MainThread" event.spont = True QApplication.postEvent(obj, event) assert QApplication.instance().thread() == obj.thread() flusher = EventFlusher() flusher.moveToThread( obj.thread() ) flusher.setParent( QApplication.instance() ) signaler = Signaler() signaler.sig.connect( flusher.set, Qt.QueuedConnection ) signaler.sig.emit() flusher.wait() flusher.clear() def display_comment(self, comment): self._comment_display(comment) def _default_comment_display(self, comment): print "--------------------------------------------------" print comment print "--------------------------------------------------"
def impl(): workflow = self.shell.projectManager.workflow pixClassApplet = workflow.pcApplet gui = pixClassApplet.getMultiLaneGui() # Clear all the labels while len(gui.currentGui()._labelControlUi.labelListModel) > 0: gui.currentGui()._labelControlUi.labelListModel.removeRow(0) # Re-add all labels self.test_4_AddLabels() # Make sure the entire slice is visible viewMenu = gui.currentGui().menus()[0] viewMenu.actionFitToScreen.trigger() with Timer() as timer: # Enable interactive mode assert gui.currentGui( )._labelControlUi.liveUpdateButton.isChecked() == False gui.currentGui()._labelControlUi.liveUpdateButton.click() # Do to the way we wait for the views to finish rendering, the GUI hangs while we wait. self.waitForViews(gui.currentGui().editor.imageViews) logger.debug("Interactive Mode Rendering Time: {}".format( timer.seconds())) # Disable iteractive mode. gui.currentGui()._labelControlUi.liveUpdateButton.click() self.waitForViews(gui.currentGui().editor.imageViews)
def execute(self, slot, subindex, ignored_roi, result): configFilePath = self.ConfigFilePath.value config = parseClusterConfigFile(configFilePath) blockwiseFileset = self._primaryBlockwiseFileset # Check axis compatibility inputAxes = self.Input.meta.getTaggedShape().keys() outputAxes = list(blockwiseFileset.description.axes) assert set(inputAxes) == set(outputAxes), \ "Output dataset has the wrong set of axes. Input axes: {}, Output axes: {}".format( "".join(inputAxes), "".join(outputAxes) ) roiString = self.RoiString.value roi = Roi.loads(roiString) if len(roi.start) != len(self.Input.meta.shape): assert False, "Task roi: {} is not valid for this input. Did the master launch this task correctly?".format( roiString) logger.info("Executing for roi: {}".format(roi)) if config.use_node_local_scratch: assert False, "FIXME." assert ( blockwiseFileset.getEntireBlockRoi(roi.start)[1] == roi.stop ).all( ), "Each task must execute exactly one full block. ({},{}) is not a valid block roi.".format( roi.start, roi.stop) assert self.Input.ready() # Convert the task subrequest shape dict into a shape for this dataset (and axisordering) subrequest_shape = map( lambda tag: config.task_subrequest_shape[tag.key], self.Input.meta.axistags) primary_subrequest_shape = self._primaryBlockwiseFileset.description.sub_block_shape if primary_subrequest_shape is not None: # If the output dataset specified a sub_block_shape, override the cluster config subrequest_shape = primary_subrequest_shape with Timer() as computeTimer: # Stream the data out to disk. streamer = BigRequestStreamer(self.Input, (roi.start, roi.stop), subrequest_shape, config.task_parallel_subrequests) streamer.progressSignal.subscribe(self.progressSignal) streamer.resultSignal.subscribe(self._handlePrimaryResultBlock) streamer.execute() # Now the block is ready. Update the status. blockwiseFileset.setBlockStatus(roi.start, BlockwiseFileset.BLOCK_AVAILABLE) logger.info("Finished task in {} seconds".format( computeTimer.seconds())) result[0] = True return result
def impl(): workflow = self.shell.projectManager.workflow pixClassApplet = workflow.pcApplet gui = pixClassApplet.getMultiLaneGui() with Timer() as timer: gui.currentGui().editor.posModel.slicingPos = (0,0,1) # Do to the way we wait for the views to finish rendering, the GUI hangs while we wait. self.waitForViews(gui.currentGui().editor.imageViews) logger.debug("New Slice Rendering Time: {}".format( timer.seconds() ))
def setInSlot(self, slot, subindex, roi, value): key = roi.toSlice() if slot == self.WriteSeeds: with Timer() as timer: print "Writing seeds to label array" self.opLabelArray.LabelSinkInput[roi.toSlice()] = value print "Writing seeds to label array took {} seconds".format( timer.seconds() ) assert self._mst is not None # Important: mst.seeds will requires erased values to be 255 (a.k.a -1) value[:] = numpy.where(value == 100, 255, value) with Timer() as timer: print "Writing seeds to MST" if hasattr(key, '__len__'): self._mst.seeds[key[1:4]] = value else: self._mst.seeds[key] = value print "Writing seeds to MST took {} seconds".format( timer.seconds() ) self.has_seeds = True else: raise RuntimeError("unknown slots")
def setupClass(cls): # Base class first super(TestPixelClassificationGui, cls).setupClass() if hasattr(cls, 'SAMPLE_DATA'): cls.using_random_data = False else: cls.using_random_data = True cls.SAMPLE_DATA = os.path.split(__file__)[0] + '/random_data.npy' data = numpy.random.random((1, 200, 200, 50, 1)) data *= 256 numpy.save(cls.SAMPLE_DATA, data.astype(numpy.uint8)) # Start the timer cls.timer = Timer() cls.timer.unpause()
def impl(): workflow = self.shell.projectManager.workflow pixClassApplet = workflow.pcApplet gui = pixClassApplet.getMultiLaneGui() # Make sure the entire slice is visible gui.currentGui().menuGui.actionFitToScreen.trigger() with Timer() as timer: # Enable interactive mode assert gui.currentGui()._labelControlUi.liveUpdateButton.isChecked() == False gui.currentGui()._labelControlUi.liveUpdateButton.click() # Do to the way we wait for the views to finish rendering, the GUI hangs while we wait. self.waitForViews(gui.currentGui().editor.imageViews) logger.debug("Interactive Mode Rendering Time: {}".format( timer.seconds() ))
def _buildDone(self): """ Builds the done segmentation anew, for example after saving an object or deleting an object. """ if self._mst is None: return with Timer() as timer: self._done_lut = numpy.zeros(len(self._mst.objects.lut), dtype=numpy.int32) self._done_seg_lut = numpy.zeros(len(self._mst.objects.lut), dtype=numpy.int32) print "building 'done' luts" for name, objectSupervoxels in self._mst.object_lut.iteritems(): if name == self._currObjectName: continue self._done_lut[objectSupervoxels] += 1 assert name in self._mst.object_names, "%s not in self._mst.object_names, keys are %r" % (name, self._mst.object_names.keys()) self._done_seg_lut[objectSupervoxels] = self._mst.object_names[name] print "building the 'done' luts took {} seconds".format( timer.seconds() )
def execute(self, slot, subindex, roi, result): assert roi.stop - roi.start == self.Output.meta.shape, "Watershed must be run on the entire volume." input_image = self.Input(roi.start, roi.stop).wait() volume_feat = input_image[0, ..., 0] result_view = result[0, ..., 0] with Timer() as watershedTimer: if self.Input.meta.getTaggedShape()['z'] > 1: sys.stdout.write("Watershed...") sys.stdout.flush() #result_view[...] = vigra.analysis.watersheds(volume_feat[:,:])[0].astype(numpy.int32) result_view[...] = vigra.analysis.watersheds( volume_feat[:, :].astype(numpy.uint8))[0] print "done", numpy.max(result[...]) else: sys.stdout.write("Watershed...") sys.stdout.flush() labelVolume = vigra.analysis.watersheds( volume_feat[:, :, 0])[0].view(dtype=numpy.int32) result_view[...] = labelVolume[:, :, numpy.newaxis] print "done", numpy.max(labelVolume) print "Watershed took {} seconds".format(watershedTimer.seconds()) return result
class EventRecorder( QObject ): """ Records spontaneous events from the UI and serializes them as strings that can be evaluated in Python. """ def __init__(self, parent=None, ignore_parent_events=True): QObject.__init__(self, parent=parent) self._ignore_parent_events = False if parent is not None and ignore_parent_events: self._ignore_parent_events = True self._parent_name = get_fully_qualified_name(parent) self._captured_events = [] self._timer = Timer() @property def paused(self): return self._timer.paused QEvent_Style = 91 IgnoredEventTypes = set( [ QEvent.Paint, QEvent.KeyboardLayoutChange, QEvent.WindowActivate, QEvent.WindowDeactivate, QEvent.ActivationChange, # These event symbols are not exposed in pyqt, so we pull them from our own enum EventTypes.Style, EventTypes.ApplicationActivate, EventTypes.ApplicationDeactivate, EventTypes.NonClientAreaMouseMove, EventTypes.NonClientAreaMouseButtonPress, EventTypes.NonClientAreaMouseButtonRelease, EventTypes.NonClientAreaMouseButtonDblClick ] ) IgnoredEventClasses = (QChildEvent, QTimerEvent, QGraphicsSceneMouseEvent, QWindowStateChangeEvent, QMoveEvent) def captureEvent(self, watched, event): if self._shouldSaveEvent(event): try: eventstr = event_to_string(event) except KeyError: logger.warn("Don't know how to record event: {}".format( str(event) )) print "Don't know", str(event) else: timestamp_in_seconds = self._timer.seconds() objname = str(get_fully_qualified_name(watched)) if not ( self._ignore_parent_events and objname.startswith(self._parent_name) ): self._captured_events.append( (eventstr, objname, timestamp_in_seconds) ) return False def insertComment(self, comment): self._captured_events.append( (comment, "comment", None) ) def _shouldSaveEvent(self, event): if isinstance(event, QMouseEvent): # Ignore most mouse movement events if the user isn't pressing anything. if event.type() == QEvent.MouseMove \ and int(event.button()) == 0 \ and int(event.buttons()) == 0 \ and int(event.modifiers()) == 0: # Somewhat hackish (and slow), but we have to record mouse movements during combo box usage. # Same for QMenu usage (on Mac, it doesn't seem to matter, but on Fedora it does matter.) widgetUnderCursor = QApplication.instance().widgetAt( QCursor.pos() ) if widgetUnderCursor is not None and widgetUnderCursor.objectName() == "qt_scrollarea_viewport": return has_ancestor(widgetUnderCursor, QComboBox) if isinstance(widgetUnderCursor, QMenu): return True return False else: return True # Ignore non-spontaneous events if not event.spontaneous(): return False if event.type() in self.IgnoredEventTypes: return False if isinstance(event, self.IgnoredEventClasses): return False return True def unpause(self): # Here, we use a special override of QApplication.notify() instead of using QApplication.instance().installEventFilter(). # That's because (contrary to the documentation), the QApplication eventFilter does NOT get to see every event in the application. # Testing shows that events that were "filtered out" by a different event filter may not be seen by the QApplication event filter. self._timer.unpause() def _notify(receiver, event): self.captureEvent(receiver, event) return _orig_QApp_notify(receiver, event) from ilastik.shell.gui.startShellGui import EventRecordingApp assert isinstance( QApplication.instance(), EventRecordingApp ) QApplication.instance()._notify =_notify def pause(self): self._timer.pause() QApplication.instance()._notify = _orig_QApp_notify def writeScript(self, fileobj): # Write header comments fileobj.write( """ # Event Recording # Started at: {} """.format( str(self._timer.start_time) ) ) # Write playback function definition fileobj.write( """ def playback_events(player): import PyQt4.QtCore from PyQt4.QtCore import Qt, QEvent, QPoint import PyQt4.QtGui from ilastik.utility.gui.eventRecorder.objectNameUtils import get_named_object from ilastik.utility.gui.eventRecorder.eventRecorder import EventPlayer from ilastik.shell.gui.startShellGui import shell player.display_comment("SCRIPT STARTING") """) # Write all events and comments for eventstr, objname, timestamp_in_seconds in self._captured_events: if objname == "comment": eventstr = eventstr.replace('\\', '\\\\') eventstr = eventstr.replace('"', '\\"') eventstr = eventstr.replace("'", "\\'") fileobj.write( """ ######################## player.display_comment(\"""{eventstr}\""") ######################## """.format( **locals() ) ) else: fileobj.write( """ obj = get_named_object( '{objname}' ) player.post_event( obj, {eventstr}, {timestamp_in_seconds} ) """.format( **locals() ) ) fileobj.write( """ player.display_comment("SCRIPT COMPLETE") """)
def execute(self, slot, subindex, roi, result): #make sure raw data is 5D: t,{x,y,z},c ax = self.Input.meta.axistags sh = self.Input.meta.shape assert len(ax) == 5 assert ax[0].key == "t" and sh[0] == 1 for i in range(1, 4): assert ax[i].isSpatial() assert ax[4].key == "c" and sh[4] == 1 volume5d = self.Input.value sigma = self.Sigma.value volume = volume5d[0, :, :, :, 0] result_view = result[0, :, :, :, 0] print "input volume shape: ", volume.shape print "input volume size: ", volume.nbytes / 1024**2, "MB" fvol = numpy.asarray(volume, numpy.float32) #Choose filter selected by user volume_filter = self.Filter.value print "applying filter", fvol.shape with Timer() as filterTimer: if fvol.shape[2] > 1: # true 3D volume if volume_filter == OpFilter.HESSIAN_BRIGHT: print "lowest eigenvalue of Hessian of Gaussian" result_view[ ...] = vigra.filters.hessianOfGaussianEigenvalues( fvol, sigma)[:, :, :, 2] result_view[:] = numpy.max(result_view) - result_view elif volume_filter == OpFilter.HESSIAN_DARK: print "greatest eigenvalue of Hessian of Gaussian" result_view[ ...] = vigra.filters.hessianOfGaussianEigenvalues( fvol, sigma)[:, :, :, 0] elif volume_filter == OpFilter.STEP_EDGES: print "Gaussian Gradient Magnitude" result_view[...] = vigra.filters.gaussianGradientMagnitude( fvol, sigma) elif volume_filter == OpFilter.RAW: print "Gaussian Smoothing" result_view[...] = vigra.filters.gaussianSmoothing( fvol, sigma) elif volume_filter == OpFilter.RAW_INVERTED: print "negative Gaussian Smoothing" result_view[...] = vigra.filters.gaussianSmoothing( -fvol, sigma) print "Filter took {} seconds".format(filterTimer.seconds()) else: # 2D Image fvol = fvol[:, :, 0] if volume_filter == OpFilter.HESSIAN_BRIGHT: print "lowest eigenvalue of Hessian of Gaussian" volume_feat = vigra.filters.hessianOfGaussianEigenvalues( fvol, sigma)[:, :, 1] volume_feat[:] = numpy.max(volume_feat) - volume_feat elif volume_filter == OpFilter.HESSIAN_DARK: print "greatest eigenvalue of Hessian of Gaussian" volume_feat = vigra.filters.hessianOfGaussianEigenvalues( fvol, sigma)[:, :, 0] elif volume_filter == OpFilter.STEP_EDGES: print "Gaussian Gradient Magnitude" volume_feat = vigra.filters.gaussianGradientMagnitude( fvol, sigma) elif volume_filter == OpFilter.RAW: print "Gaussian Smoothing" volume_feat = vigra.filters.gaussianSmoothing(fvol, sigma) elif volume_filter == OpFilter.RAW_INVERTED: print "negative Gaussian Smoothing" volume_feat = vigra.filters.gaussianSmoothing(-fvol, sigma) result_view[:, :, 0] = volume_feat print "Filter took {} seconds".format(filterTimer.seconds()) return result
def loadObject(self, name): print "want to load object with name = %s" % name if not self.hasObjectWithName(name): print " --> no such object '%s'" % name return False if self.hasCurrentObject(): self.saveCurrentObject() self._clearLabels() fgVoxels, bgVoxels = self.loadObject_impl(name) fg_bounding_box_start = numpy.array( map( numpy.min, fgVoxels ) ) fg_bounding_box_stop = 1 + numpy.array( map( numpy.max, fgVoxels ) ) bg_bounding_box_start = numpy.array( map( numpy.min, bgVoxels ) ) bg_bounding_box_stop = 1 + numpy.array( map( numpy.max, bgVoxels ) ) bounding_box_start = numpy.minimum( fg_bounding_box_start, bg_bounding_box_start ) bounding_box_stop = numpy.maximum( fg_bounding_box_stop, bg_bounding_box_stop ) bounding_box_slicing = roiToSlice( bounding_box_start, bounding_box_stop ) bounding_box_shape = tuple(bounding_box_stop - bounding_box_start) dtype = self.opLabelArray.Output.meta.dtype # Convert coordinates to be relative to bounding box fgVoxels = numpy.array(fgVoxels) fgVoxels = fgVoxels - numpy.array( [bounding_box_start] ).transpose() fgVoxels = list(fgVoxels) bgVoxels = numpy.array(bgVoxels) bgVoxels = bgVoxels - numpy.array( [bounding_box_start] ).transpose() bgVoxels = list(bgVoxels) with Timer() as timer: print "Loading seeds...." z = numpy.zeros(bounding_box_shape, dtype=dtype) print "Allocating seed array took {} seconds".format( timer.seconds() ) z[fgVoxels] = 2 z[bgVoxels] = 1 self.WriteSeeds[(slice(0,1),) + bounding_box_slicing + (slice(0,1),)] = z[numpy.newaxis, :,:,:, numpy.newaxis] print "Loading seeds took a total of {} seconds".format( timer.seconds() ) #restore the correct parameter values mst = self._mst assert name in mst.object_lut assert name in mst.object_seeds_fg_voxels assert name in mst.object_seeds_bg_voxels assert name in mst.bg_priority assert name in mst.no_bias_below assert name in mst.bg_priority assert name in mst.no_bias_below self.BackgroundPriority.setValue( mst.bg_priority[name] ) self.NoBiasBelow.setValue( mst.no_bias_below[name] ) self.updatePreprocessing() # The entire segmentation layer needs to be refreshed now. self.Segmentation.setDirty() return True
class EventRecorder(QObject): """ Records spontaneous events from the UI and serializes them as strings that can be evaluated in Python. """ def __init__(self, parent=None, ignore_parent_events=True): QObject.__init__(self, parent=parent) self._ignore_parent_events = False if parent is not None and ignore_parent_events: self._ignore_parent_events = True self._parent_name = get_fully_qualified_name(parent) self._captured_events = [] self._timer = Timer() @property def paused(self): return self._timer.paused QEvent_Style = 91 IgnoredEventTypes = set([ QEvent.Paint, QEvent.KeyboardLayoutChange, QEvent.WindowActivate, QEvent.WindowDeactivate, QEvent.ActivationChange, # These event symbols are not exposed in pyqt, so we pull them from our own enum EventTypes.Style, EventTypes.ApplicationActivate, EventTypes.ApplicationDeactivate, EventTypes.NonClientAreaMouseMove, EventTypes.NonClientAreaMouseButtonPress, EventTypes.NonClientAreaMouseButtonRelease, EventTypes.NonClientAreaMouseButtonDblClick ]) IgnoredEventClasses = (QChildEvent, QTimerEvent, QGraphicsSceneMouseEvent, QWindowStateChangeEvent, QMoveEvent) def captureEvent(self, watched, event): if self._shouldSaveEvent(event): try: eventstr = event_to_string(event) except KeyError: logger.warn("Don't know how to record event: {}".format( str(event))) print "Don't know", str(event) else: timestamp_in_seconds = self._timer.seconds() objname = str(get_fully_qualified_name(watched)) if not (self._ignore_parent_events and objname.startswith(self._parent_name)): self._captured_events.append( (eventstr, objname, timestamp_in_seconds)) return False def insertComment(self, comment): self._captured_events.append((comment, "comment", None)) def _shouldSaveEvent(self, event): if isinstance(event, QMouseEvent): # Ignore most mouse movement events if the user isn't pressing anything. if event.type() == QEvent.MouseMove \ and int(event.button()) == 0 \ and int(event.buttons()) == 0 \ and int(event.modifiers()) == 0: # Somewhat hackish (and slow), but we have to record mouse movements during combo box usage. # Same for QMenu usage (on Mac, it doesn't seem to matter, but on Fedora it does matter.) widgetUnderCursor = QApplication.instance().widgetAt( QCursor.pos()) if widgetUnderCursor is not None and widgetUnderCursor.objectName( ) == "qt_scrollarea_viewport": return has_ancestor(widgetUnderCursor, QComboBox) if isinstance(widgetUnderCursor, QMenu): return True return False else: return True # Ignore non-spontaneous events if not event.spontaneous(): return False if event.type() in self.IgnoredEventTypes: return False if isinstance(event, self.IgnoredEventClasses): return False return True def unpause(self): # Here, we use a special override of QApplication.notify() instead of using QApplication.instance().installEventFilter(). # That's because (contrary to the documentation), the QApplication eventFilter does NOT get to see every event in the application. # Testing shows that events that were "filtered out" by a different event filter may not be seen by the QApplication event filter. self._timer.unpause() def _notify(receiver, event): self.captureEvent(receiver, event) return _orig_QApp_notify(receiver, event) from ilastik.shell.gui.startShellGui import EventRecordingApp assert isinstance(QApplication.instance(), EventRecordingApp) QApplication.instance()._notify = _notify def pause(self): self._timer.pause() QApplication.instance()._notify = _orig_QApp_notify def writeScript(self, fileobj): # Write header comments fileobj.write(""" # Event Recording # Started at: {} """.format(str(self._timer.start_time))) # Write playback function definition fileobj.write(""" def playback_events(player): import PyQt4.QtCore from PyQt4.QtCore import Qt, QEvent, QPoint import PyQt4.QtGui from ilastik.utility.gui.eventRecorder.objectNameUtils import get_named_object from ilastik.utility.gui.eventRecorder.eventRecorder import EventPlayer from ilastik.shell.gui.startShellGui import shell player.display_comment("SCRIPT STARTING") """) # Write all events and comments for eventstr, objname, timestamp_in_seconds in self._captured_events: if objname == "comment": eventstr = eventstr.replace('\\', '\\\\') eventstr = eventstr.replace('"', '\\"') eventstr = eventstr.replace("'", "\\'") fileobj.write(""" ######################## player.display_comment(\"""{eventstr}\""") ######################## """.format(**locals())) else: fileobj.write(""" obj = get_named_object( '{objname}' ) player.post_event( obj, {eventstr}, {timestamp_in_seconds} ) """.format(**locals())) fileobj.write(""" player.display_comment("SCRIPT COMPLETE") """)