Пример #1
0
 def createAndLoadNewProject(self, newProjectFilePath, workflow_class):
     hdf5File = ProjectManager.createBlankProjectFile(newProjectFilePath)
     readOnly = False
     self.projectManager = ProjectManager(
         self, workflow_class, headless=True, workflow_cmdline_args=self._workflow_cmdline_args
     )
     self.projectManager._loadProject(hdf5File, newProjectFilePath, readOnly)
Пример #2
0
    def openProjectFile(self, projectFilePath, force_readonly=False):
        # Make sure all workflow sub-classes have been loaded,
        #  so we can detect the workflow type in the project.
        import ilastik.workflows
        try:
            # Open the project file
            hdf5File, workflow_class, readOnly = ProjectManager.openProjectFile(projectFilePath, force_readonly)

            # If there are any "creation-time" command-line args saved to the project file,
            #  load them so that the workflow can be instantiated with the same settings 
            #  that were used when the project was first created. 
            project_creation_args = []
            if "workflow_cmdline_args" in hdf5File.keys():
                if len(hdf5File["workflow_cmdline_args"]) > 0:
                    project_creation_args = map(str, hdf5File["workflow_cmdline_args"][...])

            if workflow_class is None:
                # If the project file has no known workflow, we assume pixel classification
                import ilastik.workflows
                workflow_class = ilastik.workflows.pixelClassification.PixelClassificationWorkflow
                import warnings
                warnings.warn( "Your project file ({}) does not specify a workflow type.  "
                               "Assuming Pixel Classification".format( projectFilePath ) )            
            
            # Create our project manager
            # This instantiates the workflow and applies all settings from the project.
            self.projectManager = ProjectManager( self,
                                                  workflow_class,
                                                  headless=True,
                                                  workflow_cmdline_args=self._workflow_cmdline_args,
                                                  project_creation_args=project_creation_args )
            self.projectManager._loadProject(hdf5File, projectFilePath, readOnly)

        except ProjectManager.FileMissingError:
            logger.error("Couldn't find project file: {}".format( projectFilePath ))
            raise            
        except ProjectManager.ProjectVersionError:
            # Couldn't open project.  Try importing it.
            oldProjectFilePath = projectFilePath
            name, ext = os.path.splitext(oldProjectFilePath)
    
            # Create a brand new project file.
            projectFilePath = name + "_imported" + ext
            logger.info("Importing project as '" + projectFilePath + "'")
            hdf5File = ProjectManager.createBlankProjectFile(projectFilePath)

            # For now, we assume that any imported projects are pixel classification workflow projects.
            import ilastik.workflows
            default_workflow = ilastik.workflows.pixelClassification.PixelClassificationWorkflow

            # Create the project manager.
            # Here, we provide an additional parameter: the path of the project we're importing from. 
            self.projectManager = ProjectManager( self,
                                                  default_workflow,
                                                  importFromPath=oldProjectFilePath,
                                                  headless=True,
                                                  workflow_cmdline_args=self._workflow_cmdline_args,
                                                  project_creation_args=self._workflow_cmdline_args )

            self.projectManager._importProject(oldProjectFilePath, hdf5File, projectFilePath,readOnly = False)
Пример #3
0
    def openProjectFile(self, projectFilePath):
        # Make sure all workflow sub-classes have been loaded,
        #  so we can detect the workflow type in the project.
        import ilastik.workflows
        try:
            # Open the project file
            hdf5File, workflow_class, _ = ProjectManager.openProjectFile(projectFilePath)
            
            # Create our project manager
            # This instantiates the workflow and applies all settings from the project.
            self.projectManager = ProjectManager( workflow_class,
                                                  headless=True,
                                                  workflow_cmdline_args=self._workflow_cmdline_args )
            self.projectManager._loadProject(hdf5File, projectFilePath, readOnly = False)
            
        except ProjectManager.ProjectVersionError:
            # Couldn't open project.  Try importing it.
            oldProjectFilePath = projectFilePath
            name, ext = os.path.splitext(oldProjectFilePath)
    
            # Create a brand new project file.
            projectFilePath = name + "_imported" + ext
            logger.info("Importing project as '" + projectFilePath + "'")
            hdf5File = ProjectManager.createBlankProjectFile(projectFilePath)

            # For now, we assume that any imported projects are pixel classification workflow projects.
            import ilastik.workflows
            default_workflow = ilastik.workflows.pixelClassification.PixelClassificationWorkflow

            # Create the project manager.
            # Here, we provide an additional parameter: the path of the project we're importing from. 
            self.projectManager = ProjectManager( default_workflow,
                                                  importFromPath=oldProjectFilePath,
                                                  headless=True )
            self.projectManager._importProject(oldProjectFilePath, hdf5File, projectFilePath,readOnly = False)
Пример #4
0
class HeadlessShell(object):
    """
    For now, this class is just a stand-in for the GUI shell (used when running from the command line).
    """

    def __init__(self):
        self._applets = []
        self.projectManager = ProjectManager()
        self.currentImageIndex = -1

    def addApplet(self, aplt):
        self._applets.append(aplt)
        self.projectManager.addApplet(aplt)

    def changeCurrentInputImageIndex(self, newImageIndex):
        if newImageIndex != self.currentImageIndex:
            # Alert each central widget and viewer control widget that the image selection changed
            for i in range(len(self._applets)):
                self._applets[i].gui.setImageIndex(newImageIndex)

            self.currentImageIndex = newImageIndex

    def openProjectPath(self, projectFilePath):
        try:
            hdf5File, readOnly = self.projectManager.openProjectFile(projectFilePath)
            self.projectManager.loadProject(hdf5File, projectFilePath, readOnly)
        except ProjectManager.ProjectVersionError:
            # Couldn't open project.  Try importing it.
            oldProjectFilePath = projectFilePath
            name, ext = os.path.splitext(oldProjectFilePath)
            projectFilePath = name + "_imported" + ext

            logger.info("Importing project as '" + projectFilePath + "'")
            projectFile = self.projectManager.createBlankProjectFile(projectFilePath)
            self.projectManager.importProject(oldProjectFilePath, projectFile, projectFilePath)
Пример #5
0
class HeadlessShell(object):
    """
    For now, this class is just a stand-in for the GUI shell (used when running from the command line).
    """
    
    def __init__(self, workflowClass):
        self._workflowClass = workflowClass
        self.projectManager = None

    @property
    def workflow(self):
        return self.projectManager.workflow

    def createBlankProjectFile(self, projectFilePath):
        hdf5File = ProjectManager.createBlankProjectFile(projectFilePath)
        readOnly = False
        self.projectManager = ProjectManager( self._workflowClass,  headless=True )
        self.projectManager._loadProject(hdf5File, projectFilePath, readOnly)
        
    def openProjectPath(self, projectFilePath):
        try:
            # Open the project file
            hdf5File, readOnly = ProjectManager.openProjectFile(projectFilePath)
            
            # Create our project manager
            # This instantiates the workflow and applies all settings from the project.
            self.projectManager = ProjectManager( self._workflowClass, headless=True )
            self.projectManager._loadProject(hdf5File, projectFilePath, readOnly = False)
            
        except ProjectManager.ProjectVersionError:
            # Couldn't open project.  Try importing it.
            oldProjectFilePath = projectFilePath
            name, ext = os.path.splitext(oldProjectFilePath)
    
            # Create a brand new project file.
            projectFilePath = name + "_imported" + ext
            logger.info("Importing project as '" + projectFilePath + "'")
            hdf5File = ProjectManager.createBlankProjectFile(projectFilePath)

            # Create the project manager.
            # Here, we provide an additional parameter: the path of the project we're importing from. 
            self.projectManager = ProjectManager( self._workflowClass, importFromPath=oldProjectFilePath, headless=True )
            self.projectManager._importProject(importFromPath, hdf5File, projectFilePath,readOnly = False)

    def closeCurrentProject(self):
        self.projectManager._closeCurrentProject()
        self.projectManager.cleanUp()
        self.projectManager = None
    def create_new_tst_project(cls):
        # Instantiate 'shell'
        shell = HeadlessShell(  )
        
        # Create a blank project file and load it.
        newProjectFilePath = cls.PROJECT_FILE
        newProjectFile = ProjectManager.createBlankProjectFile(newProjectFilePath, PixelClassificationWorkflow, [])
        newProjectFile.close()
        shell.openProjectFile(newProjectFilePath)
        workflow = shell.workflow
        
        # Add a file
        from ilastik.applets.dataSelection.opDataSelection import DatasetInfo
        info = DatasetInfo()
        info.filePath = cls.SAMPLE_DATA
        opDataSelection = workflow.dataSelectionApplet.topLevelOperator
        opDataSelection.DatasetGroup.resize(1)
        opDataSelection.DatasetGroup[0][0].setValue(info)
        
        
        # Set some features
        ScalesList = [0.3, 0.7, 1, 1.6, 3.5, 5.0, 10.0]    
        FeatureIds = [ 'GaussianSmoothing',
                       'LaplacianOfGaussian',
                       'StructureTensorEigenvalues',
                       'HessianOfGaussianEigenvalues',
                       'GaussianGradientMagnitude',
                       'DifferenceOfGaussians' ]

        opFeatures = workflow.featureSelectionApplet.topLevelOperator
        opFeatures.Scales.setValue( ScalesList )
        opFeatures.FeatureIds.setValue( FeatureIds )

        #                    sigma:   0.3    0.7    1.0    1.6    3.5    5.0   10.0
        selections = numpy.array( [[True, False, False, False, False, False, False],
                                   [True, False, False, False, False, False, False],
                                   [True, False, False, False, False, False, False],
                                   [False, False, False, False, False, False, False],
                                   [False, False, False, False, False, False, False],
                                   [False, False, False, False, False, False, False]] )
        opFeatures.SelectionMatrix.setValue(selections)
    
        # Add some labels directly to the operator
        opPixelClass = workflow.pcApplet.topLevelOperator

        opPixelClass.LabelNames.setValue(['Label 1', 'Label 2'])

        slicing1 = sl[0:1,0:10,0:10,0:1,0:1]
        labels1 = 1 * numpy.ones(slicing2shape(slicing1), dtype=numpy.uint8)
        opPixelClass.LabelInputs[0][slicing1] = labels1

        slicing2 = sl[0:1,0:10,10:20,0:1,0:1]
        labels2 = 2 * numpy.ones(slicing2shape(slicing2), dtype=numpy.uint8)
        opPixelClass.LabelInputs[0][slicing2] = labels2

        # Save and close
        shell.projectManager.saveProject()
        del shell
Пример #7
0
    def __init__( self, workflow = [], parent = None, flags = QtCore.Qt.WindowFlags(0), sideSplitterSizePolicy=SideSplitterSizePolicy.Manual ):
        QMainWindow.__init__(self, parent = parent, flags = flags )
        # Register for thunk events (easy UI calls from non-GUI threads)
        self.thunkEventHandler = ThunkEventHandler(self)

        self._sideSplitterSizePolicy = sideSplitterSizePolicy

        self.projectManager = ProjectManager()
        
        import inspect, os
        ilastikShellFilePath = os.path.dirname(inspect.getfile(inspect.currentframe()))
        uic.loadUi( ilastikShellFilePath + "/ui/ilastikShell.ui", self )
        self._applets = []
        self.appletBarMapping = {}

        self.setAttribute(Qt.WA_AlwaysShowToolTips)
        
        if 'Ubuntu' in platform.platform():
            # Native menus are prettier, but aren't working on Ubuntu at this time (Qt 4.7, Ubuntu 11)
            self.menuBar().setNativeMenuBar(False)

        (self._projectMenu, self._shellActions) = self._createProjectMenu()
        self._settingsMenu = self._createSettingsMenu()
        self.menuBar().addMenu( self._projectMenu )
        self.menuBar().addMenu( self._settingsMenu )

        self.updateShellProjectDisplay()
        
        self.progressDisplayManager = ProgressDisplayManager(self.statusBar)

        self.appletBar.expanded.connect(self.handleAppleBarItemExpanded)
        self.appletBar.clicked.connect(self.handleAppletBarClick)
        self.appletBar.setVerticalScrollMode( QAbstractItemView.ScrollPerPixel )
        
        # By default, make the splitter control expose a reasonable width of the applet bar
        self.mainSplitter.setSizes([300,1])
        
        self.currentAppletIndex = 0

        self.currentImageIndex = -1
        self.populatingImageSelectionCombo = False
        self.imageSelectionCombo.currentIndexChanged.connect( self.changeCurrentInputImageIndex )
        
        self.enableWorkflow = False # Global mask applied to all applets
        self._controlCmds = []      # Track the control commands that have been issued by each applet so they can be popped.
        self._disableCounts = []    # Controls for each applet can be disabled by his peers.
                                    # No applet can be enabled unless his disableCount == 0

        # Add all the applets from the workflow
        for app in workflow.applets:
            self.addApplet(app)
        self.workflow = workflow
Пример #8
0
    def openProjectPath(self, projectFilePath):
        try:
            # Open the project file
            hdf5File, readOnly = ProjectManager.openProjectFile(projectFilePath)
            
            # Create our project manager
            # This instantiates the workflow and applies all settings from the project.
            self.projectManager = ProjectManager( self._workflowClass, hdf5File, projectFilePath, readOnly, headless=True )

        except ProjectManager.ProjectVersionError:
            # Couldn't open project.  Try importing it.
            oldProjectFilePath = projectFilePath
            name, ext = os.path.splitext(oldProjectFilePath)
    
            # Create a brand new project file.
            projectFilePath = name + "_imported" + ext
            logger.info("Importing project as '" + projectFilePath + "'")
            hdf5File = ProjectManager.createBlankProjectFile(projectFilePath)

            # Create the project manager.
            # Here, we provide an additional parameter: the path of the project we're importing from. 
            self.projectManager = ProjectManager( self._workflowClass, hdf5File, projectFilePath, readOnly=False, importFromPath=oldProjectFilePath, headless=True )
Пример #9
0
    def downloadProjectFromDvid(cls, dvid_key_url):
        # By convention, command-line users specify the location of the project 
        # keyvalue data using the same format that the DVID API itself uses.
        url_format = "^protocol://hostname/api/node/uuid/kv_instance_name(\\?/key/keyname)?"
        for field in ['protocol', 'hostname', 'uuid', 'kv_instance_name', 'keyname']:
            url_format = url_format.replace( field, '(?P<' + field + '>[^?]+)' )

        match = re.match( url_format, dvid_key_url )
        if not match:
            # DVID is the only url-based format we support right now.
            # So if it looks like the user gave a URL that isn't a valid DVID node, then error.
            raise RuntimeError("Invalid URL format for DVID key-value data: {}".format(projectFilePath))

        fields = match.groupdict()            
        projectFilePath = ProjectManager.downloadProjectFromDvid( fields['hostname'],
                                                                  fields['uuid'],
                                                                  fields['kv_instance_name'],
                                                                  fields['keyname'] )
        return projectFilePath
Пример #10
0
 def createBlankProjectFile(self, projectFilePath):
     hdf5File = ProjectManager.createBlankProjectFile(projectFilePath)
     readOnly = False
     self.projectManager = ProjectManager( self._workflowClass,  headless=True )
     self.projectManager._loadProject(hdf5File, projectFilePath, readOnly)
    def create_new_project(cls, project_file_path, dataset_path):
        # Instantiate 'shell'
        shell = HeadlessShell()

        # Create a blank project file and load it.
        newProjectFile = ProjectManager.createBlankProjectFile(project_file_path, PixelClassificationWorkflow, [])
        newProjectFile.close()
        shell.openProjectFile(project_file_path)
        workflow = shell.workflow

        # Add a file
        from ilastik.applets.dataSelection.opDataSelection import FilesystemDatasetInfo

        info = FilesystemDatasetInfo(filePath=dataset_path)
        opDataSelection = workflow.dataSelectionApplet.topLevelOperator
        opDataSelection.DatasetGroup.resize(1)
        opDataSelection.DatasetGroup[0][0].setValue(info)

        # Set some features
        ScalesList = [0.3, 0.7, 1, 1.6, 3.5, 5.0, 10.0]
        FeatureIds = [
            "GaussianSmoothing",
            "LaplacianOfGaussian",
            "StructureTensorEigenvalues",
            "HessianOfGaussianEigenvalues",
            "GaussianGradientMagnitude",
            "DifferenceOfGaussians",
        ]

        opFeatures = workflow.featureSelectionApplet.topLevelOperator
        opFeatures.Scales.setValue(ScalesList)
        opFeatures.FeatureIds.setValue(FeatureIds)

        #                    sigma:   0.3    0.7    1.0    1.6    3.5    5.0   10.0
        selections = numpy.array(
            [
                [True, False, False, False, False, False, False],
                [True, False, False, False, False, False, False],
                [True, False, False, False, False, False, False],
                [False, False, False, False, False, False, False],
                [False, False, False, False, False, False, False],
                [False, False, False, False, False, False, False],
            ]
        )
        opFeatures.SelectionMatrix.setValue(selections)

        # Add some labels directly to the operator
        opPixelClass = workflow.pcApplet.topLevelOperator

        opPixelClass.LabelNames.setValue(["Label 1", "Label 2"])

        slicing1 = sl[0:1, 0:10, 0:10, 0:1, 0:1]
        labels1 = 1 * numpy.ones(slicing2shape(slicing1), dtype=numpy.uint8)
        opPixelClass.LabelInputs[0][slicing1] = labels1

        slicing2 = sl[0:1, 0:10, 10:20, 0:1, 0:1]
        labels2 = 2 * numpy.ones(slicing2shape(slicing2), dtype=numpy.uint8)
        opPixelClass.LabelInputs[0][slicing2] = labels2

        # Train the classifier
        opPixelClass.FreezePredictions.setValue(False)
        _ = opPixelClass.Classifier.value

        # Save and close
        shell.projectManager.saveProject()
        del shell
Пример #12
0
    def openProjectFile(self, projectFilePath, force_readonly=False):
        # If the user gave a URL to a DVID key, then download the project file from dvid first.
        # (So far, DVID is the only type of URL access we support for project files.)
        if isUrl(projectFilePath):
            projectFilePath = HeadlessShell.downloadProjectFromDvid(projectFilePath)

        # Make sure all workflow sub-classes have been loaded,
        #  so we can detect the workflow type in the project.
        import ilastik.workflows

        try:
            # Open the project file
            hdf5File, workflow_class, readOnly = ProjectManager.openProjectFile(projectFilePath, force_readonly)

            # If there are any "creation-time" command-line args saved to the project file,
            #  load them so that the workflow can be instantiated with the same settings
            #  that were used when the project was first created.
            project_creation_args = []
            if "workflow_cmdline_args" in list(hdf5File.keys()):
                if len(hdf5File["workflow_cmdline_args"]) > 0:
                    project_creation_args = list(map(str, hdf5File["workflow_cmdline_args"][...]))

            if workflow_class is None:
                # If the project file has no known workflow, we assume pixel classification
                import ilastik.workflows

                workflow_class = ilastik.workflows.pixelClassification.PixelClassificationWorkflow
                import warnings

                warnings.warn(
                    "Your project file ({}) does not specify a workflow type.  "
                    "Assuming Pixel Classification".format(projectFilePath)
                )

            # Create our project manager
            # This instantiates the workflow and applies all settings from the project.
            self.projectManager = ProjectManager(
                self,
                workflow_class,
                headless=True,
                workflow_cmdline_args=self._workflow_cmdline_args,
                project_creation_args=project_creation_args,
            )
            self.projectManager._loadProject(hdf5File, projectFilePath, readOnly)

        except ProjectManager.FileMissingError:
            logger.error("Couldn't find project file: {}".format(projectFilePath))
            raise
        except ProjectManager.ProjectVersionError:
            # Couldn't open project.  Try importing it.
            oldProjectFilePath = projectFilePath
            name, ext = os.path.splitext(oldProjectFilePath)

            # Create a brand new project file.
            projectFilePath = name + "_imported" + ext
            logger.info("Importing project as '" + projectFilePath + "'")
            hdf5File = ProjectManager.createBlankProjectFile(projectFilePath)

            # For now, we assume that any imported projects are pixel classification workflow projects.
            import ilastik.workflows

            default_workflow = ilastik.workflows.pixelClassification.PixelClassificationWorkflow

            # Create the project manager.
            self.projectManager = ProjectManager(
                self,
                default_workflow,
                headless=True,
                workflow_cmdline_args=self._workflow_cmdline_args,
                project_creation_args=self._workflow_cmdline_args,
            )

            self.projectManager._importProject(oldProjectFilePath, hdf5File, projectFilePath)
Пример #13
0
class HeadlessShell(object):
    """
    For now, this class is just a stand-in for the GUI shell (used when running from the command line).
    """

    def __init__(self, workflow_cmdline_args=None):
        self._workflow_cmdline_args = workflow_cmdline_args or []
        self.projectManager = None

    @property
    def workflow(self):
        if self.projectManager is not None:
            return self.projectManager.workflow

    @property
    def currentImageIndex(self):
        return -1

    def createAndLoadNewProject(self, newProjectFilePath, workflow_class):
        hdf5File = ProjectManager.createBlankProjectFile(newProjectFilePath)
        readOnly = False
        self.projectManager = ProjectManager(
            self, workflow_class, headless=True, workflow_cmdline_args=self._workflow_cmdline_args
        )
        self.projectManager._loadProject(hdf5File, newProjectFilePath, readOnly)
        self.projectManager.saveProject()

    @classmethod
    def downloadProjectFromDvid(cls, dvid_key_url):
        dvid_key_url = str(dvid_key_url)

        # By convention, command-line users specify the location of the project
        # keyvalue data using the same format that the DVID API itself uses.
        url_format = "^protocol://hostname/api/node/uuid/kv_instance_name(/key/keyname)?"
        for field in ["protocol", "hostname", "uuid", "kv_instance_name", "keyname"]:
            url_format = url_format.replace(field, "(?P<" + field + ">[^?/]+)")

        match = re.match(url_format, dvid_key_url)
        if not match:
            # DVID is the only url-based format we support right now.
            # So if it looks like the user gave a URL that isn't a valid DVID node, then error.
            raise RuntimeError("Invalid URL format for DVID key-value data: {}".format(projectFilePath))

        fields = match.groupdict()
        projectFilePath = ProjectManager.downloadProjectFromDvid(
            fields["hostname"], fields["uuid"], fields["kv_instance_name"], fields["keyname"]
        )
        return projectFilePath

    def openProjectFile(self, projectFilePath, force_readonly=False):
        # If the user gave a URL to a DVID key, then download the project file from dvid first.
        # (So far, DVID is the only type of URL access we support for project files.)
        if isUrl(projectFilePath):
            projectFilePath = HeadlessShell.downloadProjectFromDvid(projectFilePath)

        # Make sure all workflow sub-classes have been loaded,
        #  so we can detect the workflow type in the project.
        import ilastik.workflows

        try:
            # Open the project file
            hdf5File, workflow_class, readOnly = ProjectManager.openProjectFile(projectFilePath, force_readonly)

            # If there are any "creation-time" command-line args saved to the project file,
            #  load them so that the workflow can be instantiated with the same settings
            #  that were used when the project was first created.
            project_creation_args = []
            if "workflow_cmdline_args" in list(hdf5File.keys()):
                if len(hdf5File["workflow_cmdline_args"]) > 0:
                    project_creation_args = list(map(str, hdf5File["workflow_cmdline_args"][...]))

            if workflow_class is None:
                # If the project file has no known workflow, we assume pixel classification
                import ilastik.workflows

                workflow_class = ilastik.workflows.pixelClassification.PixelClassificationWorkflow
                import warnings

                warnings.warn(
                    "Your project file ({}) does not specify a workflow type.  "
                    "Assuming Pixel Classification".format(projectFilePath)
                )

            # Create our project manager
            # This instantiates the workflow and applies all settings from the project.
            self.projectManager = ProjectManager(
                self,
                workflow_class,
                headless=True,
                workflow_cmdline_args=self._workflow_cmdline_args,
                project_creation_args=project_creation_args,
            )
            self.projectManager._loadProject(hdf5File, projectFilePath, readOnly)

        except ProjectManager.FileMissingError:
            logger.error("Couldn't find project file: {}".format(projectFilePath))
            raise
        except ProjectManager.ProjectVersionError:
            # Couldn't open project.  Try importing it.
            oldProjectFilePath = projectFilePath
            name, ext = os.path.splitext(oldProjectFilePath)

            # Create a brand new project file.
            projectFilePath = name + "_imported" + ext
            logger.info("Importing project as '" + projectFilePath + "'")
            hdf5File = ProjectManager.createBlankProjectFile(projectFilePath)

            # For now, we assume that any imported projects are pixel classification workflow projects.
            import ilastik.workflows

            default_workflow = ilastik.workflows.pixelClassification.PixelClassificationWorkflow

            # Create the project manager.
            self.projectManager = ProjectManager(
                self,
                default_workflow,
                headless=True,
                workflow_cmdline_args=self._workflow_cmdline_args,
                project_creation_args=self._workflow_cmdline_args,
            )

            self.projectManager._importProject(oldProjectFilePath, hdf5File, projectFilePath)

    def setAppletEnabled(self, applet, enabled):
        """
        Provided here to satisfy the ShellABC.
        For now, HeadlessShell has no concept of "enabled" or "disabled" applets.
        """
        pass

    def isAppletEnabled(self, applet):
        return False

    def enableProjectChanges(self, enabled):
        """
        Provided here to satisfy the ShellABC.
        For now, HeadlessShell has no mechanism for closing projects.
        """
        pass

    def closeCurrentProject(self):
        if self.projectManager is not None:
            self.projectManager._closeCurrentProject()
            self.projectManager.cleanUp()
            self.projectManager = None
Пример #14
0
class IlastikShell( QMainWindow ):
    """
    The GUI's main window.  Simply a standard 'container' GUI for one or more applets.
    """


    def __init__( self, workflow = [], parent = None, flags = QtCore.Qt.WindowFlags(0), sideSplitterSizePolicy=SideSplitterSizePolicy.Manual ):
        QMainWindow.__init__(self, parent = parent, flags = flags )
        # Register for thunk events (easy UI calls from non-GUI threads)
        self.thunkEventHandler = ThunkEventHandler(self)

        self._sideSplitterSizePolicy = sideSplitterSizePolicy

        self.projectManager = ProjectManager()
        
        import inspect, os
        ilastikShellFilePath = os.path.dirname(inspect.getfile(inspect.currentframe()))
        uic.loadUi( ilastikShellFilePath + "/ui/ilastikShell.ui", self )
        self._applets = []
        self.appletBarMapping = {}

        self.setAttribute(Qt.WA_AlwaysShowToolTips)
        
        if 'Ubuntu' in platform.platform():
            # Native menus are prettier, but aren't working on Ubuntu at this time (Qt 4.7, Ubuntu 11)
            self.menuBar().setNativeMenuBar(False)

        (self._projectMenu, self._shellActions) = self._createProjectMenu()
        self._settingsMenu = self._createSettingsMenu()
        self.menuBar().addMenu( self._projectMenu )
        self.menuBar().addMenu( self._settingsMenu )

        self.updateShellProjectDisplay()
        
        self.progressDisplayManager = ProgressDisplayManager(self.statusBar)

        for applet in workflow:
            self.addApplet(applet)

        self.appletBar.expanded.connect(self.handleAppleBarItemExpanded)
        self.appletBar.clicked.connect(self.handleAppletBarClick)
        self.appletBar.setVerticalScrollMode( QAbstractItemView.ScrollPerPixel )
        
        # By default, make the splitter control expose a reasonable width of the applet bar
        self.mainSplitter.setSizes([300,1])
        
        self.currentAppletIndex = 0

        self.currentImageIndex = -1
        self.populatingImageSelectionCombo = False
        self.imageSelectionCombo.currentIndexChanged.connect( self.changeCurrentInputImageIndex )
        
        self.enableWorkflow = False # Global mask applied to all applets
        self._controlCmds = []      # Track the control commands that have been issued by each applet so they can be popped.
        self._disableCounts = []    # Controls for each applet can be disabled by his peers.
                                    # No applet can be enabled unless his disableCount == 0

        
    def _createProjectMenu(self):
        # Create a menu for "General" (non-applet) actions
        menu = QMenu("&Project", self)

        shellActions = ShellActions()

        # Menu item: New Project
        shellActions.newProjectAction = menu.addAction("&New Project...")
        shellActions.newProjectAction.setShortcuts( QKeySequence.New )
        shellActions.newProjectAction.triggered.connect(self.onNewProjectActionTriggered)

        # Menu item: Open Project 
        shellActions.openProjectAction = menu.addAction("&Open Project...")
        shellActions.openProjectAction.setShortcuts( QKeySequence.Open )
        shellActions.openProjectAction.triggered.connect(self.onOpenProjectActionTriggered)

        # Menu item: Save Project
        shellActions.saveProjectAction = menu.addAction("&Save Project")
        shellActions.saveProjectAction.setShortcuts( QKeySequence.Save )
        shellActions.saveProjectAction.triggered.connect(self.onSaveProjectActionTriggered)

        # Menu item: Save Project As
        shellActions.saveProjectAsAction = menu.addAction("&Save Project As...")
        shellActions.saveProjectAsAction.setShortcuts( QKeySequence.SaveAs )
        shellActions.saveProjectAsAction.triggered.connect(self.onSaveProjectAsActionTriggered)

        # Menu item: Save Project Snapshot
        shellActions.saveProjectSnapshotAction = menu.addAction("&Take Snapshot...")
        shellActions.saveProjectSnapshotAction.triggered.connect(self.onSaveProjectSnapshotActionTriggered)

        # Menu item: Import Project
        shellActions.importProjectAction = menu.addAction("&Import Project...")
        shellActions.importProjectAction.triggered.connect(self.onImportProjectActionTriggered)

        # Menu item: Quit
        shellActions.quitAction = menu.addAction("&Quit")
        shellActions.quitAction.setShortcuts( QKeySequence.Quit )
        shellActions.quitAction.triggered.connect(self.onQuitActionTriggered)
        shellActions.quitAction.setShortcut( QKeySequence.Quit )
        
        return (menu, shellActions)
    
    def _createSettingsMenu(self):
        menu = QMenu("&Settings", self)
        # Menu item: Keyboard Shortcuts

        def editShortcuts():
            mgrDlg = ShortcutManagerDlg(self)
        shortcutsAction = menu.addAction("&Keyboard Shortcuts")
        shortcutsAction.triggered.connect(editShortcuts)
        
        return menu
    
    def show(self):
        """
        Show the window, and enable/disable controls depending on whether or not a project file present.
        """
        super(IlastikShell, self).show()
        self.enableWorkflow = (self.projectManager.currentProjectFile is not None)
        self.updateAppletControlStates()
        self.updateShellProjectDisplay()
        if self._sideSplitterSizePolicy == SideSplitterSizePolicy.Manual:
            self.autoSizeSideSplitter( SideSplitterSizePolicy.AutoLargestDrawer )
        else:
            self.autoSizeSideSplitter( SideSplitterSizePolicy.AutoCurrentDrawer )

    def updateShellProjectDisplay(self):
        """
        Update the title bar and allowable shell actions based on the state of the currently loaded project.
        """
        windowTitle = "ilastik - "
        projectPath = self.projectManager.currentProjectPath
        if projectPath is None:
            windowTitle += "No Project Loaded"
        else:
            windowTitle += projectPath

        readOnly = self.projectManager.currentProjectIsReadOnly
        if readOnly:
            windowTitle += " [Read Only]"

        self.setWindowTitle(windowTitle)        

        # Enable/Disable menu items
        projectIsOpen = self.projectManager.currentProjectFile is not None
        self._shellActions.saveProjectAction.setEnabled(projectIsOpen and not readOnly) # Can't save a read-only project
        self._shellActions.saveProjectAsAction.setEnabled(projectIsOpen)
        self._shellActions.saveProjectSnapshotAction.setEnabled(projectIsOpen)

    def setImageNameListSlot(self, multiSlot):
        assert multiSlot.level == 1
        self.imageNamesSlot = multiSlot
        
        def insertImageName( index, slot ):
            self.imageSelectionCombo.setItemText( index, slot.value )
            if self.currentImageIndex == -1:
                self.changeCurrentInputImageIndex(index)

        def handleImageNameSlotInsertion(multislot, index):
            assert multislot == self.imageNamesSlot
            self.populatingImageSelectionCombo = True
            self.imageSelectionCombo.insertItem(index, "uninitialized")
            self.populatingImageSelectionCombo = False
            multislot[index].notifyDirty( bind( insertImageName, index) )

        multiSlot.notifyInserted( bind(handleImageNameSlotInsertion) )

        def handleImageNameSlotRemoval(multislot, index):
            # Simply remove the combo entry, which causes the currentIndexChanged signal to fire if necessary.
            self.imageSelectionCombo.removeItem(index)
            if len(multislot) == 0:
                self.changeCurrentInputImageIndex(-1)
        multiSlot.notifyRemove( bind(handleImageNameSlotRemoval) )

    def changeCurrentInputImageIndex(self, newImageIndex):
        if newImageIndex != self.currentImageIndex \
        and self.populatingImageSelectionCombo == False:
            if newImageIndex != -1:
                try:
                    # Accessing the image name value will throw if it isn't properly initialized
                    self.imageNamesSlot[newImageIndex].value
                except:
                    # Revert to the original image index.
                    if self.currentImageIndex != -1:
                        self.imageSelectionCombo.setCurrentIndex(self.currentImageIndex)
                    return

            # Alert each central widget and viewer control widget that the image selection changed
            for i in range( len(self._applets) ):
                self._applets[i].gui.setImageIndex(newImageIndex)
                
            self.currentImageIndex = newImageIndex


    def handleAppleBarItemExpanded(self, modelIndex):
        """
        The user wants to view a different applet bar item.
        """
        drawerIndex = modelIndex.row()
        self.setSelectedAppletDrawer(drawerIndex)
    
    def setSelectedAppletDrawer(self, drawerIndex):
        """
        Show the correct applet central widget, viewer control widget, and applet drawer widget for this drawer index.
        """
        if self.currentAppletIndex != drawerIndex:
            self.currentAppletIndex = drawerIndex
            # Collapse all drawers in the applet bar...
            self.appletBar.collapseAll()
            # ...except for the newly selected item.
            self.appletBar.expand( self.getModelIndexFromDrawerIndex(drawerIndex) )
            
            if len(self.appletBarMapping) != 0:
                # Determine which applet this drawer belongs to
                assert drawerIndex in self.appletBarMapping
                applet_index = self.appletBarMapping[drawerIndex]

                # Select the appropriate central widget, menu widget, and viewer control widget for this applet
                self.appletStack.setCurrentIndex(applet_index)
                self.viewerControlStack.setCurrentIndex(applet_index)
                self.menuBar().clear()
                self.menuBar().addMenu(self._projectMenu)
                self.menuBar().addMenu(self._settingsMenu)
                for m in self._applets[applet_index].gui.menus():
                    self.menuBar().addMenu(m)
                
                self.autoSizeSideSplitter( self._sideSplitterSizePolicy )

    def getModelIndexFromDrawerIndex(self, drawerIndex):
        drawerTitleItem = self.appletBar.invisibleRootItem().child(drawerIndex)
        return self.appletBar.indexFromItem(drawerTitleItem)
                
    def autoSizeSideSplitter(self, sizePolicy):
        if sizePolicy == SideSplitterSizePolicy.Manual:
            # In manual mode, don't resize the splitter at all.
            return
        
        if sizePolicy == SideSplitterSizePolicy.AutoCurrentDrawer:
            # Get the height of the current applet drawer
            rootItem = self.appletBar.invisibleRootItem()
            appletDrawerItem = rootItem.child(self.currentAppletIndex).child(0)
            appletDrawerWidget = self.appletBar.itemWidget(appletDrawerItem, 0)
            appletDrawerHeight = appletDrawerWidget.frameSize().height()

        if sizePolicy == SideSplitterSizePolicy.AutoLargestDrawer:
            appletDrawerHeight = 0
            # Get the height of the largest drawer in the bar
            for drawerIndex in range( len(self.appletBarMapping) ):
                rootItem = self.appletBar.invisibleRootItem()
                appletDrawerItem = rootItem.child(drawerIndex).child(0)
                appletDrawerWidget = self.appletBar.itemWidget(appletDrawerItem, 0)
                appletDrawerHeight = max( appletDrawerHeight, appletDrawerWidget.frameSize().height() )
        
        # Get total height of the titles in the applet bar (not the widgets)
        firstItem = self.appletBar.invisibleRootItem().child(0)
        titleHeight = self.appletBar.visualItemRect(firstItem).size().height()
        numDrawers = len(self.appletBarMapping)
        totalTitleHeight = numDrawers * titleHeight    
    
        # Auto-size the splitter height based on the height of the applet bar.
        totalSplitterHeight = sum(self.sideSplitter.sizes())
        appletBarHeight = totalTitleHeight + appletDrawerHeight + 10 # Add a small margin so the scroll bar doesn't appear
        self.sideSplitter.setSizes([appletBarHeight, totalSplitterHeight-appletBarHeight])

    def handleAppletBarClick(self, modelIndex):
        # If the user clicks on a top-level item, automatically expand it.
        if modelIndex.parent() == self.appletBar.rootIndex():
            self.appletBar.expand(modelIndex)
        else:
            self.appletBar.setCurrentIndex( modelIndex.parent() )

    def addApplet( self, app ):
        assert isinstance( app, Applet ), "Applets must inherit from Applet base class."
        assert app.base_initialized, "Applets must call Applet.__init__ upon construction."

        assert issubclass( type(app.gui), AppletGuiInterface ), "Applet GUIs must conform to the Applet GUI interface."
        
        self._applets.append(app)
        applet_index = len(self._applets) - 1
        self.appletStack.addWidget( app.gui.centralWidget() )
        
        # Viewer controls are optional. If the applet didn't provide one, create an empty widget for him.
        if app.gui.viewerControlWidget() is None:
            self.viewerControlStack.addWidget( QWidget(parent=self) )
        else:
            self.viewerControlStack.addWidget( app.gui.viewerControlWidget() )

        # Add rows to the applet bar model
        rootItem = self.appletBar.invisibleRootItem()

        # Add all of the applet bar's items to the toolbox widget
        for controlName, controlGuiItem in app.gui.appletDrawers():
            appletNameItem = QTreeWidgetItem( self.appletBar, QtCore.QStringList( controlName ) )
            appletNameItem.setFont( 0, QFont("Ubuntu", 14) )
            drawerItem = QTreeWidgetItem(appletNameItem)
            drawerItem.setSizeHint( 0, controlGuiItem.frameSize() )
#            drawerItem.setBackground( 0, QBrush( QColor(224, 224, 224) ) )
#            drawerItem.setForeground( 0, QBrush( QColor(0,0,0) ) )
            self.appletBar.setItemWidget( drawerItem, 0, controlGuiItem )

            # Since each applet can contribute more than one applet bar item,
            #  we need to keep track of which applet this item is associated with
            self.appletBarMapping[rootItem.childCount()-1] = applet_index

        # Set up handling of GUI commands from this applet
        app.guiControlSignal.connect( bind(self.handleAppletGuiControlSignal, applet_index) )
        self._disableCounts.append(0)
        self._controlCmds.append( [] )

        # Set up handling of progress updates from this applet
        self.progressDisplayManager.addApplet(applet_index, app)
        
        # Set up handling of shell requests from this applet
        app.shellRequestSignal.connect( partial(self.handleShellRequest, applet_index) )

        self.projectManager.addApplet(app)
                
        return applet_index

    def handleAppletGuiControlSignal(self, applet_index, command=ControlCommand.DisableAll):
        """
        Applets fire a signal when they want other applet GUIs to be disabled.
        This function handles the signal.
        Each signal is treated as a command to disable other applets.
        A special command, Pop, undoes the applet's most recent command (i.e. re-enables the applets that were disabled).
        If an applet is disabled twice (e.g. by two different applets), then it won't become enabled again until both commands have been popped.
        """
        if command == ControlCommand.Pop:
            command = self._controlCmds[applet_index].pop()
            step = -1 # Since we're popping this command, we'll subtract from the disable counts
        else:
            step = 1
            self._controlCmds[applet_index].append( command ) # Push command onto the stack so we can pop it off when the applet isn't busy any more

        # Increase the disable count for each applet that is affected by this command.
        for index, count in enumerate(self._disableCounts):
            if (command == ControlCommand.DisableAll) \
            or (command == ControlCommand.DisableDownstream and index > applet_index) \
            or (command == ControlCommand.DisableUpstream and index < applet_index) \
            or (command == ControlCommand.DisableSelf and index == applet_index):
                self._disableCounts[index] += step

        # Update the control states in the GUI thread
        self.thunkEventHandler.post( self.updateAppletControlStates )

    def handleShellRequest(self, applet_index, requestAction):
        """
        An applet is asking us to do something.  Handle the request.
        """
        with Tracer(traceLogger):
            if requestAction == ShellRequest.RequestSave:
                # Call the handler directly to ensure this is a synchronous call (not queued to the GUI thread)
                self.projectManager.saveProject()

    def __len__( self ):
        return self.appletBar.count()

    def __getitem__( self, index ):
        return self._applets[index]
    
    def ensureNoCurrentProject(self, assertClean=False):
        """
        Close the current project.  If it's dirty, we ask the user for confirmation.
        
        The assertClean parameter is for tests.  Setting it to True will raise an assertion if the project was dirty.
        """
        closeProject = True
        if self.projectManager.isProjectDataDirty():
            # Testing assertion
            assert not assertClean, "Expected a clean project but found it to be dirty!"

            message = "Your current project is about to be closed, but it has unsaved changes which will be lost.\n"
            message += "Are you sure you want to proceed?"
            buttons = QMessageBox.Yes | QMessageBox.Cancel
            response = QMessageBox.warning(self, "Discard unsaved changes?", message, buttons, defaultButton=QMessageBox.Cancel)
            closeProject = (response == QMessageBox.Yes)
            

        if closeProject:
            self.closeCurrentProject()

        return closeProject

    def closeCurrentProject(self):
        for applet in self._applets:
            applet.gui.reset()
        self.projectManager.closeCurrentProject()
        self.enableWorkflow = False
        self.updateAppletControlStates()
        self.updateShellProjectDisplay()
    
    def onNewProjectActionTriggered(self):
        logger.debug("New Project action triggered")
        
        # Make sure the user is finished with the currently open project
        if not self.ensureNoCurrentProject():
            return
        
        newProjectFilePath = self.getProjectPathToCreate()

        if newProjectFilePath is not None:
            self.createAndLoadNewProject(newProjectFilePath)

    def createAndLoadNewProject(self, newProjectFilePath):
        newProjectFile = self.projectManager.createBlankProjectFile(newProjectFilePath)
        self.loadProject(newProjectFile, newProjectFilePath, False)
    
    def getProjectPathToCreate(self, defaultPath=None, caption="Create Ilastik Project"):
        """
        Ask the user where he would like to create a project file.
        """
        if defaultPath is None:
            defaultPath = os.path.expanduser("~/MyProject.ilp")
        
        fileSelected = False
        while not fileSelected:
            projectFilePath = QFileDialog.getSaveFileName(
               self, caption, defaultPath, "Ilastik project files (*.ilp)",
               options=QFileDialog.Options(QFileDialog.DontUseNativeDialog))
            
            # If the user cancelled, stop now
            if projectFilePath.isNull():
                return None
    
            projectFilePath = str(projectFilePath)
            fileSelected = True
            
            # Add extension if necessary
            fileExtension = os.path.splitext(projectFilePath)[1].lower()
            if fileExtension != '.ilp':
                projectFilePath += ".ilp"
                if os.path.exists(projectFilePath):
                    # Since we changed the file path, we need to re-check if we're overwriting an existing file.
                    message = "A file named '" + projectFilePath + "' already exists in this location.\n"
                    message += "Are you sure you want to overwrite it?"
                    buttons = QMessageBox.Yes | QMessageBox.Cancel
                    response = QMessageBox.warning(self, "Overwrite existing project?", message, buttons, defaultButton=QMessageBox.Cancel)
                    if response == QMessageBox.Cancel:
                        # Try again...
                        fileSelected = False

        return projectFilePath
    
    def onImportProjectActionTriggered(self):
        """
        Import an existing project into a new file.
        This involves opening the old file, saving it to a new file, and then opening the new file.
        """
        logger.debug("Import Project Action")

        if not self.ensureNoCurrentProject():
            return

        # Find the directory of the most recently *imported* project
        mostRecentImportPath = PreferencesManager().get( 'shell', 'recently imported' )
        if mostRecentImportPath is not None:
            defaultDirectory = os.path.split(mostRecentImportPath)[0]
        else:
            defaultDirectory = os.path.expanduser('~')

        # Select the paths to the ilp to import and the name of the new one we'll create
        importedFilePath = self.getProjectPathToOpen(defaultDirectory)
        if importedFilePath is not None:
            PreferencesManager().set('shell', 'recently imported', importedFilePath)
            defaultFile, ext = os.path.splitext(importedFilePath)
            defaultFile += "_imported"
            defaultFile += ext
            newProjectFilePath = self.getProjectPathToCreate(defaultFile)

        # If the user didn't cancel
        if importedFilePath is not None and newProjectFilePath is not None:
            self.importProject( importedFilePath, newProjectFilePath )

    def importProject(self, originalPath, newProjectFilePath):
        newProjectFile = self.projectManager.createBlankProjectFile(newProjectFilePath)
        self.projectManager.importProject(originalPath, newProjectFile, newProjectFilePath)

        self.updateShellProjectDisplay()

        # Enable all the applet controls
        self.enableWorkflow = True
        self.updateAppletControlStates()
        
    def getProjectPathToOpen(self, defaultDirectory):
        """
        Return the path of the project the user wants to open (or None if he cancels).
        """
        projectFilePath = QFileDialog.getOpenFileName(
           self, "Open Ilastik Project", defaultDirectory, "Ilastik project files (*.ilp)",
           options=QFileDialog.Options(QFileDialog.DontUseNativeDialog))

        # If the user canceled, stop now        
        if projectFilePath.isNull():
            return None

        return str(projectFilePath)

    def onOpenProjectActionTriggered(self):
        logger.debug("Open Project action triggered")
        
        # Make sure the user is finished with the currently open project
        if not self.ensureNoCurrentProject():
            return

        # Find the directory of the most recently opened project
        mostRecentProjectPath = PreferencesManager().get( 'shell', 'recently opened' )
        if mostRecentProjectPath is not None:
            defaultDirectory = os.path.split(mostRecentProjectPath)[0]
        else:
            defaultDirectory = os.path.expanduser('~')

        projectFilePath = self.getProjectPathToOpen(defaultDirectory)
        if projectFilePath is not None:
            PreferencesManager().set('shell', 'recently opened', projectFilePath)
            self.openProjectFile(projectFilePath)
    
    def openProjectFile(self, projectFilePath):
        try:
            hdf5File, readOnly = self.projectManager.openProjectFile(projectFilePath)
        except ProjectManager.ProjectVersionError,e:
            QMessageBox.warning(self, "Old Project", "Could not load old project file: " + projectFilePath + ".\nPlease try 'Import Project' instead.")
        except ProjectManager.FileMissingError:
            QMessageBox.warning(self, "Missing File", "Could not find project file: " + projectFilePath)
Пример #15
0
 def __init__(self):
     self._applets = []
     self.projectManager = ProjectManager()
     self.currentImageIndex = -1
Пример #16
0
 def __init__(self):
     self._applets = []
     self.projectManager = ProjectManager()
     self.currentImageIndex = -1
Пример #17
0
 def create_project_file(self, workflow_class, project_file_name):
     newProjectFile = ProjectManager.createBlankProjectFile(project_file_name, workflow_class, [])
     newProjectFile.close()
Пример #18
0
class HeadlessShell(object):
    """
    For now, this class is just a stand-in for the GUI shell (used when running from the command line).
    """
    
    def __init__(self, workflow_cmdline_args=None):
        self._workflow_cmdline_args = workflow_cmdline_args or []
        self.projectManager = None

    @property
    def workflow(self):
        return self.projectManager.workflow

    def createAndLoadNewProject(self, newProjectFilePath, workflow_class):
        hdf5File = ProjectManager.createBlankProjectFile(newProjectFilePath)
        readOnly = False
        self.projectManager = ProjectManager( workflow_class,
                                              headless=True,
                                              workflow_cmdline_args=self._workflow_cmdline_args  )
        self.projectManager._loadProject(hdf5File, newProjectFilePath, readOnly)
        
    def openProjectFile(self, projectFilePath):
        # Make sure all workflow sub-classes have been loaded,
        #  so we can detect the workflow type in the project.
        import ilastik.workflows
        try:
            # Open the project file
            hdf5File, workflow_class, _ = ProjectManager.openProjectFile(projectFilePath)
            
            # Create our project manager
            # This instantiates the workflow and applies all settings from the project.
            self.projectManager = ProjectManager( workflow_class,
                                                  headless=True,
                                                  workflow_cmdline_args=self._workflow_cmdline_args )
            self.projectManager._loadProject(hdf5File, projectFilePath, readOnly = False)
            
        except ProjectManager.ProjectVersionError:
            # Couldn't open project.  Try importing it.
            oldProjectFilePath = projectFilePath
            name, ext = os.path.splitext(oldProjectFilePath)
    
            # Create a brand new project file.
            projectFilePath = name + "_imported" + ext
            logger.info("Importing project as '" + projectFilePath + "'")
            hdf5File = ProjectManager.createBlankProjectFile(projectFilePath)

            # For now, we assume that any imported projects are pixel classification workflow projects.
            import ilastik.workflows
            default_workflow = ilastik.workflows.pixelClassification.PixelClassificationWorkflow

            # Create the project manager.
            # Here, we provide an additional parameter: the path of the project we're importing from. 
            self.projectManager = ProjectManager( default_workflow,
                                                  importFromPath=oldProjectFilePath,
                                                  headless=True )
            self.projectManager._importProject(oldProjectFilePath, hdf5File, projectFilePath,readOnly = False)

    def closeCurrentProject(self):
        self.projectManager._closeCurrentProject()
        self.projectManager.cleanUp()
        self.projectManager = None
Пример #19
0
class IlastikShell(QMainWindow):
    """
    The GUI's main window.  Simply a standard 'container' GUI for one or more applets.
    """
    def __init__(self,
                 workflow=[],
                 parent=None,
                 flags=QtCore.Qt.WindowFlags(0),
                 sideSplitterSizePolicy=SideSplitterSizePolicy.Manual):
        QMainWindow.__init__(self, parent=parent, flags=flags)
        # Register for thunk events (easy UI calls from non-GUI threads)
        self.thunkEventHandler = ThunkEventHandler(self)

        self._sideSplitterSizePolicy = sideSplitterSizePolicy

        self.projectManager = ProjectManager()

        import inspect, os
        ilastikShellFilePath = os.path.dirname(
            inspect.getfile(inspect.currentframe()))
        uic.loadUi(ilastikShellFilePath + "/ui/ilastikShell.ui", self)
        self._applets = []
        self.appletBarMapping = {}

        self.setAttribute(Qt.WA_AlwaysShowToolTips)

        if 'Ubuntu' in platform.platform():
            # Native menus are prettier, but aren't working on Ubuntu at this time (Qt 4.7, Ubuntu 11)
            self.menuBar().setNativeMenuBar(False)

        (self._projectMenu, self._shellActions) = self._createProjectMenu()
        self._settingsMenu = self._createSettingsMenu()
        self.menuBar().addMenu(self._projectMenu)
        self.menuBar().addMenu(self._settingsMenu)

        self.updateShellProjectDisplay()

        self.progressDisplayManager = ProgressDisplayManager(self.statusBar)

        for applet in workflow:
            self.addApplet(applet)

        self.appletBar.expanded.connect(self.handleAppleBarItemExpanded)
        self.appletBar.clicked.connect(self.handleAppletBarClick)
        self.appletBar.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)

        # By default, make the splitter control expose a reasonable width of the applet bar
        self.mainSplitter.setSizes([300, 1])

        self.currentAppletIndex = 0

        self.currentImageIndex = -1
        self.populatingImageSelectionCombo = False
        self.imageSelectionCombo.currentIndexChanged.connect(
            self.changeCurrentInputImageIndex)

        self.enableWorkflow = False  # Global mask applied to all applets
        self._controlCmds = [
        ]  # Track the control commands that have been issued by each applet so they can be popped.
        self._disableCounts = [
        ]  # Controls for each applet can be disabled by his peers.
        # No applet can be enabled unless his disableCount == 0

    def _createProjectMenu(self):
        # Create a menu for "General" (non-applet) actions
        menu = QMenu("&Project", self)

        shellActions = ShellActions()

        # Menu item: New Project
        shellActions.newProjectAction = menu.addAction("&New Project...")
        shellActions.newProjectAction.setShortcuts(QKeySequence.New)
        shellActions.newProjectAction.triggered.connect(
            self.onNewProjectActionTriggered)

        # Menu item: Open Project
        shellActions.openProjectAction = menu.addAction("&Open Project...")
        shellActions.openProjectAction.setShortcuts(QKeySequence.Open)
        shellActions.openProjectAction.triggered.connect(
            self.onOpenProjectActionTriggered)

        # Menu item: Save Project
        shellActions.saveProjectAction = menu.addAction("&Save Project")
        shellActions.saveProjectAction.setShortcuts(QKeySequence.Save)
        shellActions.saveProjectAction.triggered.connect(
            self.onSaveProjectActionTriggered)

        # Menu item: Save Project As
        shellActions.saveProjectAsAction = menu.addAction(
            "&Save Project As...")
        shellActions.saveProjectAsAction.setShortcuts(QKeySequence.SaveAs)
        shellActions.saveProjectAsAction.triggered.connect(
            self.onSaveProjectAsActionTriggered)

        # Menu item: Save Project Snapshot
        shellActions.saveProjectSnapshotAction = menu.addAction(
            "&Take Snapshot...")
        shellActions.saveProjectSnapshotAction.triggered.connect(
            self.onSaveProjectSnapshotActionTriggered)

        # Menu item: Import Project
        shellActions.importProjectAction = menu.addAction("&Import Project...")
        shellActions.importProjectAction.triggered.connect(
            self.onImportProjectActionTriggered)

        # Menu item: Quit
        shellActions.quitAction = menu.addAction("&Quit")
        shellActions.quitAction.setShortcuts(QKeySequence.Quit)
        shellActions.quitAction.triggered.connect(self.onQuitActionTriggered)
        shellActions.quitAction.setShortcut(QKeySequence.Quit)

        return (menu, shellActions)

    def _createSettingsMenu(self):
        menu = QMenu("&Settings", self)

        # Menu item: Keyboard Shortcuts

        def editShortcuts():
            mgrDlg = ShortcutManagerDlg(self)

        shortcutsAction = menu.addAction("&Keyboard Shortcuts")
        shortcutsAction.triggered.connect(editShortcuts)

        return menu

    def show(self):
        """
        Show the window, and enable/disable controls depending on whether or not a project file present.
        """
        super(IlastikShell, self).show()
        self.enableWorkflow = (self.projectManager.currentProjectFile
                               is not None)
        self.updateAppletControlStates()
        self.updateShellProjectDisplay()
        if self._sideSplitterSizePolicy == SideSplitterSizePolicy.Manual:
            self.autoSizeSideSplitter(SideSplitterSizePolicy.AutoLargestDrawer)
        else:
            self.autoSizeSideSplitter(SideSplitterSizePolicy.AutoCurrentDrawer)

    def updateShellProjectDisplay(self):
        """
        Update the title bar and allowable shell actions based on the state of the currently loaded project.
        """
        windowTitle = "ilastik - "
        projectPath = self.projectManager.currentProjectPath
        if projectPath is None:
            windowTitle += "No Project Loaded"
        else:
            windowTitle += projectPath

        readOnly = self.projectManager.currentProjectIsReadOnly
        if readOnly:
            windowTitle += " [Read Only]"

        self.setWindowTitle(windowTitle)

        # Enable/Disable menu items
        projectIsOpen = self.projectManager.currentProjectFile is not None
        self._shellActions.saveProjectAction.setEnabled(
            projectIsOpen and not readOnly)  # Can't save a read-only project
        self._shellActions.saveProjectAsAction.setEnabled(projectIsOpen)
        self._shellActions.saveProjectSnapshotAction.setEnabled(projectIsOpen)

    def setImageNameListSlot(self, multiSlot):
        assert multiSlot.level == 1
        self.imageNamesSlot = multiSlot

        def insertImageName(index, slot):
            self.imageSelectionCombo.setItemText(index, slot.value)
            if self.currentImageIndex == -1:
                self.changeCurrentInputImageIndex(index)

        def handleImageNameSlotInsertion(multislot, index):
            assert multislot == self.imageNamesSlot
            self.populatingImageSelectionCombo = True
            self.imageSelectionCombo.insertItem(index, "uninitialized")
            self.populatingImageSelectionCombo = False
            multislot[index].notifyDirty(bind(insertImageName, index))

        multiSlot.notifyInserted(bind(handleImageNameSlotInsertion))

        def handleImageNameSlotRemoval(multislot, index):
            # Simply remove the combo entry, which causes the currentIndexChanged signal to fire if necessary.
            self.imageSelectionCombo.removeItem(index)
            if len(multislot) == 0:
                self.changeCurrentInputImageIndex(-1)

        multiSlot.notifyRemove(bind(handleImageNameSlotRemoval))

    def changeCurrentInputImageIndex(self, newImageIndex):
        if newImageIndex != self.currentImageIndex \
        and self.populatingImageSelectionCombo == False:
            if newImageIndex != -1:
                try:
                    # Accessing the image name value will throw if it isn't properly initialized
                    self.imageNamesSlot[newImageIndex].value
                except:
                    # Revert to the original image index.
                    if self.currentImageIndex != -1:
                        self.imageSelectionCombo.setCurrentIndex(
                            self.currentImageIndex)
                    return

            # Alert each central widget and viewer control widget that the image selection changed
            for i in range(len(self._applets)):
                self._applets[i].gui.setImageIndex(newImageIndex)

            self.currentImageIndex = newImageIndex

    def handleAppleBarItemExpanded(self, modelIndex):
        """
        The user wants to view a different applet bar item.
        """
        drawerIndex = modelIndex.row()
        self.setSelectedAppletDrawer(drawerIndex)

    def setSelectedAppletDrawer(self, drawerIndex):
        """
        Show the correct applet central widget, viewer control widget, and applet drawer widget for this drawer index.
        """
        if self.currentAppletIndex != drawerIndex:
            self.currentAppletIndex = drawerIndex
            # Collapse all drawers in the applet bar...
            self.appletBar.collapseAll()
            # ...except for the newly selected item.
            self.appletBar.expand(
                self.getModelIndexFromDrawerIndex(drawerIndex))

            if len(self.appletBarMapping) != 0:
                # Determine which applet this drawer belongs to
                assert drawerIndex in self.appletBarMapping
                applet_index = self.appletBarMapping[drawerIndex]

                # Select the appropriate central widget, menu widget, and viewer control widget for this applet
                self.appletStack.setCurrentIndex(applet_index)
                self.viewerControlStack.setCurrentIndex(applet_index)
                self.menuBar().clear()
                self.menuBar().addMenu(self._projectMenu)
                self.menuBar().addMenu(self._settingsMenu)
                for m in self._applets[applet_index].gui.menus():
                    self.menuBar().addMenu(m)

                self.autoSizeSideSplitter(self._sideSplitterSizePolicy)

    def getModelIndexFromDrawerIndex(self, drawerIndex):
        drawerTitleItem = self.appletBar.invisibleRootItem().child(drawerIndex)
        return self.appletBar.indexFromItem(drawerTitleItem)

    def autoSizeSideSplitter(self, sizePolicy):
        if sizePolicy == SideSplitterSizePolicy.Manual:
            # In manual mode, don't resize the splitter at all.
            return

        if sizePolicy == SideSplitterSizePolicy.AutoCurrentDrawer:
            # Get the height of the current applet drawer
            rootItem = self.appletBar.invisibleRootItem()
            appletDrawerItem = rootItem.child(self.currentAppletIndex).child(0)
            appletDrawerWidget = self.appletBar.itemWidget(appletDrawerItem, 0)
            appletDrawerHeight = appletDrawerWidget.frameSize().height()

        if sizePolicy == SideSplitterSizePolicy.AutoLargestDrawer:
            appletDrawerHeight = 0
            # Get the height of the largest drawer in the bar
            for drawerIndex in range(len(self.appletBarMapping)):
                rootItem = self.appletBar.invisibleRootItem()
                appletDrawerItem = rootItem.child(drawerIndex).child(0)
                appletDrawerWidget = self.appletBar.itemWidget(
                    appletDrawerItem, 0)
                appletDrawerHeight = max(
                    appletDrawerHeight,
                    appletDrawerWidget.frameSize().height())

        # Get total height of the titles in the applet bar (not the widgets)
        firstItem = self.appletBar.invisibleRootItem().child(0)
        titleHeight = self.appletBar.visualItemRect(firstItem).size().height()
        numDrawers = len(self.appletBarMapping)
        totalTitleHeight = numDrawers * titleHeight

        # Auto-size the splitter height based on the height of the applet bar.
        totalSplitterHeight = sum(self.sideSplitter.sizes())
        appletBarHeight = totalTitleHeight + appletDrawerHeight + 10  # Add a small margin so the scroll bar doesn't appear
        self.sideSplitter.setSizes(
            [appletBarHeight, totalSplitterHeight - appletBarHeight])

    def handleAppletBarClick(self, modelIndex):
        # If the user clicks on a top-level item, automatically expand it.
        if modelIndex.parent() == self.appletBar.rootIndex():
            self.appletBar.expand(modelIndex)
        else:
            self.appletBar.setCurrentIndex(modelIndex.parent())

    def addApplet(self, app):
        assert isinstance(
            app, Applet), "Applets must inherit from Applet base class."
        assert app.base_initialized, "Applets must call Applet.__init__ upon construction."

        assert issubclass(
            type(app.gui), AppletGuiInterface
        ), "Applet GUIs must conform to the Applet GUI interface."

        self._applets.append(app)
        applet_index = len(self._applets) - 1
        self.appletStack.addWidget(app.gui.centralWidget())

        # Viewer controls are optional. If the applet didn't provide one, create an empty widget for him.
        if app.gui.viewerControlWidget() is None:
            self.viewerControlStack.addWidget(QWidget(parent=self))
        else:
            self.viewerControlStack.addWidget(app.gui.viewerControlWidget())

        # Add rows to the applet bar model
        rootItem = self.appletBar.invisibleRootItem()

        # Add all of the applet bar's items to the toolbox widget
        for controlName, controlGuiItem in app.gui.appletDrawers():
            appletNameItem = QTreeWidgetItem(self.appletBar,
                                             QtCore.QStringList(controlName))
            appletNameItem.setFont(0, QFont("Ubuntu", 14))
            drawerItem = QTreeWidgetItem(appletNameItem)
            drawerItem.setSizeHint(0, controlGuiItem.frameSize())
            #            drawerItem.setBackground( 0, QBrush( QColor(224, 224, 224) ) )
            #            drawerItem.setForeground( 0, QBrush( QColor(0,0,0) ) )
            self.appletBar.setItemWidget(drawerItem, 0, controlGuiItem)

            # Since each applet can contribute more than one applet bar item,
            #  we need to keep track of which applet this item is associated with
            self.appletBarMapping[rootItem.childCount() - 1] = applet_index

        # Set up handling of GUI commands from this applet
        app.guiControlSignal.connect(
            bind(self.handleAppletGuiControlSignal, applet_index))
        self._disableCounts.append(0)
        self._controlCmds.append([])

        # Set up handling of progress updates from this applet
        self.progressDisplayManager.addApplet(applet_index, app)

        # Set up handling of shell requests from this applet
        app.shellRequestSignal.connect(
            partial(self.handleShellRequest, applet_index))

        self.projectManager.addApplet(app)

        return applet_index

    def handleAppletGuiControlSignal(self,
                                     applet_index,
                                     command=ControlCommand.DisableAll):
        """
        Applets fire a signal when they want other applet GUIs to be disabled.
        This function handles the signal.
        Each signal is treated as a command to disable other applets.
        A special command, Pop, undoes the applet's most recent command (i.e. re-enables the applets that were disabled).
        If an applet is disabled twice (e.g. by two different applets), then it won't become enabled again until both commands have been popped.
        """
        if command == ControlCommand.Pop:
            command = self._controlCmds[applet_index].pop()
            step = -1  # Since we're popping this command, we'll subtract from the disable counts
        else:
            step = 1
            self._controlCmds[applet_index].append(
                command
            )  # Push command onto the stack so we can pop it off when the applet isn't busy any more

        # Increase the disable count for each applet that is affected by this command.
        for index, count in enumerate(self._disableCounts):
            if (command == ControlCommand.DisableAll) \
            or (command == ControlCommand.DisableDownstream and index > applet_index) \
            or (command == ControlCommand.DisableUpstream and index < applet_index) \
            or (command == ControlCommand.DisableSelf and index == applet_index):
                self._disableCounts[index] += step

        # Update the control states in the GUI thread
        self.thunkEventHandler.post(self.updateAppletControlStates)

    def handleShellRequest(self, applet_index, requestAction):
        """
        An applet is asking us to do something.  Handle the request.
        """
        with Tracer(traceLogger):
            if requestAction == ShellRequest.RequestSave:
                # Call the handler directly to ensure this is a synchronous call (not queued to the GUI thread)
                self.projectManager.saveProject()

    def __len__(self):
        return self.appletBar.count()

    def __getitem__(self, index):
        return self._applets[index]

    def ensureNoCurrentProject(self, assertClean=False):
        """
        Close the current project.  If it's dirty, we ask the user for confirmation.
        
        The assertClean parameter is for tests.  Setting it to True will raise an assertion if the project was dirty.
        """
        closeProject = True
        if self.projectManager.isProjectDataDirty():
            # Testing assertion
            assert not assertClean, "Expected a clean project but found it to be dirty!"

            message = "Your current project is about to be closed, but it has unsaved changes which will be lost.\n"
            message += "Are you sure you want to proceed?"
            buttons = QMessageBox.Yes | QMessageBox.Cancel
            response = QMessageBox.warning(self,
                                           "Discard unsaved changes?",
                                           message,
                                           buttons,
                                           defaultButton=QMessageBox.Cancel)
            closeProject = (response == QMessageBox.Yes)

        if closeProject:
            self.closeCurrentProject()

        return closeProject

    def closeCurrentProject(self):
        for applet in self._applets:
            applet.gui.reset()
        self.projectManager.closeCurrentProject()
        self.enableWorkflow = False
        self.updateAppletControlStates()
        self.updateShellProjectDisplay()

    def onNewProjectActionTriggered(self):
        logger.debug("New Project action triggered")

        # Make sure the user is finished with the currently open project
        if not self.ensureNoCurrentProject():
            return

        newProjectFilePath = self.getProjectPathToCreate()

        if newProjectFilePath is not None:
            self.createAndLoadNewProject(newProjectFilePath)

    def createAndLoadNewProject(self, newProjectFilePath):
        newProjectFile = self.projectManager.createBlankProjectFile(
            newProjectFilePath)
        self.loadProject(newProjectFile, newProjectFilePath, False)

    def getProjectPathToCreate(self,
                               defaultPath=None,
                               caption="Create Ilastik Project"):
        """
        Ask the user where he would like to create a project file.
        """
        if defaultPath is None:
            defaultPath = os.path.expanduser("~/MyProject.ilp")

        fileSelected = False
        while not fileSelected:
            projectFilePath = QFileDialog.getSaveFileName(
                self,
                caption,
                defaultPath,
                "Ilastik project files (*.ilp)",
                options=QFileDialog.Options(QFileDialog.DontUseNativeDialog))

            # If the user cancelled, stop now
            if projectFilePath.isNull():
                return None

            projectFilePath = str(projectFilePath)
            fileSelected = True

            # Add extension if necessary
            fileExtension = os.path.splitext(projectFilePath)[1].lower()
            if fileExtension != '.ilp':
                projectFilePath += ".ilp"
                if os.path.exists(projectFilePath):
                    # Since we changed the file path, we need to re-check if we're overwriting an existing file.
                    message = "A file named '" + projectFilePath + "' already exists in this location.\n"
                    message += "Are you sure you want to overwrite it?"
                    buttons = QMessageBox.Yes | QMessageBox.Cancel
                    response = QMessageBox.warning(
                        self,
                        "Overwrite existing project?",
                        message,
                        buttons,
                        defaultButton=QMessageBox.Cancel)
                    if response == QMessageBox.Cancel:
                        # Try again...
                        fileSelected = False

        return projectFilePath

    def onImportProjectActionTriggered(self):
        """
        Import an existing project into a new file.
        This involves opening the old file, saving it to a new file, and then opening the new file.
        """
        logger.debug("Import Project Action")

        if not self.ensureNoCurrentProject():
            return

        # Find the directory of the most recently *imported* project
        mostRecentImportPath = PreferencesManager().get(
            'shell', 'recently imported')
        if mostRecentImportPath is not None:
            defaultDirectory = os.path.split(mostRecentImportPath)[0]
        else:
            defaultDirectory = os.path.expanduser('~')

        # Select the paths to the ilp to import and the name of the new one we'll create
        importedFilePath = self.getProjectPathToOpen(defaultDirectory)
        if importedFilePath is not None:
            PreferencesManager().set('shell', 'recently imported',
                                     importedFilePath)
            defaultFile, ext = os.path.splitext(importedFilePath)
            defaultFile += "_imported"
            defaultFile += ext
            newProjectFilePath = self.getProjectPathToCreate(defaultFile)

        # If the user didn't cancel
        if importedFilePath is not None and newProjectFilePath is not None:
            self.importProject(importedFilePath, newProjectFilePath)

    def importProject(self, originalPath, newProjectFilePath):
        newProjectFile = self.projectManager.createBlankProjectFile(
            newProjectFilePath)
        self.projectManager.importProject(originalPath, newProjectFile,
                                          newProjectFilePath)

        self.updateShellProjectDisplay()

        # Enable all the applet controls
        self.enableWorkflow = True
        self.updateAppletControlStates()

    def getProjectPathToOpen(self, defaultDirectory):
        """
        Return the path of the project the user wants to open (or None if he cancels).
        """
        projectFilePath = QFileDialog.getOpenFileName(
            self,
            "Open Ilastik Project",
            defaultDirectory,
            "Ilastik project files (*.ilp)",
            options=QFileDialog.Options(QFileDialog.DontUseNativeDialog))

        # If the user canceled, stop now
        if projectFilePath.isNull():
            return None

        return str(projectFilePath)

    def onOpenProjectActionTriggered(self):
        logger.debug("Open Project action triggered")

        # Make sure the user is finished with the currently open project
        if not self.ensureNoCurrentProject():
            return

        # Find the directory of the most recently opened project
        mostRecentProjectPath = PreferencesManager().get(
            'shell', 'recently opened')
        if mostRecentProjectPath is not None:
            defaultDirectory = os.path.split(mostRecentProjectPath)[0]
        else:
            defaultDirectory = os.path.expanduser('~')

        projectFilePath = self.getProjectPathToOpen(defaultDirectory)
        if projectFilePath is not None:
            PreferencesManager().set('shell', 'recently opened',
                                     projectFilePath)
            self.openProjectFile(projectFilePath)

    def openProjectFile(self, projectFilePath):
        try:
            hdf5File, readOnly = self.projectManager.openProjectFile(
                projectFilePath)
        except ProjectManager.ProjectVersionError, e:
            QMessageBox.warning(
                self, "Old Project", "Could not load old project file: " +
                projectFilePath + ".\nPlease try 'Import Project' instead.")
        except ProjectManager.FileMissingError:
            QMessageBox.warning(
                self, "Missing File",
                "Could not find project file: " + projectFilePath)
Пример #20
0
class HeadlessShell(object):
    """
    For now, this class is just a stand-in for the GUI shell (used when running from the command line).
    """

    def __init__(self, workflow_cmdline_args=None):
        self._workflow_cmdline_args = workflow_cmdline_args or []
        self.projectManager = None

    @property
    def workflow(self):
        return self.projectManager.workflow

    def createAndLoadNewProject(self, newProjectFilePath, workflow_class):
        hdf5File = ProjectManager.createBlankProjectFile(newProjectFilePath)
        readOnly = False
        self.projectManager = ProjectManager(
            self, workflow_class, headless=True, workflow_cmdline_args=self._workflow_cmdline_args
        )
        self.projectManager._loadProject(hdf5File, newProjectFilePath, readOnly)

    def openProjectFile(self, projectFilePath):
        # Make sure all workflow sub-classes have been loaded,
        #  so we can detect the workflow type in the project.
        import ilastik.workflows

        try:
            # Open the project file
            hdf5File, workflow_class, _ = ProjectManager.openProjectFile(projectFilePath)

            if workflow_class is None:
                # If the project file has no known workflow, we assume pixel classification
                import ilastik.workflows

                workflow_class = ilastik.workflows.pixelClassification.PixelClassificationWorkflow
                import warnings

                warnings.warn(
                    "Your project file ({}) does not specify a workflow type.  "
                    "Assuming Pixel Classification".format(projectFilePath)
                )

            # Create our project manager
            # This instantiates the workflow and applies all settings from the project.
            self.projectManager = ProjectManager(
                self, workflow_class, headless=True, workflow_cmdline_args=self._workflow_cmdline_args
            )
            self.projectManager._loadProject(hdf5File, projectFilePath, readOnly=False)

        except ProjectManager.ProjectVersionError:
            # Couldn't open project.  Try importing it.
            oldProjectFilePath = projectFilePath
            name, ext = os.path.splitext(oldProjectFilePath)

            # Create a brand new project file.
            projectFilePath = name + "_imported" + ext
            logger.info("Importing project as '" + projectFilePath + "'")
            hdf5File = ProjectManager.createBlankProjectFile(projectFilePath)

            # For now, we assume that any imported projects are pixel classification workflow projects.
            import ilastik.workflows

            default_workflow = ilastik.workflows.pixelClassification.PixelClassificationWorkflow

            # Create the project manager.
            # Here, we provide an additional parameter: the path of the project we're importing from.
            self.projectManager = ProjectManager(
                self, default_workflow, importFromPath=oldProjectFilePath, headless=True
            )
            self.projectManager._importProject(oldProjectFilePath, hdf5File, projectFilePath, readOnly=False)

    def setAppletEnabled(self, applet, enabled):
        """
        Provided here to satisfy the ShellABC.
        For now, HeadlessShell has no concept of "enabled" or "disabled" applets.
        """
        pass

    def enableProjectChanges(self, enabled):
        """
        Provided here to satisfy the ShellABC.
        For now, HeadlessShell has no mechanism for closing projects.
        """
        pass

    def closeCurrentProject(self):
        self.projectManager._closeCurrentProject()
        self.projectManager.cleanUp()
        self.projectManager = None
Пример #21
0
class HeadlessShell(object):
    """
    For now, this class is just a stand-in for the GUI shell (used when running from the command line).
    """
    
    def __init__(self, workflow_cmdline_args=None):
        self._workflow_cmdline_args = workflow_cmdline_args or []
        self.projectManager = None

    @property
    def workflow(self):
        return self.projectManager.workflow

    @property
    def currentImageIndex(self):
        return -1

    def createAndLoadNewProject(self, newProjectFilePath, workflow_class):
        hdf5File = ProjectManager.createBlankProjectFile(newProjectFilePath)
        readOnly = False
        self.projectManager = ProjectManager( self,
                                              workflow_class,
                                              headless=True,
                                              workflow_cmdline_args=self._workflow_cmdline_args  )
        self.projectManager._loadProject(hdf5File, newProjectFilePath, readOnly)
        self.projectManager.saveProject()

    @classmethod
    def downloadProjectFromDvid(cls, dvid_key_url):
        dvid_key_url = str(dvid_key_url)
        
        # By convention, command-line users specify the location of the project 
        # keyvalue data using the same format that the DVID API itself uses.
        url_format = "^protocol://hostname/api/node/uuid/kv_instance_name(/key/keyname)?"
        for field in ['protocol', 'hostname', 'uuid', 'kv_instance_name', 'keyname']:
            url_format = url_format.replace( field, '(?P<' + field + '>[^?/]+)' )

        match = re.match( url_format, dvid_key_url )
        if not match:
            # DVID is the only url-based format we support right now.
            # So if it looks like the user gave a URL that isn't a valid DVID node, then error.
            raise RuntimeError("Invalid URL format for DVID key-value data: {}".format(projectFilePath))

        fields = match.groupdict()            
        projectFilePath = ProjectManager.downloadProjectFromDvid( fields['hostname'],
                                                                  fields['uuid'],
                                                                  fields['kv_instance_name'],
                                                                  fields['keyname'] )
        return projectFilePath
        
    def openProjectFile(self, projectFilePath, force_readonly=False):
        # If the user gave a URL to a DVID key, then download the project file from dvid first.
        # (So far, DVID is the only type of URL access we support for project files.)
        if isUrl(projectFilePath):
            projectFilePath = HeadlessShell.downloadProjectFromDvid(projectFilePath)

        # Make sure all workflow sub-classes have been loaded,
        #  so we can detect the workflow type in the project.
        import ilastik.workflows
        try:
            # Open the project file
            hdf5File, workflow_class, readOnly = ProjectManager.openProjectFile(projectFilePath, force_readonly)

            # If there are any "creation-time" command-line args saved to the project file,
            #  load them so that the workflow can be instantiated with the same settings 
            #  that were used when the project was first created. 
            project_creation_args = []
            if "workflow_cmdline_args" in hdf5File.keys():
                if len(hdf5File["workflow_cmdline_args"]) > 0:
                    project_creation_args = map(str, hdf5File["workflow_cmdline_args"][...])

            if workflow_class is None:
                # If the project file has no known workflow, we assume pixel classification
                import ilastik.workflows
                workflow_class = ilastik.workflows.pixelClassification.PixelClassificationWorkflow
                import warnings
                warnings.warn( "Your project file ({}) does not specify a workflow type.  "
                               "Assuming Pixel Classification".format( projectFilePath ) )            
            
            # Create our project manager
            # This instantiates the workflow and applies all settings from the project.
            self.projectManager = ProjectManager( self,
                                                  workflow_class,
                                                  headless=True,
                                                  workflow_cmdline_args=self._workflow_cmdline_args,
                                                  project_creation_args=project_creation_args )
            self.projectManager._loadProject(hdf5File, projectFilePath, readOnly)

        except ProjectManager.FileMissingError:
            logger.error("Couldn't find project file: {}".format( projectFilePath ))
            raise            
        except ProjectManager.ProjectVersionError:
            # Couldn't open project.  Try importing it.
            oldProjectFilePath = projectFilePath
            name, ext = os.path.splitext(oldProjectFilePath)
    
            # Create a brand new project file.
            projectFilePath = name + "_imported" + ext
            logger.info("Importing project as '" + projectFilePath + "'")
            hdf5File = ProjectManager.createBlankProjectFile(projectFilePath)

            # For now, we assume that any imported projects are pixel classification workflow projects.
            import ilastik.workflows
            default_workflow = ilastik.workflows.pixelClassification.PixelClassificationWorkflow

            # Create the project manager.
            self.projectManager = ProjectManager( self,
                                                  default_workflow,
                                                  headless=True,
                                                  workflow_cmdline_args=self._workflow_cmdline_args,
                                                  project_creation_args=self._workflow_cmdline_args )

            self.projectManager._importProject(importFromPath, hdf5File, projectFilePath)

    def setAppletEnabled(self, applet, enabled):
        """
        Provided here to satisfy the ShellABC.
        For now, HeadlessShell has no concept of "enabled" or "disabled" applets.
        """
        pass

    def isAppletEnabled(self, applet):
        return False

    def enableProjectChanges(self, enabled):
        """
        Provided here to satisfy the ShellABC.
        For now, HeadlessShell has no mechanism for closing projects.
        """
        pass

    def closeCurrentProject(self):
        self.projectManager._closeCurrentProject()
        self.projectManager.cleanUp()
        self.projectManager = None
Пример #22
0
 def create_project_file(self, workflow_class, project_file_name):
     newProjectFile = ProjectManager.createBlankProjectFile(project_file_name, workflow_class, [])
     newProjectFile.close()
Пример #23
0
class HeadlessShell(object):
    """
    For now, this class is just a stand-in for the GUI shell (used when running from the command line).
    """
    
    def __init__(self, workflow_cmdline_args=None):
        self._workflow_cmdline_args = workflow_cmdline_args or []
        self.projectManager = None

    @property
    def workflow(self):
        return self.projectManager.workflow

    def createAndLoadNewProject(self, newProjectFilePath, workflow_class):
        hdf5File = ProjectManager.createBlankProjectFile(newProjectFilePath)
        readOnly = False
        self.projectManager = ProjectManager( self,
                                              workflow_class,
                                              headless=True,
                                              workflow_cmdline_args=self._workflow_cmdline_args  )
        self.projectManager._loadProject(hdf5File, newProjectFilePath, readOnly)
        self.projectManager.saveProject()
        
    def openProjectFile(self, projectFilePath):
        # Make sure all workflow sub-classes have been loaded,
        #  so we can detect the workflow type in the project.
        import ilastik.workflows
        try:
            # Open the project file
            hdf5File, workflow_class, _ = ProjectManager.openProjectFile(projectFilePath)

            # If there are any "creation-time" command-line args saved to the project file,
            #  load them so that the workflow can be instantiated with the same settings 
            #  that were used when the project was first created. 
            project_creation_args = []
            if "workflow_cmdline_args" in hdf5File.keys():
                if len(hdf5File["workflow_cmdline_args"]) > 0:
                    project_creation_args = map(str, hdf5File["workflow_cmdline_args"][...])

            if workflow_class is None:
                # If the project file has no known workflow, we assume pixel classification
                import ilastik.workflows
                workflow_class = ilastik.workflows.pixelClassification.PixelClassificationWorkflow
                import warnings
                warnings.warn( "Your project file ({}) does not specify a workflow type.  "
                               "Assuming Pixel Classification".format( projectFilePath ) )            
            
            # Create our project manager
            # This instantiates the workflow and applies all settings from the project.
            self.projectManager = ProjectManager( self,
                                                  workflow_class,
                                                  headless=True,
                                                  workflow_cmdline_args=self._workflow_cmdline_args,
                                                  project_creation_args=project_creation_args )
            self.projectManager._loadProject(hdf5File, projectFilePath, readOnly = False)

        except ProjectManager.FileMissingError:
            logger.error("Couldn't find project file: {}".format( projectFilePath ))
            raise            
        except ProjectManager.ProjectVersionError:
            # Couldn't open project.  Try importing it.
            oldProjectFilePath = projectFilePath
            name, ext = os.path.splitext(oldProjectFilePath)
    
            # Create a brand new project file.
            projectFilePath = name + "_imported" + ext
            logger.info("Importing project as '" + projectFilePath + "'")
            hdf5File = ProjectManager.createBlankProjectFile(projectFilePath)

            # For now, we assume that any imported projects are pixel classification workflow projects.
            import ilastik.workflows
            default_workflow = ilastik.workflows.pixelClassification.PixelClassificationWorkflow

            # Create the project manager.
            # Here, we provide an additional parameter: the path of the project we're importing from. 
            self.projectManager = ProjectManager( self,
                                                  default_workflow,
                                                  importFromPath=oldProjectFilePath,
                                                  headless=True,
                                                  workflow_cmdline_args=self._workflow_cmdline_args,
                                                  project_creation_args=self._workflow_cmdline_args )

            self.projectManager._importProject(oldProjectFilePath, hdf5File, projectFilePath,readOnly = False)

    def setAppletEnabled(self, applet, enabled):
        """
        Provided here to satisfy the ShellABC.
        For now, HeadlessShell has no concept of "enabled" or "disabled" applets.
        """
        pass

    def isAppletEnabled(self, applet):
        return False

    def enableProjectChanges(self, enabled):
        """
        Provided here to satisfy the ShellABC.
        For now, HeadlessShell has no mechanism for closing projects.
        """
        pass

    def closeCurrentProject(self):
        self.projectManager._closeCurrentProject()
        self.projectManager.cleanUp()
        self.projectManager = None