예제 #1
0
파일: ddclib.py 프로젝트: olivergs/ddc
class EVOGTKApp(GUIClass):
    """
    EVOGTK application class
    """
    # Application metadata
    metadata={
        'APP_NAME': _('DDC'),
        'APP_CODENAME': 'ddc',
        'APP_VERSION': '0.5',
        'APP_DESC': _('Django Development Console'),
        'APP_COPYRIGHT': '(C) Oliver Gutiérrez',
        'APP_WEBSITE': 'http://www.evosistemas.com',
        'APP_PREFERENCES': {
            'general': {
                'workspacedir': ('str',['fcbWorkspaceDir'],''),
                'history': ('list',[],[],','),
                'lastproject': ('str',[],''),
                'openlastproject': ('bool',['chkOpenLast'],False),
            },
            'log': {
                'internalcolor': ('str',['clbInternalMessage'],'#000088'),
                'errorcolor': ('str',['clbErrorMessage'],'#880000'),
            }
        }
    }

    # Application task modes
    TASK_SERVERSTOPPED=1
    TASK_SERVERRUNNING=2
    TASK_ACTIVEPROJECT=3

    TASKS_MODES={
        evogtk.TASK_MODE_INITIAL: {
            'enable': [],
            'disable': ['actStopServer','actStartServer','actRestartServer','actOpenBrowser','actCloseProject','actSyncDB','actValidate','actCleanup','actCreateUser'],
            'show': [],
            'hide': [],
            'activate': [],
            'deactivate': [],
            'callback': None
        },
        TASK_ACTIVEPROJECT: {
            'enable': ['actStartServer','actCloseProject','actSyncDB','actValidate','actCleanup','actCreateUser'],
            'disable': [],
            'show': [],
            'hide': [],
            'activate': [],
            'deactivate': [],
            'callback': None
        },
        TASK_SERVERSTOPPED: {
            'enable': ['actStartServer'],
            'disable': ['actStopServer','actRestartServer','actOpenBrowser'],
            'show': [],
            'hide': [],
            'activate': [],
            'deactivate': [],
            'callback': None
        },
        TASK_SERVERRUNNING: {
            'enable': ['actStopServer','actRestartServer','actOpenBrowser'],
            'disable': ['actStartServer'],
            'show': [],
            'hide': [],
            'activate': [],
            'deactivate': [],
            'callback': None
        },
    }

    # Initial values
    __app_initialized=False
    __command_in_progress=False
    currentproject=None
    filemonitor=None

    def initialize(self):
        """
        GUI initialization method
        """        
        # Redirect all console output to console log
        sys.stdout = sys.stderr = ConsoleOutputRedirector(self.widgets.txtConsoleLog)
                
        if evogtk.HAS_APPINDICATOR:
            # Initialize app indicator
            self.trayicon=AppIndicator('django-development-console',icon='ddc-indicator',attention_icon='ddc-indicator-attention',menu=self.widgets.mnuIndicator,iconpath=self.PIXMAPS_DIR)
        else:            
            # Fallback to tray icon
            self.trayicon=TrayIcon(self.PIXMAPS_DIR + '/ddc-icon.png',self.metadata['APP_NAME'],menu=self.widgets.mnuIndicator)
            self.trayicon.show()
        
        # Setup console log text styles
        self.setup_text_styles()

        # Setup projects history treeview
        self.projectshistory=TreeViewFactory('list', ['str','str','str','str'], [_('Name'),_('Settings mod.'),_('Port'),_('Path')],treeview=self.widgets.tvProjectsHistory)

        # Setup project files treeview
        self.projectfiles=TreeViewFactory('tree', [['int','str','pixbuf','str']], [_('File')],[0,1],treeview=self.widgets.tvProjectFiles)

        # Setup application list treeview
        self.projectapps=TreeViewFactory('tree', ['str'], [_('Applications')],menu=self.widgets.mnuAppProperties,showmenucheck=self.applications_menu_check,treeview=self.widgets.tvProjectApps)
        self.update_projects_history()

        # Setup settings list treeview
        self.projectsettings=TreeViewFactory('list', ['str','str'], [_('Setting'),_('Value')],treeview=self.widgets.tvProjectSettings)

        # Setup user list treeview
        self.projectusers=TreeViewFactory('list', ['str','str','str','str','bool','bool','bool'], [_('Username'),_('eMail'),_('First name'),_('Last name'),_('Active'),_('Staff'),_('Superuser')],treeview=self.widgets.tvProjectUsers)

        # Initialize SQL textview widgets
        for i in ['Create','Custom','Indexes','Delete']:
            view=generateSourceWidget('sql', highlightline=False, editable=False, visiblecursor=False)
            view.show()
            self.widgets.add_widget(view, 'txtSQL%s' % i)
            self.widgets.get_widget('scrSQL%s' % i).add(view)
            
        # Setup initial task mode
        self.set_gui_task(evogtk.TASK_MODE_INITIAL)

        # Application has been initialized
        self.__app_initialized=True

        # Show main window
        self.show_mainwindow(self.widgets.winMain)
        
        # Open last project if needed
        last='ddc_project_definition-' + self.preferences.general.lastproject
        if self.preferences.general.openlastproject and last and self.preferences.has_section(last):
            data=self.preferences.get_section(last)
            self.load_project(self.preferences.general.lastproject, data['port'], data['path'], data['settingsmod'])
            # Change task mode to active project
            self.set_gui_task(self.TASK_ACTIVEPROJECT)

    def unload(self):
        """
        Unload application callback
        """
        if self.currentproject:
            self.currentproject.stopserver()

    #===========================================================================
    # GUI event callbacks
    #===========================================================================
    def quitApplication(self,widget,event=None):
        """
        Application quit callback
        """
        if widget == self.widgets.winMain:
            self.ui.actToggleMainWindow=False
        else:
            if self.dialogs.msgDialog(_('Do you want to exit %s?') % self.metadata['APP_NAME'], 'question'):
                self.savePreferences()
                self.quit()
        return True

    def openProject(self,widget=None):
        """
        Open a django project folder
        """
        # Reset open project dialog information
        self.ui.entProjectName =''
        self.ui.spnServerPort = 8000
        self.ui.fcbProjectPath = self.preferences.general.workspacedir
        self.ui.entSettingsMod = 'settings'
        self.widgets.entProjectName.grab_focus()
        # Show open dialog
        while self.dialogs.openDialog(self.widgets.winOpenProject) == 1:
            # Get project information variables
            name=path=self.ui.entProjectName.strip()
            port= int(self.ui.spnServerPort)
            path=self.ui.fcbProjectPath
            settingsmod=self.ui.entSettingsMod.strip()
            # Check if all needed information is OK
            if not name.replace(' ','').isalnum():
                self.dialogs.msgDialog(_('Project name must be an alphanumeric value'),'error')
            else:
                if name and port and path and settingsmod:
                    # Close current project
                    self.closeProject()
                    self.load_project(name,port,path,settingsmod)
                    # Change task mode to active project
                    self.set_gui_task(self.TASK_ACTIVEPROJECT)
                    # Continue when finished loading
                    break
                else:
                    self.dialogs.msgDialog(_('You must fill all the project information fields'),'error')
        # Hide open dialog
        self.widgets.winOpenProject.hide()

    def closeProject(self,widget=None):
        """
        Close current project
        """
        if self.currentproject:
            if self.dialogs.msgDialog(_('Do you want to close current project?'),'question'):
                # Remove project path from python path
                if self.currentproject.path in sys.path:
                    sys.path.remove(self.currentproject.path)
                # Stop django development server
                self.currentproject.stopserver()
                # Remove current project variable
                self.currentproject=None
                # Clear project information treeviews
                self.projectfiles.clear()
                self.projectapps.clear()
                self.projectusers.clear()
                self.projectsettings.clear()
                # Return to initial task mode
                self.set_gui_task(evogtk.TASK_MODE_INITIAL)
                self.widgets.winMain.set_title(_('Django Development Console'))

    def useHistorySettings(self,widget):
        """
        Use selected history row settings into open project dialog fields
        """
        # Get currently selected row
        if self.projectshistory.selected():
            name,settingsmod,port,path=self.projectshistory.selected()        
            self.ui.entProjectName = name
            self.ui.spnServerPort = int(port)
            self.ui.fcbProjectPath = path
            self.ui.entSettingsMod = settingsmod

    def openHistoryProject(self,widget,path,view_column):
        """
        Load selected history project directly
        """
        # Get currently selected row
        if self.projectshistory.selected():
            name,settingsmod,port,path=self.projectshistory.selected()
            # Close current project
            self.closeProject()
            self.load_project(name,port,path,settingsmod)
            # Change task mode to active project
            self.set_gui_task(self.TASK_ACTIVEPROJECT)
            self.widgets.winOpenProject.hide()

    def startServer(self,widget=None):
        """
        Start django development server
        """
        if self.currentproject:
            self.currentproject.runserver()
            self.set_gui_task(self.TASK_SERVERRUNNING)
            gobject.timeout_add(200,self.console_update_loop,priority=gobject.PRIORITY_LOW)
            self.logmsg(_('Django development server started'))
            
    def stopServer(self,widget=None):
        """
        Stop django development server
        """
        if self.currentproject:
            self.currentproject.stopserver()
            self.set_gui_task(self.TASK_SERVERSTOPPED)
            self.logmsg(_('Django development server stopped'))

    def restartServer(self,widget=None):
        """
        Restart django development server
        """
        self.logmsg(_('Restarting django development server'))
        self.stopServer()
        self.startServer()
    
    def syncDB(self,widget=None):
        """
        Synchronize current project database
        """
        if self.currentproject:
            self.run_background_task(self.currentproject.syncdb,_('Synchronizing database'))
    
    def validateModels(self,widget=None):
        """
        Verify project models
        """
        if self.currentproject:
            self.run_background_task(self.currentproject.validate,_('Validating installed models'))
    
    def cleanUp(self,widget=None):
        """
        Clean up database
        """
        if self.currentproject:
            self.run_background_task(self.currentproject.cleanup,_('Cleaning up database'))
    
    def openProjectFile(self,widget,path,view_column):
        """
        Open a project file
        """
        selected=self.projectfiles.selected()
        if selected:
            if selected[0]==0:
                if not widget.row_expanded(path):
                    widget.expand_row(path,False)
                else:
                    widget.collapse_row(path)
            else:
                print "Opening %s" % selected[1]

    def saveConsoleLog(self,widget=None):
        """
        Save log contents into a file
        """
        filename=self.dialogs.fileSelDialog('save', _('Select file to save log text'))
        if filename[0]:
            buf=self.widgets.txtConsoleLog.get_buffer()
            start,end=buf.get_bounds()
            text=buf.get_text(start, end, include_hidden_chars=False)
            fd=open(filename[0][0],'wb')
            fd.write(text)
            fd.close()
            self.logmsg(_('Log contents saved to "%s"') % filename[0][0])
    
    def clearConsoleLog(self,widget=None):
        """
        Clear console log contents
        """
        logbuffer=self.widgets.txtConsoleLog.get_buffer()
        logbuffer.delete(*logbuffer.get_bounds())

    def toggleMainWindow(self,widget=None):
        """
        Toggle main window visibility
        """
        if self.ui.actToggleMainWindow:
            self.widgets.winMain.show()
        else:
            self.widgets.winMain.hide()
            
    def createUser(self,widget=None):
        """
        Create new user
        """
        if self.currentproject:
            while self.dialogs.openDialog(self.widgets.winCreateUser) == 1:
                username=self.ui.entCreateUserName
                password=self.ui.entCreateUserPassword
                passwordconfirm=self.ui.entCreateUserPasswordConfirm
                email=self.ui.entCreateUserEmail
                active=self.ui.chkCreateUserActive
                staff=self.ui.chkCreateUserStaff
                superuser=self.ui.chkCreateUserSuperuser    
                if password != passwordconfirm:
                    self.dialogs.msgDialog(_('Specified passwords do not match'), 'error')
                else:
                    result,msg=self.currentproject.create_user(username,password,email,active,staff,superuser)
                    if not result:
                        self.dialogs.msgDialog(str(msg), 'error')
                    else:
                        self.update_users_information()
                        break
            self.widgets.winCreateUser.hide()
    
    def showSQL(self,widget=None):
        """
        Show SQL for selected application
        """
        appname=self.get_selected_project_app()
        if appname:
            self.ui.txtSQLCreate,self.ui.txtSQLCustom,self.ui.txtSQLIndexes,self.ui.txtSQLDelete = self.currentproject.get_sql(appname)
            self.dialogs.openDialog(self.widgets.winSQL,close=True)

    def resetApp(self,widget=None):
        """
        Reset selected application
        """
        appname=self.get_selected_project_app()
        if appname:
            if self.dialogs.msgDialog(_('Do you really want to reset application "%s"?') % appname, 'question'):
                self.currentproject.resetapp(appname)
                self.logmsg(_('Application "%s" has been reseted') % appname)

    def appProperties(self,widget=None):
        """
        # TODO: Show application properties
        """
        appname=self.get_selected_project_app()
        if appname:
            self.dialogs.msgDialog(_('Application name: %s') % appname, 'info')

    def removeFromHistory(self,widget=None):
        """
        Remove selected project from history list
        """
        if self.projectshistory.selected():
            name=self.projectshistory.selected()[0]
            if self.dialogs.msgDialog(_('Do you really want to remove project "%s" from history?') % name, 'question'):
                if self.preferences.has_section('ddc_project_definition-' + name):
                    self.preferences.remove_section('ddc_project_definition-' + name)
                    history=self.preferences.general.history
                    history.remove(name)
                    self.preferences.general.history=history
                    self.preferences.save()
                self.update_projects_history()

    def clearProjectHistory(self,widget=None):
        """
        Remove all projects from history list
        """
        if self.dialogs.msgDialog(_('Are you sure you really want to remove all projects in history?'), 'question'):
            for name in self.preferences.general.history:
                if self.preferences.has_section('ddc_project_definition-' + name):
                    self.preferences.remove_section('ddc_project_definition-' + name)
            self.preferences.general.history=[]
            self.preferences.save()
            self.update_projects_history()

    def openBrowser(self,widget=None):
        """
        Open web browser pointing to django development server
        """
        openWithDefaultApp('http://localhost:%s' % self.currentproject.port)

    #===========================================================================
    # Utility methods
    #===========================================================================
    def new_editor_tab(self,filename=None):
        """
        Open a new editor tab
        """
        pass

    def applications_menu_check(self,widget,event):
        """
        Applications popup menu checking
        """
        if self.get_selected_project_app():
            return self.widgets.mnuAppProperties
        else:
            # TODO: Create a menu for model properties
            return self.widgets.mnuIndicator

    def get_selected_project_app(self):
        """
        Get treeview selected project application name
        """
        if self.currentproject:
            appname=self.projectapps.selected()
            if appname and appname[0] in self.currentproject.get_apps():
                return appname[0]
        else:
            return None

    def setup_text_styles(self):
        """
        Setup text styles used in application console log
        """
        buf=self.widgets.txtConsoleLog.get_buffer()
        #newTextTag({'name': 'normal','foreground': self.preferences.log.normalcolor},buf)
        newTextTag({'name': 'internal','foreground': self.preferences.log.internalcolor, 'weight': 700},buf)
        #newTextTag({'name': 'info','foreground': self.preferences.log.notifcolor, 'weight': 700},buf)
        newTextTag({'name': 'error','foreground': self.preferences.log.errorcolor, 'weight': 700},buf)

    def logmsg(self,msg,tag='internal'):
        """
        Log a message to console
        """
        logbuffer=self.widgets.txtConsoleLog.get_buffer()
        it=logbuffer.get_end_iter()
        logbuffer.place_cursor(it)
        logbuffer.insert_with_tags_by_name(it,msg + '\n', tag)
        self.widgets.txtConsoleLog.scroll_to_mark(logbuffer.get_insert(),0)
        # Tray icon blinking
        self.trayicon.blink()
        self.notifsys.queue_msg(msg=msg,icon=self.PIXMAPS_DIR + '/ddc-icon.png')
    
    def console_update_loop(self):
        """
        Console update loop
        """
        # Django server monitoring
        if self.currentproject and self.currentproject.django_server_instance and self.currentproject.django_server_instance.isalive():
            bufdata=self.currentproject.get_server_buffer()
            if bufdata:
                print bufdata 
            return self.currentproject.django_server_instance.isalive()
        elif self.currentproject and self.currentproject.django_server_instance and not self.currentproject.django_server_instance.isalive():
            self.logmsg(_('Django development server has exited unexpectedly'),'error')
            self.currentproject.django_server_instance=None
            self.set_gui_task(self.TASK_SERVERSTOPPED)

    def run_background_task(self,callback,msg,*args,**kwargs):
        """
        Run a background task and shows a progress bar with status
        """
        if self.currentproject:
            if not self.__command_in_progress:
                def task(*args,**kwargs):
                    self.widgets.prgCommand.set_text(msg)
                    self.widgets.prgCommand.show()
                    callback(*args,**kwargs)
                
                def gui(*args,**kwargs):
                    self.widgets.prgCommand.pulse()
                    return self.__command_in_progress
                
                def end(*arg,**kwargs):
                    gobject.timeout_add(500,end2,priority=gobject.PRIORITY_HIGH)
                
                def end2():
                    self.widgets.prgCommand.set_text('')
                    self.widgets.prgCommand.hide()
                    self.__command_in_progress=False
                
                self.__command_in_progress=True
                task=threadtasks.ThreadTask(threadtasks.TYPE_SIMPLE,task,gui,end,gui_delay=100)
                task.start(*args,**kwargs)
            else:
                self.dialogs.msgDialog(_('There is another command in progress'),'error')
    
    def load_project(self,name,port,path,settingsmod):
        """
        Load a project
        """
        if not self.currentproject:
            try:
                # Create new django project instance
                self.currentproject=DjangoProject(name,port,path,settingsmod)
            except Exception,e:
                self.currentproject=None
                self.dialogs.msgDialog(_('Error loading project'),'error',str(e))
                return
            # Update files information
            self.update_project_files()
            # Update applications information
            self.update_apps_information()
            # Update users information
            self.update_users_information()
            # Update settings information
            self.update_settings_information()
            # Add project to project history
            history=self.preferences.general.history
            if self.ui.chkAddToHistory and name not in history:
                # Create new config section for new project
                self.preferences.add_section('ddc_project_definition-' + name,{'port': port,'path': path,'settingsmod': settingsmod})
                # Add project to history list
                history.append(name)
                self.preferences.general.history=history
            # Set window title
            self.widgets.winMain.set_title(_('Django Development Console') + ' - '  + name)
            # Add project as last opened one
            self.preferences.general.lastproject = name
            # Save preferences
            self.preferences.save()
            # Update projects history
            self.update_projects_history()
예제 #2
0
class FontCombo(gtk.Button):
    """
    Font combobox widget class
    """
    __gtype_name__ = 'FontCombo'
#    __properties = {
#        'predefined-colors' : (gobject.TYPE_STRING,'Predefined colors','List of comma separated HTML format predefined colors','',gobject.PARAM_CONSTRUCT | gobject.PARAM_READWRITE),
#    }
#    __gproperties__ = __properties

    __gsignals__ = { 
        'changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,(gobject.TYPE_OBJECT,))
    }

    def __init__(self):
        """
        Initialization
        """        
        # Initialize parent class
        gtk.Button.__init__(self)
        # Initialize popup window
        self.__popup_window=gtk.Window(gtk.WINDOW_POPUP)
        self.__popup_window.set_decorated(False)
        self.__popup_window.set_resizable(False)
        self.__popup_window.set_keep_above(True)
        self.__popup_window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_COMBO)
        # Initialize treeview with font list
        self.__scroller=gtk.ScrolledWindow(gtk.Adjustment(),gtk.Adjustment())
        self.__tview=gtk.TreeView()
        self.__tview.set_flags(gtk.CAN_FOCUS)
        self.__scroller.add(self.__tview)
        self.__popup_window.add(self.__scroller)
        self.__hide_signal=None
        # Connect signals to show/hide popup
        self.connect('clicked',self.__popup)
        self.connect('key-release-event',self.__scroll_to_font)
        self.__tview.connect('cursor-changed',self.__sync_font)
        self.__tview.connect('row-activated',self.__popdown_forced)
        # Enable search in treeview
        self.__tview.set_search_column(0)        
        # Initialize popup window
        self.__popup_window.show_all()
        self.__csalloc=self.__popup_window.get_allocation()
        self.__popup_window.hide()
        # Construct the widget
        hbox=gtk.HBox()
        self.__label=gtk.Label()
        self.__label.set_ellipsize(pango.ELLIPSIZE_END)
        arrow=gtk.Arrow(gtk.ARROW_DOWN,gtk.SHADOW_NONE)
        arrow.set_size_request(11,11)
        vsep=gtk.VSeparator()
        vsep.set_size_request(10,10)
        hbox.pack_start(self.__label,True,True)
        hbox.pack_start(vsep,False,False)
        hbox.pack_start(arrow,False,False)
        hbox.show_all()
        self.add(hbox)
        # Initialize font list
        self.__initialize_font_list()

    def __scroll_to_font(self,widget,event):
        """
        Scroll to a font
        """
        if event.string in string.letters+string.digits:
            print event.string
            iter=self.__fontlist.search(event.string,startwith=True)
            if iter:
                self.__tview.scroll_to_cell(self.__fontlist.storagemodel.get_path(iter))

    def do_changed(self,widget,event=None): 
        pass

    def __initialize_font_list(self):
        """
        Construct font list
        """

        def compare_data(model, iter1, iter2):
            """
            Sorting function for the fonts list store
            """
            data1 = model.get_value(iter1,0)
            data2 = model.get_value(iter2,0)
            return cmp(data1, data2)        

        self.__fontlist=TreeViewFactory('list', ['str','markup'], [],treeview=self.__tview)
        
        # Get all system fonts
        self.__font_families=pangocairo.cairo_font_map_get_default().list_families()
        for family in self.__font_families:
            escaped=gobject.markup_escape_text(family.get_name())
            markedup='<span face="%s">%s</span>' % (escaped,escaped)
            self.__fontlist.append([family.get_name(),markedup])
        self.__tview.set_headers_visible(False)
        self.__tview.get_column(0).set_property('visible',False)
        self.__scroller.set_policy(gtk.POLICY_NEVER,gtk.POLICY_AUTOMATIC)
        self.__fontlist.storagemodel.set_sort_func(0,compare_data)
        self.__fontlist.storagemodel.set_sort_column_id(0,gtk.SORT_ASCENDING)

    def __sync_font(self,widget,event=None):
        """
        Synchronize font list selection with widget label and value
        """
        # Get currently selected font
        self.__selected=self.__fontlist.selected()
        self.__label.set_markup(self.__selected[1])
        self.emit('changed', self)
        self.__popup_window.hide()

    def __popup(self,widget=None,event=None):
        """
        Shows popup
        """
        if not self.__popup_window.get_property('visible'):
            # Get main window absolute position
            x,y = self.get_toplevel().get_window().get_origin()
            # Get caller widget absolute position using main window coords
            alloc=widget.get_allocation()
            self.__popup_window.move(x+alloc.x,y+alloc.y+alloc.height)
            self.__popup_window.set_size_request(alloc.width,300)
            self.__popup_window.show()
            self.__popup_window.set_focus(self.__tview)
        else:
            self.__popup_window.hide()

    def __popdown_forced(self,*args,**kwargs):
        """
        Forced popdown of popup window
        """
        self.__popup_window.hide()

    def get_font_name(self):
        """
        Get the current font name
        """
        return self.__selected[0]
    
    def set_font_name(self,value):
        """
        Set the current font name
        """
        if self.__fontlist.select(value):
            self.__label.set_markup(self.__selected[1])