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()
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])