def __init__(self, view): self._view = view # Give controller object to the view self._view.set_controller(self) # Template file for new project self._prjTemplateFile = "./ProjectTemplate.xml" # Tag to appera in title of main window self._changedTag = " [changed]" # Dict with id's of recent projects self._recentPrjDict = {} # Parameters self.param = MainParameter() # To access prj settings (settings are related to view) self._prj_settings = ProjectSettings(self._view, None) # To access ide settings (settings are related to MainParameter) self._ide_settings = IdeSettings(None, self.param) # To count code generation durance self._tm_code_gen = 0 # Object to control async execution self._async_runner = None self._setup_ide_settings()
class MainController: """ Contoller class. Part of MVC Responsibility: Glue view and model code: - Handle all events from view (p.e. button) """ def __init__(self, view): self._view = view # Give controller object to the view self._view.set_controller(self) # Template file for new project self._prjTemplateFile = "./ProjectTemplate.xml" # Tag to appera in title of main window self._changedTag = " [changed]" # Dict with id's of recent projects self._recentPrjDict = {} # Parameters self.param = MainParameter() # To access prj settings (settings are related to view) self._prj_settings = ProjectSettings(self._view, None) # To access ide settings (settings are related to MainParameter) self._ide_settings = IdeSettings(None, self.param) # To count code generation durance self._tm_code_gen = 0 # Object to control async execution self._async_runner = None self._setup_ide_settings() def ExitIde(self): ''' Exit IDE. Exit can be canceled. @return: False if exit is canceled else True ''' if not self._check_and_save_project(): return False if self._async_runner != None: if self._async_runner.GetRunning(): self.OutputWarning("Cannot exit. "\ "Ide is doing async execution ...") return False new_list = [] # build list with max max_num_recent_projects elements last_idxs = self._recentPrjDict.keys() last_idxs.reverse() # Last added should be saved for idx in last_idxs: new_list.append(self._recentPrjDict[idx]) if len(new_list) >= self.param.max_num_recent_projects.get(): break self.param.recent_prj_list.set(new_list) self._ide_settings.save() self._view.Destroy() return True def OnRecentPrjLoad(self, event): ''' Callback from the file menue. (Recent projects) @param event: wx event. ''' project_file_name = self._recentPrjDict[event.GetId()] self._load_project(project_file_name) def DoRemoveCurInclude(self): ''' Remove current selected Include item ''' cur_num = self._view.currentItemInclude if None == cur_num: return self._view.listIncludes.DeleteItem(cur_num) self._set_settings_changed() def DoRemoveCurMacro(self): ''' Remove current selected Macro item ''' cur_num = self._view.currentItemMacro if None == cur_num: return self._view.listMacros.DeleteItem(cur_num) self._set_settings_changed() def CountCodeGenSec(self, reset=False): ''' Count the time of code generation. Must be called once per second. Update UI @param reset: Boolean to reset the counter ''' if reset: self._tm_code_gen = 0 else: self._tm_code_gen += 1 self._view.statusBar.SetStatusText(number=1, text=u'Time: %d sec' % \ self._tm_code_gen) def OutputError(self, err_txt): ''' Print error text in output window @param err_txt: Text to display ''' self._append_out_text(err_txt, self._text_error) def OutputWarning(self, err_txt): ''' Print warning text in output window @param err_txt: Text to display ''' self._append_out_text(err_txt, self._text_warn) def OutputInfo(self, inf_txt): ''' Print info text in output window @param inf_txt: ''' self._append_out_text(inf_txt, self._text_info) def OutputCode(self, code_txt): ''' Append text in the code window @param code_txt: Text to append ''' self._view.textCode.AppendText(code_txt) def GenCodeFinished(self): ''' Inform that code generation has finished ''' self._enable_generation_widgets(True) def GenCppCode(self): ''' Generate Boost.Python code ''' self._enable_generation_widgets(False) self._view.textCode.SetValue("") self._append_out_text("Generation of C++ code for Boost.Python started") self.CountCodeGenSec(reset=True) params = self._get_gccxml_params(False) gen_xml_obj = AsyncExecHandler(code_generator.gen_cpp, params) self._start_async_exec(gen_xml_obj) def GenPyPPCode(self): ''' Generate Py++ code ''' self._enable_generation_widgets(False) self._view.textCode.SetValue("") self._append_out_text("Generation of Py++ code started") self.CountCodeGenSec(reset=True) params = self._get_gccxml_params(False) gen_xml_obj = AsyncExecHandler(code_generator.gen_pypp, params) self._start_async_exec(gen_xml_obj) def GenXmlCode(self): ''' Generate XML code ''' self._enable_generation_widgets(False) self._append_out_text("Generation of XML code started") self._view.textCode.SetValue("") self.CountCodeGenSec(reset=True) params = self._get_gccxml_params(False) gen_xml_obj = AsyncExecHandler(code_generator.gen_xml, params) self._start_async_exec(gen_xml_obj) def OpenDlgHeader(self): ''' Open dialog to get header file ''' self._open_file_dlg_text_ctrl( self._view.textHeader, "Choose a Header file","C/C++ files (*.h,*.hpp,*.hxx,*.c,"\ "*.cpp,*.cxx)|*.h;*.hpp;*.hxx;*.c;*.cpp;*.cxx|All Files(*)|*") def OpenDlgGccXml(self): ''' Open dialog to get GccXml executable ''' self._open_file_dlg_text_ctrl( self._view.textGccXml, "Choose GccXml executable", "All Files(*)|*") def OpenDlgEditCurInclude(self): ''' Open dialog to edit current include ''' cur_num = self._view.currentItemInclude if None == cur_num: return start_dir = self._view.listIncludes.GetItemText(cur_num) dialog = wx.DirDialog(self._view, "Edit include directory", start_dir) try: if dialog.ShowModal() == wx.ID_OK: self._view.listIncludes.DeleteItem(cur_num) self._view.listIncludes.InsertStringItem( cur_num, dialog.GetPath()) self._set_settings_changed() finally: dialog.Destroy() def OpenDlgEditCurMacro(self): ''' Open dialog to edit current macro ''' cur_num = self._view.currentItemMacro if None == cur_num: return macro = self._view.listMacros.GetItemText(cur_num) dialog = dialog_macro.MacroDialog(self._view, macro) try: if dialog.ShowModal() == wx.OK: self._view.listMacros.DeleteItem(cur_num) new_macro = dialog.textMacro.GetLineText(0) self._view.listMacros.InsertStringItem(cur_num, new_macro) self._set_settings_changed() finally: dialog.Destroy() def OpenDlgAddInclude(self): ''' Open Dialog to add a include path ''' dialog = wx.DirDialog(self._view, "Choose include directory", self.param.last_inc_path.get()) try: if dialog.ShowModal() == wx.ID_OK: cur_num = self._view.listIncludes.GetItemCount() dir_path = dialog.GetPath() # Check weather path is already in list if not self._check_item_in_list(dir_path, self._view.listIncludes): self._view.listIncludes.InsertStringItem( cur_num, dir_path) self.param.last_inc_path.set(str(dir_path)) self._set_settings_changed() finally: dialog.Destroy() def OpenDlgAddMacro(self): ''' Open dialog to add a macro ''' dialog = dialog_macro.MacroDialog(self._view) if dialog.ShowModal() == wx.OK: cur_num = self._view.listMacros.GetItemCount() new_macro = dialog.textMacro.GetLineText(0) # Check weather macro is already in list if not self._check_item_in_list(new_macro, self._view.listMacros): self._view.listMacros.InsertStringItem(cur_num, new_macro) self._set_settings_changed() def OpenDlgLoadProject(self): ''' Open dialog to load a project ''' from xml.parsers.expat import ExpatError dialog = wx.FileDialog(self._view, "Load existing Py++ project", self.param.last_prj_path.get(), "", "Project files (*.pypp)|*.pypp|All Files(*)|*", wx.OPEN) try: if dialog.ShowModal() == wx.ID_OK: self.ClearUi() project_file_name = dialog.GetPath() self._load_project(project_file_name) except ExpatError: self._append_out_text("XML parser error in file:%s" % \ dialog.GetPath(), self._text_error) except Exception: self._append_out_text("Error loading file:%s" % \ dialog.GetPath(), self._text_error) raise finally: dialog.Destroy() def OpenDlgSaveProject(self, new_file=False): ''' Open dialog to save a project @param new_file: Boolean weather to use a new file @return: False if project save was canceled ''' # Is current file prj template? if self._prj_settings.get_file_name() == self._prjTemplateFile: project_file_name = self._open_new_prj_file() else: if new_file: project_file_name = self._open_new_prj_file() else: project_file_name = self._prj_settings.get_file_name() try: if project_file_name == None: return False # prior dialog skipped if not self._save_project(project_file_name): return False except Exception: self._append_out_text("Error saving file:%s" % \ project_file_name, self._text_error) raise return True def DoProjectNew(self): ''' Open dialog for new Project ''' self._load_project(self._prjTemplateFile) def ClearUi(self): ''' Clear all controls of UI ''' # Clear text ctrls for textCtrl in [self._view.textHeader, self._view.textGccXml, self._view.textOutput, self._view.textCode]: textCtrl.Clear() # clear list ctrls for listCtrl in [self._view.listMacros, self._view.listIncludes]: tot_num = listCtrl.GetItemCount() for cur_num in range(tot_num, 0, -1): listCtrl.DeleteItem(cur_num-1) def _cancel_if_file_exist(self, project_file_name, parent_dlg=None): # check for overwriting of file if os.path.exists(project_file_name): dialog = wx.MessageDialog(parent_dlg, "%s exists. Should it be overwritten?" % \ (project_file_name), pos=self._view.GetPosition()) if dialog.ShowModal() == wx.ID_CANCEL: return True else: return False # Return True if successfull def _check_and_save_project(self): if self._prj_settings.get_changed(): # Anyhow pos is working dialog = wx.MessageDialog(self._view, "Current project changed. "\ "Should project be saved?", style = (wx.YES_NO), pos=self._view.GetPosition()) user_input = dialog.ShowModal() dialog.Destroy() if user_input == wx.ID_NO: return True elif user_input == wx.ID_YES: return self.OpenDlgSaveProject() else: return True def _load_project(self, project_file_name): self._check_and_save_project() self.ClearUi() self._prj_settings.load(project_file_name) self._set_prj_filename_in_title(project_file_name) if project_file_name != self._prjTemplateFile: self.param.last_prj_path.set(unicode(os.path.dirname(project_file_name))) self._add_to_prj_history(project_file_name) def _save_project(self, project_file_name): self._prj_settings.save(project_file_name) self._reset_settings_changed() self.param.last_prj_path.set(os.path.dirname(project_file_name)) self._set_prj_filename_in_title(project_file_name) self._add_to_prj_history(project_file_name) return True def _open_new_prj_file(self): project_file_name = None dialog = wx.FileDialog(self._view, "Save Py++ project to new file", self.param.last_prj_path.get(), "", "Project files (*.pypp)|*.pypp|All Files(*)|*", wx.FD_SAVE) if dialog.ShowModal() == wx.ID_OK: project_file_name = dialog.GetPath() if self._cancel_if_file_exist(project_file_name, dialog): project_file_name = None dialog.Destroy() return project_file_name def _add_to_prj_history(self, prj_filename): # This don't work, see below # max_num_history = self.param.max_num_recent_projects.get() if prj_filename in self._recentPrjDict.values(): return if prj_filename == self._prjTemplateFile: return file_name = os.path.split(prj_filename)[1] menue = self._view.menueRecentPrj new_item_id = id=wx.NewId() new_item = wx.MenuItem(menue, help=prj_filename, id=new_item_id, kind=wx.ITEM_NORMAL, text=file_name) menue.PrependItem(new_item) self._view.Bind(wx.EVT_MENU, self.OnRecentPrjLoad, id=new_item_id) self._recentPrjDict[new_item_id] = prj_filename # Doesn't work. Dont know why # if menue.GetMenuItemCount() > max_num_history: # menue.Remove(max_num_history) def _open_file_dlg_text_ctrl(self, related_wx_text, caption_txt="", file_filter="All Files(*)|*", dir_path="."): """Open file open dialog and insert file in related wxText ctrl""" cur_file = related_wx_text.GetValue() if cur_file != "": dir_path = os.path.dirname(related_wx_text.GetValue()) dialog = wx.FileDialog(self._view, caption_txt, dir_path, "", file_filter, wx.OPEN) try: if dialog.ShowModal() == wx.ID_OK: related_wx_text.Clear() related_wx_text.AppendText(dialog.GetPath()) self._set_settings_changed() finally: dialog.Destroy() def _set_settings_changed(self): self._prj_settings.set_changed() title_str = self._view.GetTitle() if not self._changedTag in title_str: title_str += self._changedTag self._view.SetTitle(title_str) def _enable_generation_widgets(self, state): ctrls = [self._view.butGenCpp, self._view.butGenXml, \ self._view.butGenPyPP] for ctrl in ctrls: ctrl.Enable(state) def _get_gccxml_params(self, verbose): gcc_xml = self._prj_settings.get_param('gccXmlSettings.gccXmlPath') inc_path_list = eval(self._prj_settings.get_param( 'gccXmlSettings.includePathList')) macro_list = eval(self._prj_settings.get_param( 'gccXmlSettings.macroList')) header_file = self._prj_settings.get_param('gccXmlSettings.headerPath') if verbose: self._append_out_text(" "+ gcc_xml) self._append_out_text(" "+ str(inc_path_list)) self._append_out_text(" "+ str(macro_list)) self._append_out_text(" "+ header_file) return (gcc_xml, inc_path_list, macro_list, header_file) def _setup_ide_settings(self): # load ide settings self._ide_settings.load() prj_list = self.param.recent_prj_list.get() prj_list.reverse() for prj in prj_list: self._add_to_prj_history(prj) # Change the title string: # tilte := "<ConstPart> (<prj_name> in <prj_path>) <changedTag>" def _set_prj_filename_in_title(self, filename): if filename == self._prjTemplateFile: prj_description = "New project" else: prj_name = os.path.basename(filename) prj_path = os.path.dirname(filename) prj_description = prj_name + " in " + prj_path title_str = self._view.GetTitle() start_idx = title_str.find("(") end_idx = title_str.find(")") fnamstr = title_str[start_idx:end_idx+1] title_str = title_str.replace(fnamstr, "(" + prj_description + ")") self._view.SetTitle(title_str) def _reset_settings_changed(self): title_str = self._view.GetTitle() title_str = title_str.replace(self._changedTag, "") self._view.SetTitle(title_str) def _check_item_in_list(self, item, wx_list): idx = wx_list.FindItem(0, item) if idx == -1: return False else: return True def _append_out_text(self, text, type_of_text = 0): """ append text with different error level""" text_ctrl = self._view.textOutput type_txt = "INFO" # Error if type_of_text == MainController._text_error: type_txt = "ERROR" text_ctrl.SetDefaultStyle(wx.TextAttr(wx.RED)) # Warning elif type_of_text == MainController._text_warn: type_txt = "WARN" # Orange text_ctrl.SetDefaultStyle(wx.TextAttr(wx.Color(255, 168, 7))) # Info else: text_ctrl.SetDefaultStyle(wx.TextAttr(wx.BLACK)) text_ctrl.AppendText(type_txt + ": " + text + "\n") # Start async execution def _start_async_exec(self, async_runner): self._async_runner = async_runner self._async_runner.SetErrorOutput(self.OutputError) self._async_runner.SetResultOutput(self.OutputCode) self._async_runner.SetFinishedCb(self.GenCodeFinished) self._async_runner.SetProgressCb(self.CountCodeGenSec, 10) self._async_runner.Start() # levels _text_info = 0 # Text has informational character _text_warn = 1 # Text has warning character _text_error = 2 # Text has error character