def ResizeRecentProjectsList(): """ Updates the length of the recent projects list to the size specified by the 'RecentProjectsListMaxSize' config. """ sm = SettingsManager() new_size = sm.get_config('RecentProjectsListMaxSize') try: old_list = sm.get_config('RecentProjectsList') except KeyError: old_list = [] sm.set_value('RecentProjectsList', old_list[:new_size])
def OnNewButton(self, event=None): # handle click on New project button self.DisableKeypresses() # get project filename extension SettingsMgr = SettingsManager() ProjFileExt = SettingsMgr.get_config('ProjFileExt') OpenSuccess = False # whether valid project files are identified for opening # let the user select a template DialogueBox = SelectTemplateDialogue( parent=self, title=_('Vizop: Choose a template'), ColourScheme=self.ColourScheme, ProjTemplates=self.ProjTemplates) if DialogueBox.ShowModal() == wx.ID_OK: # get the template file and project filename to create from the user TargetTemplateFile, ProjFilename, SaveOnFly = DialogueBox.ReturnData # force the project filename to have expected extension ProjFilename = EnsureFilenameHasExtension(ProjFilename, ProjFileExt) CreatableProjFiles, ProjOpenData = self.HandleNewFileRequest( [TargetTemplateFile], NewFilenames=[ProjFilename], NewProjects=True, SaveOnFly=SaveOnFly) OpenSuccess = bool(CreatableProjFiles) # allow user to continue working in Welcome Frame if no projects can be created. TODO give user feedback if not OpenSuccess: self.ReinstateKeypresses()
def OnStoreButton(self, event=None): # handle 'Start new file from template and store my work' request global KeyPressHash self.DisableKeypresses() # retrieve the target template filename from the dropdown box SettingsMgr = SettingsManager() TargetTemplateStub = self.tc.GetStringSelection() TargetTemplateFile = self.avail_templates[TargetTemplateStub] if IsReadableFile(TargetTemplateFile): # get a default directory in which to store the project try: DefDir = SettingsMgr.get_config('NewProjDir') except KeyError: # No default dir found in settings: store one DefDir = SettingsMgr.get_value('UserHomeDir') SettingsMgr.set_value('NewProjDir', DefDir) # get default extension for project files ProjFileExt = SettingsMgr.get_config('ProjFileExt') # get a filename to store the project (StoreLocOK, ProjFilename) = GetFilenameForSave( DialogueParentFrame=self, DialogueTitle=_('Vizop needs a filename for your new project'), DefaultDir=DefDir, DefaultFile='', Wildcard='', DefaultExtension=ProjFileExt) if StoreLocOK: # set dialogue box's return data: TargetTemplateFile (str), ProjFilename (str), SaveOnFly (bool) self.ReturnData = (TargetTemplateFile, ProjFilename, True) SettingsMgr.set_value('NewProjDir', os.path.dirname(ProjFilename)) # close the template selection dialogue box self.EndModal(wx.ID_OK) else: # we're returning to template selection dialogue: reinstate keyboard shortcuts self.ReinstateKeypresses() else: # there was a problem with the template file; pop up a message i = wx.MessageBox( _('Vizop says sorry'), (_("Template %s can't be opened") % TargetTemplateStub), style=wx.OK)
def select_file_from_all(message='', default_path=None, wildcard='*', allow_multi_files=False, parent_frame=None, **Args): """ Provide generic file selection dialogue. Allows user to select any file(s) from the filesystem. Returns list of full file paths selected (or [] if none were selected) """ # **Args is to soak up obsolete arg read_only (no longer recognised by wxPython) # force MacOS to apply filter in wildcard wx.SystemOptions.SetOption(u"osx.openfiledialog.always-show-types", 1) # get default directory to show in dialogue if default_path is None: sm = SettingsManager() try: # get saved path from config file default_path = sm.get_config('UserWorkingDir') except KeyError: # no path saved; select user's home folder default_path = os.path.expanduser('~') dialogue = wx.FileDialog(message=message, defaultDir=default_path, wildcard='Vizop project files (*.vip)|*.vip', style=((wx.FD_MULTIPLE * allow_multi_files) | wx.FD_OPEN), parent=parent_frame, pos=(100, 100)) if dialogue.ShowModal() == wx.ID_OK: # dialogue box exited with OK button if allow_multi_files: return dialogue.GetPaths() else: return [dialogue.GetPath()] else: # dialogue box exited with Cancel button return []
def GetValueFromUserConfig(ConfigName): # retrieve and return config value for ConfigName (str) from the user's config data (stored in config file) # returns default (see settings module) if no key has been stored sm = SettingsManager() return sm.get_config(ConfigName)
class NoProjectOpenFrame(wx.Frame): # Define the welcome window that appears whenever no project is open, including at launch of vizop def __init__(self, parent, ID, title, ColourScheme): global KeyPressHash self.ColourScheme = ColourScheme wx.Frame.__init__(self, parent, wx.ID_ANY, title, size=(600, 400)) self.settings_manager = SettingsManager() self.Centre( ) # places window in centre of screen, Rappin p235. TODO need to place in centre of primary display # set up sizers to contain the objects on the welcome frame self.sizer1 = wx.BoxSizer(wx.HORIZONTAL) # set up logo self.sizer1.Add(LogoPanel(self, self.ColourScheme), 1, wx.ALIGN_LEFT | wx.EXPAND) # set up button area # set correct shortcut key text for 'Exit' key ExitShortcutKey = { 'Linux': 'Ctrl + Q', 'Windows': 'Ctrl + Q', 'Darwin': info.CommandSymbol + 'Q' }.get(system(), 'Ctrl + Q') # info per button: (text in button, x and y location, handling routine, keyboard shortcut, internal name) ButtonInfo = [ (_('Open recent project | R'), 40, 40, self.OnOpenRecentButton, ord('r'), 'Recent'), (_('Open existing project | O'), 40, 90, self.OnOpenExistingButton, ord('o'), 'Existing'), (_('Start new project | N'), 40, 140, self.OnNewButton, ord('n'), 'New'), (_('Join a project collaboration | J'), 40, 190, self.OnJoinCollaborationButton, ord('j'), 'Join'), (_('About Vizop | T'), 40, 240, self.OnAbout, ord('t'), 'About'), (_('Exit | ' + ExitShortcutKey), 40, 290, self.OnExitButton, [wx.WXK_CONTROL, ord('q')], 'Exit') ] # get the current recent project list, or create a new one if it doesn't exist try: RecentProjList = self.settings_manager.get_config( 'RecentProjectsList') except KeyError: RecentProjList = [] # get filename of all available project templates self.ProjTemplates = GetAvailProjTemplates() panel3 = wx.Panel(self) # panel to hold the buttons panel3.SetBackgroundColour(ColourScheme.BackBright) KeyPressHash = ClearKeyPressRegister(KeyPressHash) for (ButtonText, x, y, ButtonHandler, Key, Name) in ButtonInfo: # set up each button in the welcome frame RegisterKey = True # whether to register the keypress b = wx.Button(panel3, -1, ButtonText, pos=(x, y), size=(220, 40)) b.Bind(wx.EVT_BUTTON, ButtonHandler) b.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL)) b.SetForegroundColour('navy') if (Name == 'Recent') and not RecentProjList: b.Disable() # disable Recent projects button if none available RegisterKey = False if (Name == 'New') and not self.ProjTemplates: b.Disable( ) # disable New project button if no templates available RegisterKey = False if RegisterKey: KeyPressHash = RegisterKeyPressHandler(KeyPressHash, Key, ButtonHandler) panel3.SetFocus() # enable button bar to handle Tab, Space, Enter keys self.sizer1.Add(panel3, 1, wx.EXPAND) self.sizer1.SetItemMinSize(1, (300, 400)) self.Bind(wx.EVT_IDLE, self.OnIdle) # Lay out sizers self.SetMinSize((600, 400)) self.SetSizer(self.sizer1) # assigns sizer1 to fill Welcome frame self.SetAutoLayout(True) self.sizer1.FitInside(self) self.Show(True) def OnIdle( self, Event ): # during idle time, handle keystrokes for shortcuts. This procedure is repeated in module datacore global KeyPressHash # initialise storage object used to remember keys pressed last time, for chord detection in correct order if not hasattr(self, 'PrevChordKeys'): self.PrevChordKeys = [] KeysDownThisTime = [ ] # prepare list of key codes that are 'hit' and match any keys in KeyPressHash KeyPressIndexHit = None # which item (index in KeyPressHash) is to be invoked for (HashIndex, (KeyStroke, Handler, Args)) in enumerate( KeyPressHash ): # check each keystroke registered in KeyPressHash if type(KeyStroke) is int: if wx.GetKeyState(KeyStroke): KeysDownThisTime.append(KeyStroke) if not (KeyStroke in self.PrevChordKeys): KeyPressIndexHit = HashIndex # KeyStroke has been detected this time, but not last time elif type(KeyStroke) is list: # key chord handling ChordFulfilled = True for k in KeyStroke: # check each key in KeyStroke if wx.GetKeyState(k): KeysDownThisTime.append(k) else: ChordFulfilled = False # check that (1) all keys in KeyStroke are pressed, AND # (2) not all of them were pressed last time (i.e. >=1 of them is newly pressed) if ChordFulfilled and not set(KeyStroke).issubset( set(self.PrevChordKeys)): KeyPressIndexHit = HashIndex # invoke handler if keystroke detected self.PrevChordKeys = KeysDownThisTime # store currently-pressed keys for comparison next time # Must do the line above BEFORE invoking Handler - else KeyStrokes newly defined in Handler may falsely activate if (KeyPressIndexHit is not None): (KeyStroke, Handler, Args) = KeyPressHash[KeyPressIndexHit] Handler(**Args) # invoke handler def WrapUpAfterOpeningProjects(self, ProjFiles, TemplateFiles, RequestToQuit=False, SaveOnFly=True): # after successful opening of project file(s) from welcome frame, perform tidying up # ProjFiles is list of paths of valid project files to be opened or created (str) # TemplateFiles is list of paths of valid template files to be used to create new projects, using the filepaths # in ProjFiles (str) # RequestToQuit (bool): whether vizop should quit now, on user's request # SaveOnFly (bool): whether new project should be saved on the fly # set return values NoProjectOpenFrameData.Data = { 'ProjectFilesToOpen': ProjFiles, 'TemplateFilesToSpawnFrom': TemplateFiles, 'RequestToQuit': RequestToQuit, 'SaveOnFly': SaveOnFly } self.Destroy() # destroy welcome frame def WrapUpAfterJoinCollaborationRequest(self): # perform tidying up and exit welcome frame after receiving request to collaborate on remote project # similar to WrapUpAfterOpeningProjects() except there's no open project to handle NoProjectOpenFrameData.Data = { 'ProjectFilesToOpen': [], 'TemplateFilesToSpawnFrom': [], 'RequestToQuit': False, 'SaveOnFly': False, 'OpenCollaboration': True } self.Destroy() # destroy welcome frame def DisableKeypresses( self ): # temporarily suppress all keypress handlers, to avoid inadvertent behaviour global KeyPressHash self.OldKPR = KeyPressHash[:] KeyPressHash = ClearKeyPressRegister( KeyPressHash) # to avoid inadvertent behaviour from key presses def ReinstateKeypresses(self): # reinstate keyboard shortcuts global KeyPressHash KeyPressHash = self.OldKPR def HandleNewFileRequest(self, Filenames, NewFilenames=[], NewProjects=False, SaveOnFly=True): # Check NewFilenames can be opened. # Check whether Filenames (list of paths) contains openable project templates. # NewFilename: full paths of files to create for new project # NewProjects (bool): whether the Filenames are templates to use for creation of new projects (always True) # SaveOnFly (bool): if creating new project, whether work should be saved on the fly # If any openable projects, call WrapUpAfterOpeningProjects() to close Welcome Frame and send data to datacore. # Return values: OpenableProjFiles (list of paths that contain valid and openable projects) # ProjOpenData (list of dict of data for each file, returned from projects.TestProjectsOpenable) ProjOpenData = projects.TestProjectsOpenable( Filenames, ReadOnly=False) # find out if the projects can be opened # expected return value (ProjOpenData): a list of dict, one for each project file in Filenames # dict keys are: Openable (bool), Comment (str) - explaining why project is not openable (for user feedback) # make list of openable/creatable project files OpenableProjFiles = [ Filenames[i] for i in range(len(Filenames)) if ProjOpenData[i]['Openable'] if (IsWritableLocation(os.path.dirname(NewFilenames[i])) or not SaveOnFly) ] # Note, the writability check is duplicated in projects.SaveEntireProject() and might be redundant here if OpenableProjFiles: # can any of the selected project files be opened? if NewProjects: # tidy up and exit Welcome Frame self.WrapUpAfterOpeningProjects( ProjFiles=NewFilenames, TemplateFiles=OpenableProjFiles, SaveOnFly=SaveOnFly) else: self.WrapUpAfterOpeningProjects(ProjFiles=OpenableProjFiles, TemplateFiles=[]) return OpenableProjFiles, ProjOpenData def HandleOpenFileRequest(self, Filenames, NewFilenames=[], NewProjects=False, SaveOnFly=True): # Check whether Filenames (list of existing paths) contains openable projects. # NewFilename: full paths of files to create for new project (redundant, now using HandleNewFileRequest() ) # NewProjects (bool): whether the Filenames are templates to use for creation of new projects - always False # SaveOnFly (bool): if creating new project, whether work should be saved on the fly # If any openable projects, call WrapUpAfterOpeningProjects() to close Welcome Frame and send data to datacore. # Return values: OpenableProjFiles (list of paths that contain valid and openable projects) # ProjOpenData (list of dict of data for each file, returned from projects.TestProjectsOpenable) ProjOpenData = projects.TestProjectsOpenable( Filenames, ReadOnly=False) # find out if the projects can be opened # expected return value (ProjOpenData): a list of dict, one for each project file in Filenames # dict keys are: Openable (bool), Comment (str) - explaining why project is not openable (for user feedback) # make list of openable/creatable project files OpenableProjFiles = [ Filenames[i] for i in range(len(Filenames)) if ProjOpenData[i]['Openable'] if (IsWritableLocation(os.path.dirname(Filenames[i])) or not SaveOnFly) ] # Note, the writability check is duplicated in projects.SaveEntireProject() and might be redundant here if OpenableProjFiles: # can any of the selected project files be opened? if NewProjects: # tidy up and exit Welcome Frame self.WrapUpAfterOpeningProjects( ProjFiles=NewFilenames, TemplateFiles=OpenableProjFiles, SaveOnFly=SaveOnFly) else: self.WrapUpAfterOpeningProjects(ProjFiles=OpenableProjFiles, TemplateFiles=[]) return OpenableProjFiles, ProjOpenData def OnOpenRecentButton(self, event=None ): # handle user request to Open Recent Project self.DisableKeypresses() OpenSuccess = False # whether valid project files are identified for opening # read recent projects list from cache file try: recent_projects_list = self.settings_manager.get_config( 'RecentProjectsList') except KeyError: #recent_projects_list does not exist - so create it recent_projects_list = [] self.settings_manager.set_value('RecentProjectsList', recent_projects_list) RPDisplayList = [ ] # build list of recent projects in display-friendly format for ProjFilename in reversed(recent_projects_list): RPDisplayList.append( _('%s in %s') % (os.path.basename(ProjFilename), os.path.dirname(ProjFilename))) DialogueBox = wx.MultiChoiceDialog( self, _('Which project(s) would you like to open?'), _('Choose a vizop project file to open'), RPDisplayList) # set up standard selection dialogue if DialogueBox.ShowModal() == wx.ID_OK: # user clicked OK FilenamesSelected = [] # make list of filenames selected for index in DialogueBox.GetSelections(): FilenamesSelected.append(recent_projects_list[index]) OpenableProjFiles, ProjOpenData = self.HandleOpenFileRequest( FilenamesSelected) OpenSuccess = bool(OpenableProjFiles) # allow user to continue working in Welcome Frame if no projects can be opened. TODO give user feedback if not OpenSuccess: self.ReinstateKeypresses() def OnOpenExistingButton(self, Event=None): # handle click on Open Existing button self.DisableKeypresses() FilenamesSelected = projects.GetProjectFilenamesToOpen(self) if FilenamesSelected: # did user select any files to open? OpenableProjFiles, ProjOpenData = self.HandleOpenFileRequest( FilenamesSelected) OpenSuccess = bool( OpenableProjFiles ) # using OpenSuccess for consistency with other handlers else: OpenSuccess = False # allow user to continue working in Welcome Frame if no projects can be opened. TODO give user feedback if not OpenSuccess: self.ReinstateKeypresses() def OnNewButton(self, event=None): # handle click on New project button self.DisableKeypresses() # get project filename extension SettingsMgr = SettingsManager() ProjFileExt = SettingsMgr.get_config('ProjFileExt') OpenSuccess = False # whether valid project files are identified for opening # let the user select a template DialogueBox = SelectTemplateDialogue( parent=self, title=_('Vizop: Choose a template'), ColourScheme=self.ColourScheme, ProjTemplates=self.ProjTemplates) if DialogueBox.ShowModal() == wx.ID_OK: # get the template file and project filename to create from the user TargetTemplateFile, ProjFilename, SaveOnFly = DialogueBox.ReturnData # force the project filename to have expected extension ProjFilename = EnsureFilenameHasExtension(ProjFilename, ProjFileExt) CreatableProjFiles, ProjOpenData = self.HandleNewFileRequest( [TargetTemplateFile], NewFilenames=[ProjFilename], NewProjects=True, SaveOnFly=SaveOnFly) OpenSuccess = bool(CreatableProjFiles) # allow user to continue working in Welcome Frame if no projects can be created. TODO give user feedback if not OpenSuccess: self.ReinstateKeypresses() def OnJoinCollaborationButton( self, Event=None): # handle user request to join collaboration%%% DialogueBox = JoinCollaborationDialogue( parent=self, title=_('Vizop: Join a collaboration'), ColourScheme=self.ColourScheme) if DialogueBox.ShowModal() == wx.ID_OK: pass def OnAbout(self, event=None): OnAboutRequest(Parent=self, event=None) def OnExitButton(self, event=None): self.DisableKeypresses() NoProjectOpenFrameData.Data = { 'RequestToQuit': True } # store return data used by 'heart' module self.Destroy()