def __init__(self, model): """ Standard constructor. @type model: model.workspace.Workspace @param model: Corresponding model for this view. """ # Initialize Observer Observer.__init__(self) parent = None # View related config vars self.config = model.config.view # Filename, etcetera self.fileName = '' self.filePath = os.path.join(os.getcwd(), 'examples') self._cwFileNewCount = 1 size = wx.Size(self.config.getVal(tools.config.View.FRAMEWIDTH), \ self.config.getVal(tools.config.View.FRAMEHEIGHT)) wx.Frame.__init__(self, parent, -1, self.GetTitle(), size=size) # Remember Maximized Frame; Only works on MSW if sys.platform in ("win32") and self.config.getVal( tools.config.View.FRAME_MAXIMIZED): self.Maximize(True) # Zoom self.zoom = self.config.getVal(tools.config.View.ZOOM_LASTVALUE) # initialize controller self._controller = CabelController(model, self) # List of modules and connections self._modules = [] self._connections = [] self._draggedCable = None self._modulesDict = {} # Create menus menubar = wx.MenuBar() # File self._filemenu = wx.Menu() self._filemenu.Append(wx.ID_NEW, "&New\tCTRL-N", "New..") self._filemenu.Append(wx.ID_OPEN, "&Open\tCTRL-O", "Open a Workspace") # RecentMenu self._recentMenuId = wx.NewId() recentMenu = self._getRecentMenu() self._filemenu.AppendMenu(self._recentMenuId, "Open Recent...", recentMenu) if recentMenu.GetMenuItemCount() == 0: self._filemenu.Enable(self._recentMenuId, False) self._filemenu.AppendSeparator() # Save self._filemenu.Append(wx.ID_SAVE, "&Save\tCTRL-S", "Save Workspace") self._filemenu.Append(wx.ID_SAVEAS, "S&ave As\tALT-S", "Save Workpsace As...") self._filemenu.AppendSeparator() # Csound start/stop self._playStopId = wx.NewId() self._filemenu.Append( self._playStopId, self._controller._model.isPlaying() and 'Stop Csound\tCTRL-Y' or 'Start Csound\tCTRL-Y') idExportToCsd = wx.NewId() self._filemenu.Append( idExportToCsd, "&Export to CSD\tCTRL-E", "Export workspace to CSD (Csound Unified File Format)") self._filemenu.AppendSeparator() # Exit self._filemenu.Append(wx.ID_EXIT, "E&xit\tCTRL-Q", "Exit Cabel") menubar.Append(self._filemenu, "&File") # Modules self._modulesMenu = self.getModulesMenu( self._controller._getXmlModuleList()) menubar.Append(self._modulesMenu, "&Modules") # Options self._optionsmenu = wx.Menu() # bottom Pane # -> properties if self.config.getVal( tools.config.View.BOTTOMWINDOW_REMEMBERPROPERTIES): showBottomPane = self.config.getVal( tools.config.View.BOTTOMWINDOW_SHOW) else: showBottomPane = self.config.getDefault( tools.config.View.BOTTOMWINDOW_SHOW) # -> menu entry self.id_bottomPane = wx.NewId() self._optionsmenu.AppendCheckItem(self.id_bottomPane, "Show &Bottom Pane\tALT-X", "Show Bottom Pane") self._optionsmenu.Check(self.id_bottomPane, showBottomPane) id_refreshMods = wx.NewId() self._optionsmenu.Append(id_refreshMods, "&Refresh Module List\tALT-R", "Refresh the Xml-Module List") self._optionsmenu.AppendSeparator() # Preferences id_options = wx.NewId() self._optionsmenu.Append(id_options, "Preferences\tALT-P", "Preferences") menubar.Append(self._optionsmenu, "&Options") self.SetMenuBar(menubar) # Associate menu events to handler functions self.Bind(wx.EVT_MENU, self._controller.onNew, id=wx.ID_NEW) self.Bind(wx.EVT_MENU, self._controller.onOpen, id=wx.ID_OPEN) self.Bind(wx.EVT_MENU, self._controller.onSave, id=wx.ID_SAVE) self.Bind(wx.EVT_MENU, self._controller.onSaveAs, id=wx.ID_SAVEAS) self.Bind(wx.EVT_MENU, self._controller.onPlayStop, id=self._playStopId) self.Bind(wx.EVT_MENU, self._controller.onMenuExportToCsd, id=idExportToCsd) self.Bind(wx.EVT_MENU, self._controller.onMenuExit, id=wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.onToggleBottomPane, id=self.id_bottomPane) self.Bind(wx.EVT_MENU, self._controller._refreshModulesMenu, id=id_refreshMods) self.Bind(wx.EVT_MENU, self._controller.onOptionsOpen, id=id_options) self.Bind(wx.EVT_CLOSE, self._controller.onClose) # Create Statusbar self.statusbar = CabelStatusBar(self) self.SetStatusBar(self.statusbar) self.SetStatusBarPane(0) # Create splitted window self._splitter = CabelSplitterWindow(self, wx.ID_ANY) # Add workspace for synth building self.workspace = CabelScrolledWindow(self._splitter, -1, self) # wxTextCtrl which gets the stdout and stderr output of the app self.ioTextCtrl = None # Initialize Splitter self._splitter.initialize(self.workspace) # Associate mouse events to handler functions self.workspace.Bind(wx.EVT_LEFT_DOWN, self._controller.onMouseLeftDown) self.workspace.Bind(wx.EVT_LEFT_UP, self._controller.onMouseLeftUp) self.workspace.Bind(wx.EVT_LEFT_DCLICK, self._controller.onMouseLeftDclick) self.workspace.Bind(wx.EVT_RIGHT_DOWN, self._controller.onMouseRightDown) self.workspace.Bind(wx.EVT_MIDDLE_DOWN, self._controller.onMouseMiddleDown) self.workspace.Bind(wx.EVT_MOTION, self._controller.onMouseMotion) self.Bind(wx.EVT_SIZE, self._controller.onSize) # Add additional key events self.Bind(wx.EVT_KEY_DOWN, self._controller.onKey) self.update(None, None)
def __init__(self, model): """ Standard constructor. @type model: model.workspace.Workspace @param model: Corresponding model for this view. """ # Initialize Observer Observer.__init__(self) parent = None # View related config vars self.config = model.config.view # Filename, etcetera self.fileName = '' self.filePath = os.path.join(os.getcwd(), 'examples') self._cwFileNewCount = 1 size = wx.Size(self.config.getVal(tools.config.View.FRAMEWIDTH), \ self.config.getVal(tools.config.View.FRAMEHEIGHT)) wx.Frame.__init__(self, parent, -1, self.GetTitle(), size = size) # Remember Maximized Frame; Only works on MSW if sys.platform in ("win32") and self.config.getVal(tools.config.View.FRAME_MAXIMIZED): self.Maximize(True) # Zoom self.zoom = self.config.getVal(tools.config.View.ZOOM_LASTVALUE) # initialize controller self._controller = CabelController(model, self) # List of modules and connections self._modules = [] self._connections = [] self._draggedCable = None self._modulesDict = {} # Create menus menubar = wx.MenuBar() # File self._filemenu = wx.Menu() self._filemenu.Append(wx.ID_NEW, "&New\tCTRL-N", "New..") self._filemenu.Append(wx.ID_OPEN, "&Open\tCTRL-O", "Open a Workspace") # RecentMenu self._recentMenuId = wx.NewId() recentMenu = self._getRecentMenu() self._filemenu.AppendMenu(self._recentMenuId, "Open Recent...", recentMenu) if recentMenu.GetMenuItemCount() == 0: self._filemenu.Enable(self._recentMenuId, False) self._filemenu.AppendSeparator() # Save self._filemenu.Append(wx.ID_SAVE, "&Save\tCTRL-S", "Save Workspace") self._filemenu.Append(wx.ID_SAVEAS, "S&ave As\tALT-S", "Save Workpsace As...") self._filemenu.AppendSeparator() # Csound start/stop self._playStopId = wx.NewId() self._filemenu.Append(self._playStopId, self._controller._model.isPlaying() and 'Stop Csound\tCTRL-Y' or 'Start Csound\tCTRL-Y') idExportToCsd = wx.NewId() self._filemenu.Append(idExportToCsd, "&Export to CSD\tCTRL-E", "Export workspace to CSD (Csound Unified File Format)") self._filemenu.AppendSeparator() # Exit self._filemenu.Append(wx.ID_EXIT, "E&xit\tCTRL-Q", "Exit Cabel") menubar.Append(self._filemenu, "&File") # Modules self._modulesMenu = self.getModulesMenu(self._controller._getXmlModuleList()) menubar.Append(self._modulesMenu, "&Modules") # Options self._optionsmenu = wx.Menu() # bottom Pane # -> properties if self.config.getVal(tools.config.View.BOTTOMWINDOW_REMEMBERPROPERTIES): showBottomPane = self.config.getVal(tools.config.View.BOTTOMWINDOW_SHOW) else: showBottomPane = self.config.getDefault(tools.config.View.BOTTOMWINDOW_SHOW) # -> menu entry self.id_bottomPane = wx.NewId() self._optionsmenu.AppendCheckItem(self.id_bottomPane, "Show &Bottom Pane\tALT-X", "Show Bottom Pane") self._optionsmenu.Check(self.id_bottomPane, showBottomPane) id_refreshMods = wx.NewId() self._optionsmenu.Append(id_refreshMods, "&Refresh Module List\tALT-R", "Refresh the Xml-Module List") self._optionsmenu.AppendSeparator() # Preferences id_options = wx.NewId() self._optionsmenu.Append(id_options, "Preferences\tALT-P", "Preferences") menubar.Append(self._optionsmenu, "&Options") self.SetMenuBar(menubar) # Associate menu events to handler functions self.Bind(wx.EVT_MENU, self._controller.onNew, id = wx.ID_NEW) self.Bind(wx.EVT_MENU, self._controller.onOpen, id = wx.ID_OPEN) self.Bind(wx.EVT_MENU, self._controller.onSave, id = wx.ID_SAVE) self.Bind(wx.EVT_MENU, self._controller.onSaveAs, id = wx.ID_SAVEAS) self.Bind(wx.EVT_MENU, self._controller.onPlayStop, id = self._playStopId) self.Bind(wx.EVT_MENU, self._controller.onMenuExportToCsd, id = idExportToCsd) self.Bind(wx.EVT_MENU, self._controller.onMenuExit, id = wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.onToggleBottomPane, id = self.id_bottomPane) self.Bind(wx.EVT_MENU, self._controller._refreshModulesMenu, id = id_refreshMods) self.Bind(wx.EVT_MENU, self._controller.onOptionsOpen, id = id_options) self.Bind(wx.EVT_CLOSE, self._controller.onClose) # Create Statusbar self.statusbar = CabelStatusBar(self) self.SetStatusBar(self.statusbar) self.SetStatusBarPane(0) # Create splitted window self._splitter = CabelSplitterWindow(self, wx.ID_ANY) # Add workspace for synth building self.workspace = CabelScrolledWindow(self._splitter, -1, self) # wxTextCtrl which gets the stdout and stderr output of the app self.ioTextCtrl = None # Initialize Splitter self._splitter.initialize(self.workspace) # Associate mouse events to handler functions self.workspace.Bind(wx.EVT_LEFT_DOWN, self._controller.onMouseLeftDown) self.workspace.Bind(wx.EVT_LEFT_UP, self._controller.onMouseLeftUp) self.workspace.Bind(wx.EVT_LEFT_DCLICK, self._controller.onMouseLeftDclick) self.workspace.Bind(wx.EVT_RIGHT_DOWN, self._controller.onMouseRightDown) self.workspace.Bind(wx.EVT_MIDDLE_DOWN, self._controller.onMouseMiddleDown) self.workspace.Bind(wx.EVT_MOTION, self._controller.onMouseMotion) self.Bind(wx.EVT_SIZE, self._controller.onSize) # Add additional key events self.Bind(wx.EVT_KEY_DOWN, self._controller.onKey) self.update(None, None)
class CabelFrame(wx.Frame, Observer): """ CabelFrame. A frame showing the contents of a single Cabel session. @type _controller: view.workspace.CabelController @ivar _controller: Corresponding controller for this view. @type _modules: list @ivar _modules: List of modules to paint on workspace. @type _connections: list @ivar _connections: List of connections to paint on workspace. @type _draggedCable: model.view.connection.Connection @ivar _draggedCable: Visual feedback when connection two modules. @type _modulesDict: dict @ivar _modulesDict: Dictionary to map ids to their corresponding view modules. @type __fileMenu: wx.Menu @ivar __fileMenu: File Menu. @type _modulesMenu: wx.Menu @ivar _modulesMenu: Menu with all in th modules- searchpath defined xml modules @type __recentMenuId: id @ivar __recentMenuId: Id of RecentMenu Entry. @type _splitter: CabelSplitterWindow @ivar _splitter: Splitter Window. @type _shell: wx.py.shell.Shell @ivar _shell: Python shell for command input. @type config: tools.config.Config.View @ivar config: relevant config Vars @type statusbar: wx.StatusBar @ivar statusbar: Statusbar of CabelFrame @type workspace: view.workspace.CabelScrolledWindow @ivar workspace: Workspace for synth building. @type ioTextCtrl: wx.TextCtrl @ivar ioTextCtrl: Gets the stdout and stderr output of the app @type fileName: string @ivar fileName: Name of the workspace @type filePath: string @ivar filePath: Save-Path of the workspace. @type zoom: int @ivar zoom: Zoom. """ def __init__(self, model): """ Standard constructor. @type model: model.workspace.Workspace @param model: Corresponding model for this view. """ # Initialize Observer Observer.__init__(self) parent = None # View related config vars self.config = model.config.view # Filename, etcetera self.fileName = '' self.filePath = os.path.join(os.getcwd(), 'examples') self._cwFileNewCount = 1 size = wx.Size(self.config.getVal(tools.config.View.FRAMEWIDTH), \ self.config.getVal(tools.config.View.FRAMEHEIGHT)) wx.Frame.__init__(self, parent, -1, self.GetTitle(), size=size) # Remember Maximized Frame; Only works on MSW if sys.platform in ("win32") and self.config.getVal( tools.config.View.FRAME_MAXIMIZED): self.Maximize(True) # Zoom self.zoom = self.config.getVal(tools.config.View.ZOOM_LASTVALUE) # initialize controller self._controller = CabelController(model, self) # List of modules and connections self._modules = [] self._connections = [] self._draggedCable = None self._modulesDict = {} # Create menus menubar = wx.MenuBar() # File self._filemenu = wx.Menu() self._filemenu.Append(wx.ID_NEW, "&New\tCTRL-N", "New..") self._filemenu.Append(wx.ID_OPEN, "&Open\tCTRL-O", "Open a Workspace") # RecentMenu self._recentMenuId = wx.NewId() recentMenu = self._getRecentMenu() self._filemenu.AppendMenu(self._recentMenuId, "Open Recent...", recentMenu) if recentMenu.GetMenuItemCount() == 0: self._filemenu.Enable(self._recentMenuId, False) self._filemenu.AppendSeparator() # Save self._filemenu.Append(wx.ID_SAVE, "&Save\tCTRL-S", "Save Workspace") self._filemenu.Append(wx.ID_SAVEAS, "S&ave As\tALT-S", "Save Workpsace As...") self._filemenu.AppendSeparator() # Csound start/stop self._playStopId = wx.NewId() self._filemenu.Append( self._playStopId, self._controller._model.isPlaying() and 'Stop Csound\tCTRL-Y' or 'Start Csound\tCTRL-Y') idExportToCsd = wx.NewId() self._filemenu.Append( idExportToCsd, "&Export to CSD\tCTRL-E", "Export workspace to CSD (Csound Unified File Format)") self._filemenu.AppendSeparator() # Exit self._filemenu.Append(wx.ID_EXIT, "E&xit\tCTRL-Q", "Exit Cabel") menubar.Append(self._filemenu, "&File") # Modules self._modulesMenu = self.getModulesMenu( self._controller._getXmlModuleList()) menubar.Append(self._modulesMenu, "&Modules") # Options self._optionsmenu = wx.Menu() # bottom Pane # -> properties if self.config.getVal( tools.config.View.BOTTOMWINDOW_REMEMBERPROPERTIES): showBottomPane = self.config.getVal( tools.config.View.BOTTOMWINDOW_SHOW) else: showBottomPane = self.config.getDefault( tools.config.View.BOTTOMWINDOW_SHOW) # -> menu entry self.id_bottomPane = wx.NewId() self._optionsmenu.AppendCheckItem(self.id_bottomPane, "Show &Bottom Pane\tALT-X", "Show Bottom Pane") self._optionsmenu.Check(self.id_bottomPane, showBottomPane) id_refreshMods = wx.NewId() self._optionsmenu.Append(id_refreshMods, "&Refresh Module List\tALT-R", "Refresh the Xml-Module List") self._optionsmenu.AppendSeparator() # Preferences id_options = wx.NewId() self._optionsmenu.Append(id_options, "Preferences\tALT-P", "Preferences") menubar.Append(self._optionsmenu, "&Options") self.SetMenuBar(menubar) # Associate menu events to handler functions self.Bind(wx.EVT_MENU, self._controller.onNew, id=wx.ID_NEW) self.Bind(wx.EVT_MENU, self._controller.onOpen, id=wx.ID_OPEN) self.Bind(wx.EVT_MENU, self._controller.onSave, id=wx.ID_SAVE) self.Bind(wx.EVT_MENU, self._controller.onSaveAs, id=wx.ID_SAVEAS) self.Bind(wx.EVT_MENU, self._controller.onPlayStop, id=self._playStopId) self.Bind(wx.EVT_MENU, self._controller.onMenuExportToCsd, id=idExportToCsd) self.Bind(wx.EVT_MENU, self._controller.onMenuExit, id=wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.onToggleBottomPane, id=self.id_bottomPane) self.Bind(wx.EVT_MENU, self._controller._refreshModulesMenu, id=id_refreshMods) self.Bind(wx.EVT_MENU, self._controller.onOptionsOpen, id=id_options) self.Bind(wx.EVT_CLOSE, self._controller.onClose) # Create Statusbar self.statusbar = CabelStatusBar(self) self.SetStatusBar(self.statusbar) self.SetStatusBarPane(0) # Create splitted window self._splitter = CabelSplitterWindow(self, wx.ID_ANY) # Add workspace for synth building self.workspace = CabelScrolledWindow(self._splitter, -1, self) # wxTextCtrl which gets the stdout and stderr output of the app self.ioTextCtrl = None # Initialize Splitter self._splitter.initialize(self.workspace) # Associate mouse events to handler functions self.workspace.Bind(wx.EVT_LEFT_DOWN, self._controller.onMouseLeftDown) self.workspace.Bind(wx.EVT_LEFT_UP, self._controller.onMouseLeftUp) self.workspace.Bind(wx.EVT_LEFT_DCLICK, self._controller.onMouseLeftDclick) self.workspace.Bind(wx.EVT_RIGHT_DOWN, self._controller.onMouseRightDown) self.workspace.Bind(wx.EVT_MIDDLE_DOWN, self._controller.onMouseMiddleDown) self.workspace.Bind(wx.EVT_MOTION, self._controller.onMouseMotion) self.Bind(wx.EVT_SIZE, self._controller.onSize) # Add additional key events self.Bind(wx.EVT_KEY_DOWN, self._controller.onKey) self.update(None, None) def createWorkspace(self, workspaceReader): """ """ # Open workspace from worksp if workspaceReader: # Set view modules self._modules = workspaceReader.viewModules.values() self._modulesDict = workspaceReader.viewModules # Set connections self._connections = [] for c in workspaceReader.connections: outModule = self._modulesDict[c['fromModuleId']] inModule = self._modulesDict[c['toModuleId']] self._connections.append(ModuleConnection(outModule, \ c['fromVarId'], \ inModule, \ c['toVarId'])) # Set file location self.fileName = workspaceReader.fileName self.filePath = workspaceReader.filePath # Delete old workspace and create new one else: self._modules = [] self._modulesDict = {} self._connections = [] self.fileName = '' self._cwFileNewCount += 1 self._controller._actModule = None self.workspace.Refresh() return self def update(self, observable, arg): """ This method is called whenever the observed object is changed. An application calls an observable object's notifyObservers method to have all the object's observers notified of the change. @type o: Observable @param o: The observable object. @type arg: object @param arg: An argument passed to the notifyObservers method. """ # Refresh the workspace? refresh = True # Read arg gand do what has to be done. if arg: # model.workspace.addModule; # arg[1] module to add if arg[0] == 'add': newModule = arg[1] self.addModule(newModule, self._controller._lastMousePt) # model.workspace.removeModule; # arg[1] module to remove, arg[2] in- & outgoing connections to remove elif arg[0] == 'remove': rmModule = arg[1] rmConnections = arg[2] self.removeModule(rmModule, rmConnections) # model.workspace.connect; # arg[1] new connection elif arg[0] == 'connect': newConnection = arg[1] self.addConnection(newConnection) # model.workspace.disconnect; # arg[1] connection to be removed elif arg[0] == 'disconnect': rmConnections = arg[1] self.removeConnection(rmConnections) # model.workspace.setValue: # arg[1] input variable which changed, arg[2] new value of input var elif arg[0] == 'set': inVar = arg[1] newValue = arg[2] # gets triggered by the config.setting.updateView method: # arg[1] in view workspace defined method without parameters. elif arg[0] == 'funCall': self._controller._callFunc(self, arg[1]) # Repaints the workspace if refresh: self.workspace.Refresh() def onToggleBottomPane(self, event): """ Toggle bottom pane in the splitter window. @type event: wx.Event @param event: Event associated with this function. """ if not self.config.getVal(tools.config.View.BOTTOMWINDOW_SHOW): self._splitter.split() else: self._splitter.unSplit() def addModule(self, module, pt): """ Adds module to view workspace. @type module: model.module.Module @param module: module to add to view workspace @type pt: wx.Point @param pt: Position on the workspace of module to add """ # Module is not yet on the view.workspace if not self._modulesDict.has_key(module.id): viewModule = Module(pt.x, pt.y, module, self._controller) # if lastToAddPosition not changes, this helps pt.x += 25 pt.y += 20 self._modulesDict[module.id] = viewModule # Yet it is else: viewModule = self._modulesDict[module.id] # If it is not yet in the internal list of view.Modules if viewModule not in self._modules: # then add it self._modules.append(viewModule) # Set added module to actModule self._controller.setModuleFocus(viewModule) return viewModule def removeModule(self, module, connections): """ Removes module from workspace. @type module: model.module.Module @param module: module to be removed from view workspace. @type connections: list @param connections: list of model.connection.Connection to delete. """ # remove modules connections for c in connections: self.removeConnection(c) # remove module if self._modulesDict.has_key(module.id): viewModule = self._modulesDict[module.id] self._modules.remove(viewModule) if self._controller._actModule == viewModule: self._controller.setModuleFocus(None) del self._modulesDict[module.id] def addConnection(self, connection): """ Add connection to view workspace. @type connection: model.connection.Connection @param connection: Model connection to add to view workspace. """ outputNum = connection.fromVar.module.outVars.index(connection.fromVar) inputNum = connection.toVar.module.inVars.index(connection.toVar) newConnection = ModuleConnection( self._modulesDict[connection.fromVar.module.id], outputNum, self._modulesDict[connection.toVar.module.id], inputNum) if newConnection not in self._connections: self._connections.append(newConnection) def removeConnection(self, connection): """ Remove connection from view workspace. @type connection: model.connection.Connection @param connection: Connection to remove from view workspace. """ # Create corresponding view connection outputNum = connection.fromVar.module.outVars.index(connection.fromVar) inputNum = connection.toVar.module.inVars.index(connection.toVar) fromModule = connection.fromVar.module toModule = connection.toVar.module rmConnection = ModuleConnection(self._modulesDict[fromModule.id], outputNum, self._modulesDict[toModule.id], inputNum) # If in _connections list remove it if rmConnection in self._connections: self._connections.remove(rmConnection) def getModuleAt(self, pt): """ Return the first module found at given point. @type pt: wx.Point @param pt: Point to test if on a module. @rtype : view.module.Module @return: Module at point or None. """ # Search from last added module to first for i in range(len(self._modules) - 1, -1, -1): if self._modules[i].isOnModule(pt): return self._modules[i] return None def scrollWorkspaceOnBorder(self, pt): """ If pt is on workspace border scrolls workspace. @type pt: wx.Point @param pt: Point to test if on worspace border. """ unitX, unitY = self.workspace.GetScrollPixelsPerUnit() sizeX, sizeY = self.workspace.GetClientSize() originX, originY = self.workspace.GetViewStart() distanceLeft = pt.x - originX * unitX distanceRight = originX * unitX + sizeX - pt.x distanceTop = pt.y - originY * unitY distanceBottom = originY * unitY + sizeY - pt.y if distanceLeft < 10: self.workspace.Scroll(originX - 1, -1) if distanceRight < 10: self.workspace.Scroll(originX + 1, -1) if distanceTop < 10: self.workspace.Scroll(-1, originY - 1) if distanceBottom < 10: self.workspace.Scroll(-1, originY + 1) def getModulesMenu(self, xmlModuleList): """ Gets the Menu for all the defined Xml Modules. @type xmlModuleList: list @param xmlModuleList: List of tuples which represents the structure of the modules folder. @rtype: wx.Menu @return: Menu """ modulesmenu = wx.Menu() for xmlModuleListItem in xmlModuleList: newId = wx.NewId() if isinstance(xmlModuleListItem[1], list): modulesmenu.AppendMenu( newId, xmlModuleListItem[0], self.getModulesMenu(xmlModuleListItem[1])) else: self._controller._idToModules[newId] = xmlModuleListItem[ 1].fullName modulesmenu.Append(newId, xmlModuleListItem[0], xmlModuleListItem[1].description) self.Bind(wx.EVT_MENU, self._controller.onModulesMenu, id=newId) return modulesmenu def getModuleMenu(self): """ Return menu when right clicking on a module. """ modulemenu = wx.Menu() # "Remove Module" Entry delId = wx.NewId() modulemenu.Append(delId, 'Remove Module') self.Bind(wx.EVT_MENU, self._controller.onRemoveActModule, id=delId) # "Zoom Module" Entries if self.config.getVal(tools.config.View.ZOOM_INDIVIDUAL_ACTIVE): # Zoom Entry modulemenu.AppendSeparator() # Zoom Entry Ids zoomInId = wx.NewId() zoomOutId = wx.NewId() # Zooms zoomDefault = self.config.getVal( tools.config.View.ZOOM_FACTOR_DEFAULT) # Scales scaleInFactor = scaleOutFactor = zoomDefault / 100 # Zoom Menu Entries modulemenu.Append(zoomInId, 'zoom +' + str(zoomDefault) + '%') modulemenu.Append(zoomOutId, 'zoom -' + str(zoomDefault) + '%') # Bindings self.Bind(wx.EVT_MENU, self._controller.onScaleInActModule, id=zoomInId) self.Bind(wx.EVT_MENU, self._controller.onScaleOutActModule, id=zoomOutId) if self.config.getVal(tools.config.Directories.EDITOR): modulemenu.AppendSeparator() viewXmlId = wx.NewId() modulemenu.Append(viewXmlId, 'Show Module Xml') self.Bind(wx.EVT_MENU, self._controller.onShowModuleXML, id=viewXmlId) return modulemenu def _getRecentMenu(self): """ """ configDir = self._controller._configDirs recentList = [] recentList = configDir.getVal(tools.config.Directories.RECENTFILES) recentMenu = wx.Menu() for i in xrange(0, len(recentList)): recentId = wx.NewId() recentMenu.Append( recentId, '&' + str(i) + ' ' + str(os.path.normpath(recentList[i]))) self._controller._idToRecent[recentId] = str(recentList[i]) self.Bind(wx.EVT_MENU, self._controller.onRecent, id=recentId) return recentMenu def reloadRecentMenu(self): """ """ self._filemenu.Remove(self._recentMenuId) recentMenu = self._getRecentMenu() self._filemenu.InsertMenu(2, self._recentMenuId, 'Open Recent...', recentMenu) # Depending if there are Items in the RecentMenu Disable / Enable it if self._filemenu.IsEnabled(self._recentMenuId): if recentMenu.GetMenuItemCount() == 0: self._filemenu.Enable(self._recentMenuId, False) else: if recentMenu.GetMenuItemCount() > 0: self._filemenu.Enable(self._recentMenuId, True) def createDraggedCable(self, startPt, endPt): """ Create connection between startPt and endPt as visual feedback for connection mode. @type startPt: wx.Point @param startPt: Start point of dragged cable. @type endPt: wx.Point @param endPt: End point of dragged cable. """ self._draggedCable = Connection(startPt, endPt) def removeDraggedCable(self): """ Remove dragged cable from workspace. """ self._draggedCable = None self.workspace.Refresh() def getModules(self): """ Return list of view modules. @rtype: List @return: List of view modules. """ return self._modules def getConnections(self): """ Return list of view connections. @rtype: List @return: List of view connections. """ return self._connections def getDraggedCable(self): """ Return dragged cable object. @rtype: model.view.connection.Connection @return: Visual feedback when connection two modules. """ return self._draggedCable def GetTitle(self): """ Return Title string for the CabelFrame. @rtype: string @return: Title string for the CabelFrame. """ try: star = (self._controller._saved and [''] or ['*'])[0] except AttributeError: # self._controller isn't defined yet star = '' if self.fileName == '': documentName = 'new' + str(self._cwFileNewCount) + '.cw' else: documentName = self.fileName + '.cw' return 'Cabel - [' + documentName + ']' + star
class CabelFrame(wx.Frame, Observer): """ CabelFrame. A frame showing the contents of a single Cabel session. @type _controller: view.workspace.CabelController @ivar _controller: Corresponding controller for this view. @type _modules: list @ivar _modules: List of modules to paint on workspace. @type _connections: list @ivar _connections: List of connections to paint on workspace. @type _draggedCable: model.view.connection.Connection @ivar _draggedCable: Visual feedback when connection two modules. @type _modulesDict: dict @ivar _modulesDict: Dictionary to map ids to their corresponding view modules. @type __fileMenu: wx.Menu @ivar __fileMenu: File Menu. @type _modulesMenu: wx.Menu @ivar _modulesMenu: Menu with all in th modules- searchpath defined xml modules @type __recentMenuId: id @ivar __recentMenuId: Id of RecentMenu Entry. @type _splitter: CabelSplitterWindow @ivar _splitter: Splitter Window. @type _shell: wx.py.shell.Shell @ivar _shell: Python shell for command input. @type config: tools.config.Config.View @ivar config: relevant config Vars @type statusbar: wx.StatusBar @ivar statusbar: Statusbar of CabelFrame @type workspace: view.workspace.CabelScrolledWindow @ivar workspace: Workspace for synth building. @type ioTextCtrl: wx.TextCtrl @ivar ioTextCtrl: Gets the stdout and stderr output of the app @type fileName: string @ivar fileName: Name of the workspace @type filePath: string @ivar filePath: Save-Path of the workspace. @type zoom: int @ivar zoom: Zoom. """ def __init__(self, model): """ Standard constructor. @type model: model.workspace.Workspace @param model: Corresponding model for this view. """ # Initialize Observer Observer.__init__(self) parent = None # View related config vars self.config = model.config.view # Filename, etcetera self.fileName = '' self.filePath = os.path.join(os.getcwd(), 'examples') self._cwFileNewCount = 1 size = wx.Size(self.config.getVal(tools.config.View.FRAMEWIDTH), \ self.config.getVal(tools.config.View.FRAMEHEIGHT)) wx.Frame.__init__(self, parent, -1, self.GetTitle(), size = size) # Remember Maximized Frame; Only works on MSW if sys.platform in ("win32") and self.config.getVal(tools.config.View.FRAME_MAXIMIZED): self.Maximize(True) # Zoom self.zoom = self.config.getVal(tools.config.View.ZOOM_LASTVALUE) # initialize controller self._controller = CabelController(model, self) # List of modules and connections self._modules = [] self._connections = [] self._draggedCable = None self._modulesDict = {} # Create menus menubar = wx.MenuBar() # File self._filemenu = wx.Menu() self._filemenu.Append(wx.ID_NEW, "&New\tCTRL-N", "New..") self._filemenu.Append(wx.ID_OPEN, "&Open\tCTRL-O", "Open a Workspace") # RecentMenu self._recentMenuId = wx.NewId() recentMenu = self._getRecentMenu() self._filemenu.AppendMenu(self._recentMenuId, "Open Recent...", recentMenu) if recentMenu.GetMenuItemCount() == 0: self._filemenu.Enable(self._recentMenuId, False) self._filemenu.AppendSeparator() # Save self._filemenu.Append(wx.ID_SAVE, "&Save\tCTRL-S", "Save Workspace") self._filemenu.Append(wx.ID_SAVEAS, "S&ave As\tALT-S", "Save Workpsace As...") self._filemenu.AppendSeparator() # Csound start/stop self._playStopId = wx.NewId() self._filemenu.Append(self._playStopId, self._controller._model.isPlaying() and 'Stop Csound\tCTRL-Y' or 'Start Csound\tCTRL-Y') idExportToCsd = wx.NewId() self._filemenu.Append(idExportToCsd, "&Export to CSD\tCTRL-E", "Export workspace to CSD (Csound Unified File Format)") self._filemenu.AppendSeparator() # Exit self._filemenu.Append(wx.ID_EXIT, "E&xit\tCTRL-Q", "Exit Cabel") menubar.Append(self._filemenu, "&File") # Modules self._modulesMenu = self.getModulesMenu(self._controller._getXmlModuleList()) menubar.Append(self._modulesMenu, "&Modules") # Options self._optionsmenu = wx.Menu() # bottom Pane # -> properties if self.config.getVal(tools.config.View.BOTTOMWINDOW_REMEMBERPROPERTIES): showBottomPane = self.config.getVal(tools.config.View.BOTTOMWINDOW_SHOW) else: showBottomPane = self.config.getDefault(tools.config.View.BOTTOMWINDOW_SHOW) # -> menu entry self.id_bottomPane = wx.NewId() self._optionsmenu.AppendCheckItem(self.id_bottomPane, "Show &Bottom Pane\tALT-X", "Show Bottom Pane") self._optionsmenu.Check(self.id_bottomPane, showBottomPane) id_refreshMods = wx.NewId() self._optionsmenu.Append(id_refreshMods, "&Refresh Module List\tALT-R", "Refresh the Xml-Module List") self._optionsmenu.AppendSeparator() # Preferences id_options = wx.NewId() self._optionsmenu.Append(id_options, "Preferences\tALT-P", "Preferences") menubar.Append(self._optionsmenu, "&Options") self.SetMenuBar(menubar) # Associate menu events to handler functions self.Bind(wx.EVT_MENU, self._controller.onNew, id = wx.ID_NEW) self.Bind(wx.EVT_MENU, self._controller.onOpen, id = wx.ID_OPEN) self.Bind(wx.EVT_MENU, self._controller.onSave, id = wx.ID_SAVE) self.Bind(wx.EVT_MENU, self._controller.onSaveAs, id = wx.ID_SAVEAS) self.Bind(wx.EVT_MENU, self._controller.onPlayStop, id = self._playStopId) self.Bind(wx.EVT_MENU, self._controller.onMenuExportToCsd, id = idExportToCsd) self.Bind(wx.EVT_MENU, self._controller.onMenuExit, id = wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.onToggleBottomPane, id = self.id_bottomPane) self.Bind(wx.EVT_MENU, self._controller._refreshModulesMenu, id = id_refreshMods) self.Bind(wx.EVT_MENU, self._controller.onOptionsOpen, id = id_options) self.Bind(wx.EVT_CLOSE, self._controller.onClose) # Create Statusbar self.statusbar = CabelStatusBar(self) self.SetStatusBar(self.statusbar) self.SetStatusBarPane(0) # Create splitted window self._splitter = CabelSplitterWindow(self, wx.ID_ANY) # Add workspace for synth building self.workspace = CabelScrolledWindow(self._splitter, -1, self) # wxTextCtrl which gets the stdout and stderr output of the app self.ioTextCtrl = None # Initialize Splitter self._splitter.initialize(self.workspace) # Associate mouse events to handler functions self.workspace.Bind(wx.EVT_LEFT_DOWN, self._controller.onMouseLeftDown) self.workspace.Bind(wx.EVT_LEFT_UP, self._controller.onMouseLeftUp) self.workspace.Bind(wx.EVT_LEFT_DCLICK, self._controller.onMouseLeftDclick) self.workspace.Bind(wx.EVT_RIGHT_DOWN, self._controller.onMouseRightDown) self.workspace.Bind(wx.EVT_MIDDLE_DOWN, self._controller.onMouseMiddleDown) self.workspace.Bind(wx.EVT_MOTION, self._controller.onMouseMotion) self.Bind(wx.EVT_SIZE, self._controller.onSize) # Add additional key events self.Bind(wx.EVT_KEY_DOWN, self._controller.onKey) self.update(None, None) def createWorkspace(self, workspaceReader): """ """ # Open workspace from worksp if workspaceReader: # Set view modules self._modules = workspaceReader.viewModules.values() self._modulesDict = workspaceReader.viewModules # Set connections self._connections = [] for c in workspaceReader.connections: outModule = self._modulesDict[c['fromModuleId']] inModule = self._modulesDict[c['toModuleId']] self._connections.append(ModuleConnection(outModule, \ c['fromVarId'], \ inModule, \ c['toVarId'])) # Set file location self.fileName = workspaceReader.fileName self.filePath = workspaceReader.filePath # Delete old workspace and create new one else: self._modules = [] self._modulesDict = {} self._connections = [] self.fileName = '' self._cwFileNewCount += 1 self._controller._actModule = None self.workspace.Refresh() return self def update(self, observable, arg): """ This method is called whenever the observed object is changed. An application calls an observable object's notifyObservers method to have all the object's observers notified of the change. @type o: Observable @param o: The observable object. @type arg: object @param arg: An argument passed to the notifyObservers method. """ # Refresh the workspace? refresh = True # Read arg gand do what has to be done. if arg: # model.workspace.addModule; # arg[1] module to add if arg[0] == 'add': newModule = arg[1] self.addModule(newModule, self._controller._lastMousePt) # model.workspace.removeModule; # arg[1] module to remove, arg[2] in- & outgoing connections to remove elif arg[0] == 'remove': rmModule = arg[1] rmConnections = arg[2] self.removeModule(rmModule, rmConnections) # model.workspace.connect; # arg[1] new connection elif arg[0] == 'connect': newConnection = arg[1] self.addConnection(newConnection) # model.workspace.disconnect; # arg[1] connection to be removed elif arg[0] == 'disconnect': rmConnections = arg[1] self.removeConnection(rmConnections) # model.workspace.setValue: # arg[1] input variable which changed, arg[2] new value of input var elif arg[0] == 'set': inVar = arg[1] newValue = arg[2] # gets triggered by the config.setting.updateView method: # arg[1] in view workspace defined method without parameters. elif arg[0] == 'funCall': self._controller._callFunc(self, arg[1]) # Repaints the workspace if refresh: self.workspace.Refresh() def onToggleBottomPane(self, event): """ Toggle bottom pane in the splitter window. @type event: wx.Event @param event: Event associated with this function. """ if not self.config.getVal(tools.config.View.BOTTOMWINDOW_SHOW): self._splitter.split() else: self._splitter.unSplit() def addModule(self, module, pt): """ Adds module to view workspace. @type module: model.module.Module @param module: module to add to view workspace @type pt: wx.Point @param pt: Position on the workspace of module to add """ # Module is not yet on the view.workspace if not self._modulesDict.has_key(module.id): viewModule = Module(pt.x, pt.y, module, self._controller) # if lastToAddPosition not changes, this helps pt.x += 25 pt.y += 20 self._modulesDict[module.id] = viewModule # Yet it is else: viewModule = self._modulesDict[module.id] # If it is not yet in the internal list of view.Modules if viewModule not in self._modules: # then add it self._modules.append(viewModule) # Set added module to actModule self._controller.setModuleFocus(viewModule) return viewModule def removeModule(self, module, connections): """ Removes module from workspace. @type module: model.module.Module @param module: module to be removed from view workspace. @type connections: list @param connections: list of model.connection.Connection to delete. """ # remove modules connections for c in connections: self.removeConnection(c) # remove module if self._modulesDict.has_key(module.id): viewModule = self._modulesDict[module.id] self._modules.remove(viewModule) if self._controller._actModule == viewModule: self._controller.setModuleFocus(None) del self._modulesDict[module.id] def addConnection(self, connection): """ Add connection to view workspace. @type connection: model.connection.Connection @param connection: Model connection to add to view workspace. """ outputNum = connection.fromVar.module.outVars.index(connection.fromVar) inputNum = connection.toVar.module.inVars.index(connection.toVar) newConnection = ModuleConnection(self._modulesDict[connection.fromVar.module.id], outputNum, self._modulesDict[connection.toVar.module.id], inputNum) if newConnection not in self._connections: self._connections.append(newConnection) def removeConnection(self, connection): """ Remove connection from view workspace. @type connection: model.connection.Connection @param connection: Connection to remove from view workspace. """ # Create corresponding view connection outputNum = connection.fromVar.module.outVars.index(connection.fromVar) inputNum = connection.toVar.module.inVars.index(connection.toVar) fromModule = connection.fromVar.module toModule = connection.toVar.module rmConnection = ModuleConnection(self._modulesDict[fromModule.id], outputNum, self._modulesDict[toModule.id], inputNum) # If in _connections list remove it if rmConnection in self._connections: self._connections.remove(rmConnection) def getModuleAt(self, pt): """ Return the first module found at given point. @type pt: wx.Point @param pt: Point to test if on a module. @rtype : view.module.Module @return: Module at point or None. """ # Search from last added module to first for i in range(len(self._modules) - 1, -1, -1): if self._modules[i].isOnModule(pt): return self._modules[i] return None def scrollWorkspaceOnBorder(self, pt): """ If pt is on workspace border scrolls workspace. @type pt: wx.Point @param pt: Point to test if on worspace border. """ unitX, unitY = self.workspace.GetScrollPixelsPerUnit() sizeX, sizeY = self.workspace.GetClientSize() originX, originY = self.workspace.GetViewStart() distanceLeft = pt.x - originX * unitX distanceRight = originX * unitX + sizeX - pt.x distanceTop = pt.y - originY * unitY distanceBottom = originY * unitY + sizeY - pt.y if distanceLeft < 10: self.workspace.Scroll(originX - 1, -1) if distanceRight < 10: self.workspace.Scroll(originX + 1, -1) if distanceTop < 10: self.workspace.Scroll(-1, originY - 1) if distanceBottom < 10: self.workspace.Scroll(-1, originY + 1) def getModulesMenu(self, xmlModuleList): """ Gets the Menu for all the defined Xml Modules. @type xmlModuleList: list @param xmlModuleList: List of tuples which represents the structure of the modules folder. @rtype: wx.Menu @return: Menu """ modulesmenu = wx.Menu() for xmlModuleListItem in xmlModuleList: newId = wx.NewId() if isinstance(xmlModuleListItem[1], list): modulesmenu.AppendMenu(newId, xmlModuleListItem[0], self.getModulesMenu(xmlModuleListItem[1])) else: self._controller._idToModules[newId] = xmlModuleListItem[1].fullName modulesmenu.Append(newId, xmlModuleListItem[0], xmlModuleListItem[1].description) self.Bind(wx.EVT_MENU, self._controller.onModulesMenu, id = newId) return modulesmenu def getModuleMenu(self): """ Return menu when right clicking on a module. """ modulemenu = wx.Menu() # "Remove Module" Entry delId = wx.NewId() modulemenu.Append(delId, 'Remove Module') self.Bind(wx.EVT_MENU, self._controller.onRemoveActModule, id = delId) # "Zoom Module" Entries if self.config.getVal(tools.config.View.ZOOM_INDIVIDUAL_ACTIVE): # Zoom Entry modulemenu.AppendSeparator() # Zoom Entry Ids zoomInId = wx.NewId() zoomOutId = wx.NewId() # Zooms zoomDefault = self.config.getVal(tools.config.View.ZOOM_FACTOR_DEFAULT) # Scales scaleInFactor = scaleOutFactor = zoomDefault / 100 # Zoom Menu Entries modulemenu.Append(zoomInId, 'zoom +' + str(zoomDefault) + '%') modulemenu.Append(zoomOutId, 'zoom -' + str(zoomDefault) + '%') # Bindings self.Bind(wx.EVT_MENU, self._controller.onScaleInActModule, id = zoomInId) self.Bind(wx.EVT_MENU, self._controller.onScaleOutActModule, id = zoomOutId) if self.config.getVal(tools.config.Directories.EDITOR): modulemenu.AppendSeparator() viewXmlId = wx.NewId() modulemenu.Append(viewXmlId, 'Show Module Xml') self.Bind(wx.EVT_MENU, self._controller.onShowModuleXML, id = viewXmlId) return modulemenu def _getRecentMenu(self): """ """ configDir = self._controller._configDirs recentList = [] recentList = configDir.getVal(tools.config.Directories.RECENTFILES) recentMenu = wx.Menu() for i in xrange (0, len(recentList)): recentId = wx.NewId() recentMenu.Append(recentId, '&' + str(i) + ' ' + str(os.path.normpath(recentList[i]))) self._controller._idToRecent[recentId] = str(recentList[i]) self.Bind(wx.EVT_MENU, self._controller.onRecent, id = recentId) return recentMenu def reloadRecentMenu(self): """ """ self._filemenu.Remove(self._recentMenuId) recentMenu = self._getRecentMenu() self._filemenu.InsertMenu(2, self._recentMenuId, 'Open Recent...', recentMenu) # Depending if there are Items in the RecentMenu Disable / Enable it if self._filemenu.IsEnabled(self._recentMenuId): if recentMenu.GetMenuItemCount() == 0: self._filemenu.Enable(self._recentMenuId, False) else: if recentMenu.GetMenuItemCount() > 0: self._filemenu.Enable(self._recentMenuId, True) def createDraggedCable(self, startPt, endPt): """ Create connection between startPt and endPt as visual feedback for connection mode. @type startPt: wx.Point @param startPt: Start point of dragged cable. @type endPt: wx.Point @param endPt: End point of dragged cable. """ self._draggedCable = Connection(startPt, endPt) def removeDraggedCable(self): """ Remove dragged cable from workspace. """ self._draggedCable = None self.workspace.Refresh() def getModules(self): """ Return list of view modules. @rtype: List @return: List of view modules. """ return self._modules def getConnections(self): """ Return list of view connections. @rtype: List @return: List of view connections. """ return self._connections def getDraggedCable(self): """ Return dragged cable object. @rtype: model.view.connection.Connection @return: Visual feedback when connection two modules. """ return self._draggedCable def GetTitle(self): """ Return Title string for the CabelFrame. @rtype: string @return: Title string for the CabelFrame. """ try: star = (self._controller._saved and [''] or ['*'])[0] except AttributeError: # self._controller isn't defined yet star = '' if self.fileName == '': documentName = 'new' + str(self._cwFileNewCount) + '.cw' else: documentName = self.fileName + '.cw' return 'Cabel - [' + documentName + ']' + star