Exemplo n.º 1
0
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 "--------------------------------------------------"
Exemplo n.º 2
0
 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()
Exemplo n.º 3
0
 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
Exemplo n.º 4
0
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 "--------------------------------------------------"
Exemplo n.º 5
0
        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)
Exemplo n.º 6
0
 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
Exemplo n.º 7
0
 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()
Exemplo n.º 8
0
    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() ))
Exemplo n.º 10
0
    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")
Exemplo n.º 11
0
    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() ))
Exemplo n.º 13
0
 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() )
Exemplo n.º 14
0
    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
Exemplo n.º 15
0
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")
""")
Exemplo n.º 16
0
    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
Exemplo n.º 17
0
    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
Exemplo n.º 18
0
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")
""")