Exemplo n.º 1
0
class ResourceManager(PrymatexComponent, QtCore.QObject):
    def __init__(self, **kwargs):
        super(ResourceManager, self).__init__(**kwargs)
        # Icon Handler
        QtGui.QIcon._fromTheme = QtGui.QIcon.fromTheme
        QtGui.QIcon.fromTheme = self.icon_from_theme

        self.prymatex_resources = []
        
        self.base = Resource(self.application().createNamespace('base', ''))
        # Shortcut Models
        self.shortcutsTreeModel = ShortcutsTreeModel(self)

    def loadResources(self, message_handler):
        # Load standard shortcuts
        self.shortcutsTreeModel.loadStandardSequences(self.resources())

    @classmethod
    def contributeToSettings(cls):
        from prymatex.gui.settings.shortcuts import ShortcutsSettingsWidget

        return [ ShortcutsSettingsWidget ]

    def registerShortcut(self, qobject, sequence):
        return self.shortcutsTreeModel.registerShortcut(qobject, sequence)
        
    def addNamespace(self, namespace):
        self.prymatex_resources.insert(0, Resource(namespace))

    def get_provider(self, names, builtins=True):
        names = names if isinstance(names, (list, tuple)) else [ names ]  
        resources = [res for res in self.prymatex_resources if res.namespace().name in names]
        if builtins:
            resources = resources + self.builtins()
        resources.append(self.base)
        logger.debug("get_provider for [%s] got [%s]" % (
            ",".join(names), 
            ",".join([r.namespace().name for r in resources])
        ))
        return ResourceProvider(resources)

    def builtins(self):
        return [res for res in self.prymatex_resources if res.namespace().builtin]

    def providerForClass(self, componentClass):
        resource_name = getattr(componentClass, "RESOURCES", '')
        return self.get_provider(resource_name)

    def icon_from_theme(self, index):
        # TODO: default provider
        for res in self.prymatex_resources:
            icon = res.get_icon(index)
            if not icon.isNull():
                return icon
        return QtGui.QIcon()
Exemplo n.º 2
0
    def __init__(self, **kwargs):
        super(ResourceManager, self).__init__(**kwargs)
        # Icon Handler
        QtGui.QIcon._fromTheme = QtGui.QIcon.fromTheme
        QtGui.QIcon.fromTheme = self.icon_from_theme

        self.prymatex_resources = []
        
        self.base = Resource(self.application().createNamespace('base', ''))
        # Shortcut Models
        self.shortcutsTreeModel = ShortcutsTreeModel(self)
Exemplo n.º 3
0
Arquivo: app.py Projeto: D3f0/prymatex
    def __init__(self, **kwargs):
        """Inicialización de la aplicación."""
        super(PrymatexApplication, self).__init__(**kwargs)

        # Some init's
        self.setApplicationName(prymatex.__name__.title())
        self.setApplicationVersion(prymatex.__version__)
        self.setOrganizationDomain(prymatex.__url__)
        self.setOrganizationName(prymatex.__author__)
        self.platform = sys.platform

        resources.loadPrymatexResources(config.PMX_SHARE_PATH)

        # Connects
        self.aboutToQuit.connect(self.closePrymatex)
        self.componentInstances = {}
        self.shortcutsTreeModel = ShortcutsTreeModel(self)
        self.shortcutsTreeModel.loadStandardSequences()

        self.replaceSysExceptHook()
    
        # Windows
        self._main_windows = []
Exemplo n.º 4
0
Arquivo: app.py Projeto: D3f0/prymatex
class PrymatexApplication(PrymatexComponent, QtGui.QApplication):
    """The application instance.
    There can't be two apps running simultaneously, since configuration issues may occur.
    The application loads the Support."""

    # ---------------------- Settings
    SETTINGS_GROUP = "Global"

    @ConfigurableItem()
    def qtStyle(self, styleName):
        if styleName:
            self.setStyle(styleName)

    @ConfigurableItem(default = "default")
    def qtStyleSheet(self, styleSheetName):
        if styleSheetName in resources.STYLESHEETS:
            self.setStyleSheet(resources.STYLESHEETS[styleSheetName])

    askAboutExternalDeletions = ConfigurableItem(default=False)
    askAboutExternalChanges = ConfigurableItem(default=False)

    RESTART_CODE = 1000

    def __init__(self, **kwargs):
        """Inicialización de la aplicación."""
        super(PrymatexApplication, self).__init__(**kwargs)

        # Some init's
        self.setApplicationName(prymatex.__name__.title())
        self.setApplicationVersion(prymatex.__version__)
        self.setOrganizationDomain(prymatex.__url__)
        self.setOrganizationName(prymatex.__author__)
        self.platform = sys.platform

        resources.loadPrymatexResources(config.PMX_SHARE_PATH)

        # Connects
        self.aboutToQuit.connect(self.closePrymatex)
        self.componentInstances = {}
        self.shortcutsTreeModel = ShortcutsTreeModel(self)
        self.shortcutsTreeModel.loadStandardSequences()

        self.replaceSysExceptHook()
    
        # Windows
        self._main_windows = []

    # ------ exception and logger handlers
    def getLogger(self, *largs, **kwargs):
        return logger.getLogger(*largs, **kwargs)

    def replaceSysExceptHook(self):
        # Exceptions, Print exceptions in a window
        def displayExceptionDialog(exctype, value, traceback):
            ''' Display a nice dialog showing the python traceback'''
            from prymatex.gui.emergency.tracedialog import PMXTraceBackDialog
            sys.__excepthook__(exctype, value, traceback)
            PMXTraceBackDialog.fromSysExceptHook(exctype, value, traceback).exec_()

        sys.excepthook = displayExceptionDialog

        # Route messages to application logger
        QtCore.qInstallMsgHandler(self.qtMessageHandler)

    def qtMessageHandler(self, msgType, msgString):
        ''' Route Qt messaging system into Prymatex/Python one'''
        if msgType == QtCore.QtDebugMsg:
            self.logger.debug(msgString)
        elif msgType == QtCore.QtWarningMsg:
            self.logger.warn(msgString)
        elif msgType == QtCore.QtCriticalMsg:
            self.logger.critical(msgString)
        elif msgType == QtCore.QtFatalMsg:
            self.logger.fatal(msgString)
        elif msgType == QtCore.QtSystemMsg:
            self.logger.debug("System: %s" % msgString)

    # ------- prymatex's micro kernel
    def applyOptions(self, options):
        # The basic managers
        self.options = options

        # Prepare profile
        from prymatex.managers.profile import ProfileManager
        self.extendComponent(ProfileManager)
        self.profileManager = ProfileManager(parent = self)
        self.currentProfile = self.profileManager.currentProfile(self.options.profile)
        if self.currentProfile is None:
            return False

        if self.options.reset_settings:
            self.currentProfile.clear()

        # Prepare settings for application
        self.populateComponentClass(PrymatexApplication)
        self.currentProfile.registerConfigurableInstance(self)

        logger.config(self.options.verbose, self.currentProfile.PMX_LOG_PATH, self.options.log_pattern)

        self.checkSingleInstance()

        return True

    def installTranslator(self):
        pass
        #slanguage = QtCore.QLocale.system().name()
        #print language
        #self.translator = QtCore.QTranslator()
        #print os.path.join(config.PMX_SHARE_PATH, "Languages")

        #self.translator.load(settings.LANGUAGE)
        #self.installTranslator(translator)

    # ---------------------- PMXBaseComponent methods
    @classmethod
    def contributeToSettings(cls):
        from prymatex.gui.settings.general import GeneralSettingsWidget
        from prymatex.gui.settings.shortcuts import ShortcutsSettingsWidget

        return [ GeneralSettingsWidget, ShortcutsSettingsWidget ]

    def loadGraphicalUserInterface(self):
        self.showMessage = self.logger.info
        if not self.options.no_splash:
            from prymatex.widgets.splash import SplashScreen
            splash_image = resources.get_image('newsplash')
            splash = SplashScreen(splash_image)
            splash.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.SplashScreen)

            splashFont = QtGui.QFont("Monospace", 11)
            splashFont.setStyleStrategy(QtGui.QFont.PreferAntialias)

            splash.setFont(splashFont)
            splash.setMask(splash_image.mask())
            splash.show()
            self.showMessage = splash.showMessage
        try:
            # Build Managers
            self.pluginManager = self.buildPluginManager()      # WARN: FIST Plugin Manager
            self.storageManager = self.buildStorageManager()    # Persistence system Manager
            self.supportManager = self.buildSupportManager()    # Support Manager
            self.fileManager = self.buildFileManager()          # File Manager
            self.projectManager = self.buildProjectManager()    # Project Manager
            self.schedulerManager =  self.buildSchedulerManager()
            self.serverManager = self.buildServerManager()

            # Load Bundles
            self.supportManager.loadSupport(self.showMessage)
            
            # Load Projects
            self.projectManager.loadProjects(self.showMessage)
            
            # Create Main Window
            main_window = self.buildMainWindow()
            
            # Change messages handler
            self.showMessage = main_window.showMessage

            # Load settings
            self.profileManager.loadSettings(self.showMessage)

            if not self.options.no_splash:
                splash.finish(main_window)

            main_window.show()
            self.logger.info("Application startup")
        except KeyboardInterrupt:
            self.logger.critical("Quit signal catched during application startup. Quiting...")
            self.quit()

    def unloadGraphicalUserInterface(self):
        #TODO: ver como dejar todo lindo y ordenado para terminar correctamente
        #if self.zmqContext is not None:
        #    self.zmqContext.destroy()
        for main_window in self.mainWindows():
            main_window.close()
            main_window.deleteLater()

    def restart(self):
        self.exit(self.RESTART_CODE)

    def checkSingleInstance(self):
        """Checks if there's another instance using current profile"""
        self.fileLock = os.path.join(self.currentProfile.PMX_PROFILE_PATH, 'prymatex.pid')

        if os.path.exists(self.fileLock):
            #Mejorar esto
            pass
            #raise exceptions.AlreadyRunningError('%s seems to be runnig. Please close the instance or run other profile.' % (self.currentProfile.PMX_PROFILE_NAME))
        else:
            f = open(self.fileLock, 'w')
            f.write('%s' % self.applicationPid())
            f.close()

    # -------------------- Managers
    def buildPluginManager(self):
        from prymatex.managers.plugins import PluginManager
        #manager = self.createComponentInstance(PluginManager, parent = self)
        self.populateComponentClass(PluginManager)

        manager = PluginManager(parent = self)

        self.currentProfile.registerConfigurableInstance(manager)

        manager.initialize(parent = self)

        manager.addNamespace('prymatex', config.PMX_SHARE_PATH)

        manager.addNamespace('user', config.PMX_HOME_PATH)
        
        manager.loadPlugins()
        return manager

    def buildSupportManager(self):
        from prymatex.managers.support import SupportManager
        manager = self.createComponentInstance(SupportManager, parent = self)

        #Prepare prymatex namespace
        manager.addNamespace('prymatex', config.PMX_SHARE_PATH)

        #Prepare user namespace
        manager.addNamespace('user', config.PMX_HOME_PATH)

        # Update environment
        manager.updateEnvironment({
            # TextMate Compatible :P
            'TM_APP_PATH': config.PMX_APP_PATH,
            'TM_BUNDLES_PATH': manager.environmentVariables()['PMX_BUNDLES_PATH'],
            'TM_PID': self.applicationPid(),
            #Prymatex
            'PMX_APP_NAME': self.applicationName().title(),
            'PMX_APP_PATH': config.PMX_APP_PATH,
            'PMX_PREFERENCES_PATH': self.currentProfile.value('PMX_PREFERENCES_PATH'),
            'PMX_VERSION': self.applicationVersion(),
            'PMX_PID': self.applicationPid(),
            #User
            'PMX_HOME_PATH': config.PMX_HOME_PATH,
            'PMX_PROFILE_NAME': self.currentProfile.value('PMX_PROFILE_NAME'),
            'PMX_PROFILE_PATH': self.currentProfile.value('PMX_PROFILE_PATH'),
            'PMX_TMP_PATH': self.currentProfile.value('PMX_TMP_PATH'),
            'PMX_LOG_PATH': self.currentProfile.value('PMX_LOG_PATH')
        })

        return manager

    def buildFileManager(self):
        from prymatex.managers.files import FileManager
        manager = self.createComponentInstance(FileManager, parent = self)

        manager.fileSytemChanged.connect(self.on_fileManager_fileSytemChanged)
        return manager

    def buildProjectManager(self):
        from prymatex.managers.projects import ProjectManager
        return self.createComponentInstance(ProjectManager, parent = self)

    def buildStorageManager(self):
        from prymatex.managers.storage import StorageManager
        return self.createComponentInstance(StorageManager, parent = self)

    def buildSchedulerManager(self):
        from prymatex.managers.coroutines import SchedulerManager
        return self.createComponentInstance(SchedulerManager, parent = self)

    def buildServerManager(self):
        from prymatex.managers.server import ServerManager
        return self.createComponentInstance(ServerManager, parent = self)

    # --------------------- Application events
    def closePrymatex(self):
        self.logger.debug("Close")

        self.storageManager.close()
        for main_window in self.mainWindows():
            self.currentProfile.saveState(main_window)
        self.currentProfile.sync()
        if os.path.exists(self.fileLock):
            os.unlink(self.fileLock)

    # --------------------- Exend and populate components
    def extendComponent(self, componentClass):
        componentClass.application = self
        componentClass.logger = self.getLogger('.'.join([componentClass.__module__, componentClass.__name__]))

    def populateComponentClass(self, componentClass):
        self.extendComponent(componentClass)
        if issubclass(componentClass, PrymatexComponent):
            # Add configurable class to the current profile 
            self.currentProfile.addConfigurableClass(componentClass)
            # Add settings widgets
            for settingClass in componentClass.contributeToSettings():
                self.extendComponent(settingClass)
                settingWidget = settingClass(
                    settings = componentClass._settings,
                    profile = self.currentProfile)
                componentClass._settings.addDialog(settingWidget)
                self.profileManager.registerSettingsWidget(settingWidget)

    # ------------------- Create components
    def createComponentInstance(self, componentClass, **kwargs):
        if not hasattr(componentClass, 'application') or componentClass.application != self:
            self.populateComponentClass(componentClass)

        # ------------------- Build
        buildedInstances = []
        def buildComponentInstance(klass, **kwargs):
            component = klass(**kwargs)

            # Add components
            componentClasses = self.pluginManager.findComponentsForClass(klass)
            for componentClass in componentClasses:
                # Filter editors, editors create explicit
                if issubclass(componentClass, PrymatexEditor):
                    continue
                subComponent = buildComponentInstance(componentClass, parent = component)
                component.addComponent(subComponent)
            buildedInstances.append(component)
            return component

        component = buildComponentInstance(componentClass, **kwargs)

        # ------------------- Configure Bottom-up
        for instance in buildedInstances[::-1]:
            self.currentProfile.registerConfigurableInstance(instance)

        # ------------------- Initialize Top-down
        for instance in buildedInstances:
            instance.initialize()
            # Shortcuts
            for settings in instance.contributeToShortcuts():
                create_shortcut(component, settings, sequence_handler = self.registerShortcut)

        # -------------------- Store
        self.componentInstances.setdefault(componentClass, []).append(component)

        return component

    def deleteComponentInstance(self, component):
        self.currentProfile.unregisterConfigurableInstance(component)
        component.deleteLater()
        
    # ------------ Find Component
    def componentHierarchyForClass(self, componentClass):
        return self.pluginManager.componentHierarchyForClass(componentClass)

    def findComponentsForClass(self, componentClass):
        return self.pluginManager.findComponentsForClass(componentClass)

    # ------------- Settings access
    def settingValue(self, settingPath):
        return self.currentProfile.settingValue(settingPath)

    def registerSettingHook(self, settingPath, handler):
        self.currentProfile.registerSettingHook(settingPath, handler)

    def unregisterSettingHook(self, settingPath, handler):
        self.currentProfile.unregisterSettingHook(settingPath, handler)

    # ------------- Editors and windows handle
    def createEditorInstance(self, class_name = None, file_path=None, 
        cursor_position = None, parent=None):
        editorClass = None
        if class_name is not None:
            editorClass = self.pluginManager.findEditorClassByName(class_name)
        elif file_path is not None:
            editorClass = self.pluginManager.findEditorClassForFile(file_path)
        if editorClass is None:
            editorClass = self.pluginManager.defaultEditor()

        # Exists file ?
        if file_path and not self.fileManager.isfile(file_path):
            file_path = None
        editor = self.createComponentInstance(editorClass, 
            parent = parent, 
            file_path = file_path
        )
        if file_path:
            editor.open(file_path)
        if cursor_position:
            editor.setCursorPosition(cursor_position)
        return editor

    def deleteEditorInstance(self, editor):
        editor.close()
        self.deleteComponentInstance(editor)

    def findEditorForFile(self, filepath):
        for main_window in self.mainWindows():
            editor = main_window.findEditorForFile(filepath)
            if editor:
                return main_window, editor

    def mainWindows(self):
        return self._main_windows
        
    def buildMainWindow(self):
        """Creates the windows"""
        from prymatex.gui.main import PrymatexMainWindow

        main_window = self.createComponentInstance(PrymatexMainWindow)

        self.currentProfile.restoreState(main_window)

        if not main_window.editors():
            main_window.addEmptyEditor()
        self._main_windows.append(main_window)
        return main_window

    def currentWindow(self):
        # TODO Aca retornar la window actual
        return self._main_windows[0]

    def canBeHandled(self, filepath):
        #from prymatex.utils.pyqtdebug import ipdb_set_trace
        #ipdb_set_trace()
        ext = os.path.splitext(filepath)[1].replace('.', '')
        for fileTypes in [syntax.item.fileTypes for syntax in
                          self.supportManager.getAllSyntaxes()
                          if hasattr(syntax.item, 'fileTypes') and
                          syntax.item.fileTypes]:

            if ext in fileTypes:
                return True
        return False

    __options = None

    @property
    def options(self):
        return self.__options

    @options.setter
    def options(self, value):
        self.__options = value
        # Send some singal? Don't think so yet, this is intended to be set at startup

    # ---- Open (file, directory, url, canelones)
    def openFile(self, filepath, cursorPosition=None, focus=True, main_window=None):
        """Open a editor in current window"""
        file_path = self.fileManager.normcase(filepath)

        if self.fileManager.isOpen(file_path):
            main_window, editor = self.findEditorForFile(file_path)
            if editor is not None:
                main_window.setCurrentEditor(editor)
                if cursorPosition is not None:
                    editor.setCursorPosition(cursorPosition)
        elif self.fileManager.exists(file_path):
            main_window = main_window or self.currentWindow()
            editor = self.createEditorInstance(
                file_path = file_path,
                cursor_position = cursorPosition,
                parent = main_window,
                )
            # TODO el dialogo de no tengo editor para ese tipo de archivo
            if editor:
                main_window.tryCloseEmptyEditor()
                main_window.addEditor(editor, focus)

    def openDirectory(self, directoryPath):
        raise NotImplementedError("Directory contents should be opened as files here")

    def openUrl(self, url):
        if isinstance(url, six.string_types):
            url = QtCore.QUrl(url)
        if url.scheme() == "txmt":
            sourceFile = url.queryItemValue('url')
            position = (0, 0)
            line = url.queryItemValue('line')
            if line.isdigit():
                position = (int(line) - 1, position[1])
            column = url.queryItemValue('column')
            if column.isdigit():
                position = (position[0], int(column) - 1)
            if sourceFile:
                filePath = QtCore.QUrl(sourceFile, QtCore.QUrl.TolerantMode).toLocalFile()
                self.openFile(filePath, cursorPosition = position)
            else:
                self.currentWindow().currentEditor().setCursorPosition(position)
        elif url.scheme() == "file":
            self.openFile(url.toLocalFile())
        else:
            QtGui.QDesktopServices.openUrl(url)

    def openPath(self, path):
        if os.path.exists(path):
            if os.path.isfile(path):
                self.openFile(path)
            else:
                self.openDirectory(path)

    # ------- Shortcuts
    def registerShortcut(self, qobject, sequence):
        """Register QAction or QShortcut to Prymatex main application,
        with sequence
        """
        self.shortcutsTreeModel.registerShortcut(qobject, sequence)

    def applyShortcuts(self):
        self.shortcutsTreeModel.applyShortcuts()

    def checkExternalAction(self, main_window, editor):
        if editor.isExternalChanged():
            message = "The file '%s' has been changed on the file system, Do you want to replace the editor contents with these changes?"
            result = QtGui.QMessageBox.question(editor, _("File changed"),
                                                _(message) % editor.filePath,
                                                buttons=QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
                                                defaultButton=QtGui.QMessageBox.Yes) if self.askAboutExternalChanges else QtGui.QMessageBox.Yes
            if result == QtGui.QMessageBox.Yes:
                editor.reload()
            elif result == QtGui.QMessageBox.No:
                pass
        elif editor.isExternalDeleted():
            message = "The file '%s' has been deleted or is not accessible. Do you want to save your changes or close the editor without saving?"
            result = QtGui.QMessageBox.question(editor, _("File deleted"),
                                                _(message) % editor.filePath,
                                                buttons=QtGui.QMessageBox.Save | QtGui.QMessageBox.Close,
                                                defaultButton=QtGui.QMessageBox.Close) if self.askAboutExternalDeletions else QtGui.QMessageBox.Close
            if result == QtGui.QMessageBox.Close:
                main_window.closeEditor(editor)
            elif result == QtGui.QMessageBox.Save:
                main_window.saveEditor(editor)

    def on_fileManager_fileSytemChanged(self, filePath, change):
        main_window, editor = self.findEditorForFile(filePath)
        editor.setExternalAction(change)
        if main_window.currentEditor() == editor:
            self.checkExternalAction(main_window, editor)

    def __str__(self):
        return '<PrymatexApplication at {} PID: {}>'.format(hash(self), os.getpid())

    __unicode__ = __repr__ = __str__