Ejemplo n.º 1
0
class NeuroptikonApp(wx.App):
    def __init__(self, *args, **keywordArgs):
        self._networks = set([])
        self.preferences = None
        self.inspector = None

        self.config = None
        self._console = None

        # Keep track of open frames so we can close them on quit.
        # TODO: use wxWidget's document/view framework instead
        self._frames = []

        self._customizationDocSetId = wx.NewId()
        self._dataMgmtDocSetId = wx.NewId()
        self._scriptingDocSetId = wx.NewId()
        self._uiDocSetId = wx.NewId()

        wx.App.__init__(self, *args, **keywordArgs)

    def OnInit(self):

        import Inspectors, Layouts, Shapes  # pylint: disable=W0612

        neuroptikon.library = Library()
        self._loadDefaultLibraryItems()

        neuroptikon.config = wx.Config('Neuroptikon')

        if platform.system() == 'Darwin':
            wx.MenuBar.MacSetCommonMenuBar(self.menuBar())

        neuroptikon.scriptLocals = self.scriptLocals

        self.preferences = Preferences()
        self.inspector = InspectorFrame()

        self.SetExitOnFrameDelete(False)

        startupScript = neuroptikon.config.Read('Startup Script', '')
        try:
            exec startupScript in self.scriptLocals()
        except:
            (exceptionType, exceptionValue,
             exceptionTraceback) = sys.exc_info()
            frames = traceback.extract_tb(exceptionTraceback)[1:]
            message = gettext(
                'An error occurred while running the startup script:')
            subMessage = str(
                exceptionValue
            ) + ' (' + exceptionType.__name__ + ')' + '\n\nTraceback:\n' + ''.join(
                traceback.format_list(frames))
            if platform.system() == 'Darwin':
                wx.MessageBox(subMessage, message, style=wx.ICON_ERROR | wx.OK)
            else:
                wx.MessageBox(message + '\n\n' + subMessage,
                              'Neuroptikon',
                              parent=self,
                              style=wx.ICON_ERROR | wx.OK)

        # open an empty network by default
        # TODO: pref to re-open last doc?
        self.onNewNetwork()

        return True

    def _loadDefaultLibraryItems(self):
        neuroptikon.library.add(
            Neurotransmitter('ACh', gettext('Acetylcholine')))
        neuroptikon.library.add(Neurotransmitter('DA', gettext('Dopamine')))
        neuroptikon.library.add(
            Neurotransmitter('epinephrine', gettext('Epinephrine')))
        neuroptikon.library.add(
            Neurotransmitter('GABA', gettext('Gamma-aminobutyric acid'),
                             gettext('GABA')))
        neuroptikon.library.add(Neurotransmitter('GLU', gettext('Glutamate')))
        neuroptikon.library.add(Neurotransmitter('glycine',
                                                 gettext('Glycine')))
        neuroptikon.library.add(
            Neurotransmitter('histamine', gettext('Histamine')))
        neuroptikon.library.add(
            Neurotransmitter('norepinephrine', gettext('Norepinephrine')))
        neuroptikon.library.add(Neurotransmitter('5-HT', gettext('Serotonin')))

        # pylint: disable=E1101
        neuroptikon.library.add(
            NeuronClass(identifier='basket',
                        name=gettext('Basket cell'),
                        polarity=Neuron.Polarity.MULTIPOLAR,
                        functions=[Neuron.Function.INTERNEURON],
                        activation='inhibitory',
                        neurotransmitters=[
                            neuroptikon.library.neurotransmitter('GABA')
                        ]))
        neuroptikon.library.add(
            NeuronClass(identifier='pyramidal',
                        name=gettext('Pyramidal cell'),
                        polarity=Neuron.Polarity.MULTIPOLAR,
                        activation='excitatory',
                        neurotransmitters=[
                            neuroptikon.library.neurotransmitter('glutamate')
                        ]))
        neuroptikon.library.add(
            NeuronClass(
                identifier='RSad pyramidal',
                name=gettext('RSad Pyramidal cell'),
                parentClass=neuroptikon.library.neuronClass('pyramidal')))
        neuroptikon.library.add(
            NeuronClass(
                identifier='RSna pyramidal',
                name=gettext('RSna Pyramidal cell'),
                parentClass=neuroptikon.library.neuronClass('pyramidal')))
        neuroptikon.library.add(
            NeuronClass(
                identifier='IB pyramidal',
                name=gettext('IB Pyramidal cell'),
                parentClass=neuroptikon.library.neuronClass('pyramidal')))
        neuroptikon.library.add(
            NeuronClass(
                identifier='betz',
                name=gettext('Betz cell'),
                parentClass=neuroptikon.library.neuronClass('pyramidal'),
                functions=[Neuron.Function.MOTOR]))
        neuroptikon.library.add(
            NeuronClass(identifier='medium spiny',
                        name=gettext('Medium spiny neuron'),
                        polarity=Neuron.Polarity.MULTIPOLAR,
                        activation='inhibitory',
                        neurotransmitters=[
                            neuroptikon.library.neurotransmitter('GABA')
                        ]))
        neuroptikon.library.add(
            NeuronClass(identifier='purkinje',
                        name=gettext('Purkinje cell'),
                        polarity=Neuron.Polarity.MULTIPOLAR,
                        activation='inhibitory',
                        neurotransmitters=[
                            neuroptikon.library.neurotransmitter('GABA')
                        ]))
        neuroptikon.library.add(
            NeuronClass(identifier='renshaw',
                        name=gettext('Renshaw cell'),
                        polarity=Neuron.Polarity.MULTIPOLAR,
                        functions=[Neuron.Function.INTERNEURON],
                        activation='inhibitory',
                        neurotransmitters=[
                            neuroptikon.library.neurotransmitter('glycine')
                        ]))
        neuroptikon.library.add(
            NeuronClass(identifier='anterior horn',
                        name=gettext('Anterior horn cell')))
        # pylint: enable=E1101

        neuroptikon.library.add(Modality('light', gettext('Light')))
        neuroptikon.library.add(Modality('odor', gettext('Odor')))
        neuroptikon.library.add(Modality('sound', gettext('Sound')))
        neuroptikon.library.add(Modality('taste', gettext('Taste')))

        # Load any ontologies in <root>/Ontologies
        try:
            for fileName in os.listdir(neuroptikon.rootDir + os.sep +
                                       'Ontologies'):
                if fileName.endswith('.obo'):
                    identifier = os.path.splitext(fileName)[0]
                    ontology = Ontology(identifier)
                    try:
                        ontology.importOBO(neuroptikon.rootDir + os.sep +
                                           'Ontologies' + os.sep + fileName)
                        neuroptikon.library.add(ontology)
                    except:
                        (exceptionType, exceptionValue,
                         exceptionTraceback_) = sys.exc_info()
                        print 'Could not import ontology ' + fileName + ' (' + str(
                            exceptionValue
                        ) + ' (' + exceptionType.__name__ + ')' + ')'
        except:
            (exceptionType, exceptionValue,
             exceptionTraceback_) = sys.exc_info()
            print 'Could not import ontologies (' + str(
                exceptionValue) + ' (' + exceptionType.__name__ + ')' + ')'

        # Load any textures in <root>/Textures
        try:
            for fileName in os.listdir(neuroptikon.rootDir + os.sep +
                                       'Textures'):
                if fileName != '.svn':
                    identifier = os.path.splitext(fileName)[0]
                    texture = Texture(identifier, gettext(identifier))
                    if texture.loadImage(neuroptikon.rootDir + os.sep +
                                         'Textures' + os.sep + fileName):
                        neuroptikon.library.add(texture)
        except:
            (exceptionType, exceptionValue,
             exceptionTraceback_) = sys.exc_info()
            print 'Could not import textures (' + str(
                exceptionValue) + ' (' + exceptionType.__name__ + ')' + ')'

    def onQuit(self, event_):
        continueQuit = True
        for frame in list(self._frames):
            if frame.isModified():
                frame.Raise()
            if not frame.Close():
                continueQuit = False
                break
        if continueQuit and not any(
                self._frames) and platform.system() == 'Darwin':
            self.ExitMainLoop()

    def menuBar(self, frame=None):
        menuBar = wx.MenuBar()

        fileMenu = wx.Menu()
        self.Bind(
            wx.EVT_MENU, self.onNewNetwork,
            fileMenu.Append(wx.ID_NEW, gettext('New Network\tCtrl-N'),
                            gettext('Open a new network window')))
        self.Bind(
            wx.EVT_MENU, self.onOpenNetwork,
            fileMenu.Append(wx.ID_OPEN, gettext('Open Network...\tCtrl-O'),
                            gettext('Open a previously saved network')))
        closeItem = fileMenu.Append(
            wx.ID_CLOSE, gettext('Close Network\tCtrl-W'),
            gettext('Close the current network window'))
        if frame:
            frame.Bind(wx.EVT_MENU, frame.onCloseWindow, closeItem)
        else:
            closeItem.Enable(False)
        saveItem = fileMenu.Append(wx.ID_SAVE,
                                   gettext('Save Network...\tCtrl-S'),
                                   gettext('Save the current network'))
        if frame:
            frame.Bind(wx.EVT_MENU, frame.onSaveNetwork, saveItem)
        else:
            saveItem.Enable(False)
        saveAsItem = fileMenu.Append(wx.ID_SAVEAS,
                                     gettext('Save As...\tCtrl-Shift-S'),
                                     gettext('Save to a new file'))
        if frame:
            frame.Bind(wx.EVT_MENU, frame.onSaveNetworkAs, saveAsItem)
        else:
            saveAsItem.Enable(False)
        fileMenu.AppendSeparator()
        runScriptItem = fileMenu.Append(wx.NewId(),
                                        gettext('Run Script...\tCtrl-R'),
                                        gettext('Run a console script file'))
        if frame:
            frame.Bind(wx.EVT_MENU, frame.onRunScript, runScriptItem)
        else:
            runScriptItem.Enable(False)
        self.Bind(
            wx.EVT_MENU, self.onBrowseLibrary,
            fileMenu.Append(wx.NewId(),
                            gettext('Browse the Library\tCtrl-Alt-L'),
                            gettext('Open the Library window')))
        self.Bind(
            wx.EVT_MENU, self.onOpenConsole,
            fileMenu.Append(wx.NewId(),
                            gettext('Open the Console\tCtrl-Alt-O'),
                            gettext('Open the Console window')))
        self.Bind(
            wx.EVT_MENU, self.onOpenPreferences,
            fileMenu.Append(wx.ID_PREFERENCES, gettext('Settings'),
                            gettext('Change Neuroptikon preferences')))
        fileMenu.AppendSeparator()
        self.Bind(
            wx.EVT_MENU, self.onQuit,
            fileMenu.Append(wx.ID_EXIT, gettext('E&xit\tCtrl-Q'),
                            gettext('Exit the Neuroptikon application')))
        menuBar.Append(fileMenu, gettext('&File'))

        helpMenu = wx.Menu()
        self.Bind(
            wx.EVT_MENU, self.onShowDocumentation,
            helpMenu.Append(
                self._customizationDocSetId,
                gettext('Customizing Neuroptikon'),
                gettext('Show the documentation on customizing Neuroptikon')))
        self.Bind(
            wx.EVT_MENU, self.onShowDocumentation,
            helpMenu.Append(
                self._dataMgmtDocSetId, gettext('Data Management'),
                gettext(
                    'Show the documentation on managing Neuroptikon data')))
        self.Bind(
            wx.EVT_MENU, self.onShowDocumentation,
            helpMenu.Append(
                self._scriptingDocSetId, gettext('Scripting Interface'),
                gettext('Show the documentation on scripting Neuroptikon')))
        self.Bind(
            wx.EVT_MENU, self.onShowDocumentation,
            helpMenu.Append(
                self._uiDocSetId, gettext('User Interface'),
                gettext(
                    'Show the documentation on interacting with Neuroptikon')))
        helpMenu.AppendSeparator()
        self.Bind(
            wx.EVT_MENU, self.onSendFeedback,
            helpMenu.Append(wx.NewId(), gettext('Report Bug or Enhancement'),
                            gettext('Report a bug or request a new feature')))
        if platform.system() != 'Darwin':
            helpMenu.AppendSeparator()
        self.Bind(
            wx.EVT_MENU, self.onAboutNeuroptikon,
            helpMenu.Append(wx.ID_ABOUT, gettext('About Neuroptikon'),
                            gettext('Information about this program')))
        menuBar.Append(helpMenu, gettext('&Help'))

        return menuBar

    def scriptLocals(self):
        layoutClasses = {}
        for layoutClass in display.layout.layoutClasses():
            layoutClasses[layoutClass.name()] = layoutClass
        shapeClasses = {}
        for shapeClass in display.shape.shapeClasses():
            shapeClasses[shapeClass.__name__] = shapeClass
        scriptLocals = {
            'createNetwork': self.createNetwork,
            'displayNetwork': self.displayNetwork,
            'openNetwork': self.openNetwork,
            'networks': self.networks,
            'library': neuroptikon.library,
            'Neurotransmitter': Neurotransmitter,
            'NeuronClass': NeuronClass,
            'Modality': Modality,
            'Ontology': Ontology,
            'Texture': Texture,
            'NeuralPolarity': Neuron.Polarity,  # DEPRECATED: remove soon... 
            'NeuralFunction': Neuron.Function,  # DEPRECATED: remove soon...
            'Attribute': Attribute,
            'layouts': layoutClasses,
            'shapes': shapeClasses,
            'Object': Object,
            'showMessage': self.showMessage
        }
        for objectClass in NeuroObject.__subclasses__():  # pylint: disable=E1101
            scriptLocals[objectClass.__name__] = objectClass
        if 'DEBUG' in os.environ:
            scriptLocals['os'] = os
            scriptLocals['sys'] = sys
            scriptLocals['wx'] = wx
            import osg
            scriptLocals['osg'] = osg
            import gc
            scriptLocals['gc'] = gc
            scriptLocals['datetime'] = datetime
            scriptLocals['date'] = date
            scriptLocals['time'] = time
            try:
                import objgraph
                scriptLocals['objgraph'] = objgraph
            except ImportError:
                pass
            from pydispatch import dispatcher
            scriptLocals['dispatcher'] = dispatcher

        return scriptLocals

    def onRunScript(self, event_):
        dlg = wx.FileDialog(None, gettext('Choose a script to run'), 'Scripts',
                            '', '*.py', wx.OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            prevDir = os.getcwd()
            os.chdir(os.path.dirname(dlg.GetPath()))
            try:
                execfile(dlg.GetPath(), self.scriptLocals())
            except:
                (exceptionType, exceptionValue,
                 exceptionTraceback) = sys.exc_info()
                wx.MessageBox(
                    str(exceptionValue) + ' (' + exceptionType.__name__ + ')',
                    gettext('An error occurred at line %d of the script:') %
                    exceptionTraceback.tb_next.tb_lineno,
                    style=wx.ICON_ERROR | wx.OK)
            os.chdir(prevDir)
        dlg.Destroy()

    def onOpenConsole(self, event_):
        if not self._console:
            confDir = wx.StandardPaths.Get().GetUserDataDir()
            if not os.path.exists(confDir):
                os.mkdir(confDir)
            fileName = os.path.join(confDir, 'Console.config')
            self.config = wx.FileConfig(localFilename=fileName)
            self.config.SetRecordDefaults(True)
            self._console = py.shell.ShellFrame(title=gettext('Console'),
                                                config=self.config,
                                                dataDir=confDir,
                                                locals=self.scriptLocals())
            #TODO: need to just hide the console window on close or set up some kind of callback to clear _console when the console closes
        self._console.Show()
        self._console.Raise()

    def onNewNetwork(self, event_=None):
        network = self.createNetwork()
        self.displayNetwork(network)

    def createNetwork(self):
        network = Network()
        #TODO: implement doc/view framework
        return network

    def openNetwork(self, filePath):
        """
        Open the previously saved network at the indicated path.
        
        Any displays will be restored or a default one will be opened.
        """

        from neuroptikon_frame import NeuroptikonFrame

        # TODO: raise an existing window if the network is already open

        try:
            xmlTree = ElementTree.parse(filePath)

            # Instantiate the network
            networkElement = xmlTree.find('Network')
            network = Network._fromXMLElement(networkElement)
            if network is None:
                raise ValueError, gettext('Could not load the network')
            network.setSavePath(filePath)
            network.setModified(False)
            self._networks.add(network)

            # Instantiate any displays
            for frameElement in xmlTree.findall('DisplayWindow'):
                frame = NeuroptikonFrame._fromXMLElement(frameElement,
                                                         network=network)
                if frame is None:
                    raise ValueError, gettext(
                        'Could not create one of the displays')
                frame.Show(True)
                frame.Raise()
                self._frames.append(frame)

            # Create a default display if none were specified in the file.
            if len(network.displays) == 0:
                self.displayNetwork(network).zoomToFit()
        except:
            raise

    def onOpenNetwork(self, event_):
        dlg = wx.FileDialog(None, gettext('Choose a saved network to open:'),
                            '', '', '*.xml', wx.OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            self.openNetwork(dlg.GetPath())
        dlg.Destroy()

    def networks(self):
        return list(self._networks)

    def releaseNetwork(self, network):
        if not any(network.displays):
            network.removeAllObjects()
            self._networks.remove(network)

    def displayNetwork(self, network, populateDisplay=True):
        """
        Open a new display for the network.
        """

        self._networks.add(network)
        from neuroptikon_frame import NeuroptikonFrame
        frame = NeuroptikonFrame(network=network,
                                 populateDisplay=populateDisplay)
        frame.Show(True)
        frame.Raise()
        self._frames.append(frame)
        return frame.display

    def displayWasClosed(self, displayFrame):
        self._frames.remove(displayFrame)
        if len(self._frames) == 0 and platform.system() != 'Darwin':
            self.ExitMainLoop()

    def onOpenPreferences(self, event_):
        self.preferences.Show(True)
        self.preferences.Raise()
        return self.preferences

    def onBrowseLibrary(self, event_):
        neuroptikon.library.browse()

    def onOpenInspector(self, event_):
        self.inspector.Show(True)
        self.inspector.Raise()
        return self.inspector

    def onShowDocumentation(self, event):
        if event.GetId() == self._customizationDocSetId:
            page = 'Customizing/index.html'
        elif event.GetId() == self._dataMgmtDocSetId:
            page = 'DataManagement.html'
        elif event.GetId() == self._scriptingDocSetId:
            page = 'Scripting/index.html'
        elif event.GetId() == self._uiDocSetId:
            page = 'UserInterface/index.html'

        documentation.showPage(page)

    def onSendFeedback(self, event_):
        dlg = FeedbackDialog()
        if dlg.ShowModal() == wx.ID_OK:
            queryDict = {
                'pid': 10000,
                'summary': dlg.summary(),
                'description': dlg.description(),
                'customfield_10000': dlg.contactEmail()
            }
            if platform.system() == 'Darwin':
                environment = 'OS: Mac OS X ' + platform.mac_ver()[0] + '\n'
            elif platform.system() == 'Windows':
                release, version, csd, ptype = platform.win32_ver()
                environment = 'OS: Windows ' + release + '  ' + version + ' ' + csd + ' ' + ptype + '\n'
            else:
                environment = 'OS: ' + platform.system(
                ) + ' ' + platform.version() + '\n'
            environment += 'Python version: ' + platform.python_version(
            ) + '\n'
            queryDict['environment'] = environment
            if dlg.isBugReport():
                queryDict['issuetype'] = 1
            elif dlg.isFeatureRequest():
                queryDict['issuetype'] = 2
            # task = 3, improvement = 4
            queryDict['versions'] = __version__.JIRA_version_id
            url = 'http://www.neuroptikon.org/issues/secure/CreateIssueDetails.jspa?' + urllib.urlencode(
                queryDict)
            if not wx.LaunchDefaultBrowser(url):
                message = gettext('The issue could not be created')
                resolution = gettext(
                    'Please visit http://www.neuroptikon.org/issues/ to send your feedback.'
                )
                if platform.system() == 'Darwin':
                    wx.MessageBox(resolution, message)
                else:
                    wx.MessageBox(gettext('Send Feedback'),
                                  message + '\n\n' + resolution)

        dlg.Destroy()

    def onAboutNeuroptikon(self, event_):
        info = wx.AboutDialogInfo()
        info.SetName(gettext('Neuroptikon'))
        info.SetVersion(__version__.version)
        info.SetDescription(
            gettext(
                'Developed at the \nJanelia Farm Research Campus by:\n\nFrank Midgley\nVivek Jayaraman\nMitya Chklovskii\nDon Olbris'
            ))
        info.SetCopyright(
            gettext('Copyright \xa9 2010 - Howard Hughes Medical Institute'))
        wx.AboutBox(info)

    def showMessage(self, message, subMessage=None, isError=False):
        """
        Display a message to the user.
        """

        if platform.system() == 'Darwin':
            wx.MessageBox(
                subMessage,
                message or '',
                style=(wx.ICON_ERROR if isError else wx.ICON_INFORMATION)
                | wx.OK)
        else:
            wx.MessageBox(
                'Neuroptikon', (message + '\n\n' +
                                subMessage if message else subMessage or ''),
                style=(wx.ICON_ERROR if isError else wx.ICON_INFORMATION)
                | wx.OK)