"""Module containing the wxPython implementation for the GUI tutorial COMMENTS: ========= + Not a lot of Python doc, but easy to use C++ documentation (easy translation) + Consistent object construction without dependence on positional params + Clean, easy to use for beginners IDIOMS: ======= + Events are tied to the ID of the widget via EVT_XXX wrapper function + Unique id's generated automatically using wxNewId() + Use wxBoxSizer(orientation) to pack child widgets via Add() + Timing issue when adding child widgets and them knowing their size; call Layout() and Refresh() after widget has had a chance to 'settle' """ import os from wxPython import * from wxPython.wx import * from wxPython.html import wxHtmlWindow from wxPython.lib.anchors import LayoutAnchors from oogui import OOGui import oogui # const values used in wxGui _PRESTATUS = "Directory: " _ANCHOR = wxEXPAND | wxALL # flags used to set resizing preferences # IDs used for controls with event handlers; others use -1 for id _GOTO_BUTTON_ID = wxNewId() _GOTO_TEXT_ID = wxNewId() _FILE_LIST_ID = wxNewId() _DIR_LIST_ID = wxNewId() _FILE_TEXT_ID = wxNewId() def _setSizer(w, orientation=wxVERTICAL): """General utility that creates a wxBoxSizer for specified container""" sizer = wxBoxSizer(orientation) w.SetAutoLayout(true) w.SetSizer(sizer) return sizer def _getLabeledList(w, label, listId, command): """Returns a sized container and its listbox with listbox's selection event mapped to specified command. The box will be labeled at the top left with value of label. """ sizer = wxBoxSizer(wxVERTICAL) sizer.Add(wxStaticText(w, -1, label), 0) listbox = wxListBox(w, listId) sizer.Add(listbox, 1, wxEXPAND) # bind the double-click event EVT_LISTBOX_DCLICK(w, listId, command) return sizer, listbox class WxGui(wxApp, OOGui): """Specialization of OOGui that uses wxPython. Note: wxPython requires that you derive a class from wxApp and provide an OnInit method for it that is automatically called from the wxApp constructor. """ def __init__(self, geometry=None, resfile=None, startdir=os.getcwd()): """Initialize parent objects""" redirectStdio = false logFilename = None wxApp.__init__(self, redirectStdio, logFilename) # remember, wxApp always calls the derived class's (i.e., WxGui) # OnInit() method, so self._topLevel has been created already OOGui.__init__(self, self._topLevel, geometry, resfile, startdir) def OnInit(self): """Automatically called when the wxApp starts up""" # initialize and get the top level window for application self._topLevel = wxFrame(NULL, -1, "") self.SetTopWindow(self._topLevel) # only BMP handler is loaded by default wxInitAllImageHandlers() return true # ok to continue # ------------------------------------------------------------------- # polymorphic implementations # ------------------------------------------------------------------- def _loadResources(self, resfile=None): """Load the resources (options) for all the 'named' widgets""" if resfile: # FIXME # self._topLevel.option_readfile(resfile, priority=OPTION_PRIORITY) print 'FIXME: Load resource file!!!' return true def _buildGui(self, title): """Builds the main user interface for the wxPython impl""" # set status before creating other panels self._topLevel.CreateStatusBar() # use the Sizer window layout scheme sizer = _setSizer(self._topLevel) # build the cwd panel sizer.Add(self._buildCWD(self._topLevel), 0, _ANCHOR) # build the split pane containing lists and display sizer.Add(self._buildPanes(self._topLevel), 1, _ANCHOR, oogui.BORDER) self._topLevel.SetTitle(title) def _startGui(self, geometry=None): """Starts the main event loop for the application""" # use geometry if specified if geometry: #self._topLevel.SetPosition(xxx) #self._topLevel.SetSize(xxx) # FIXME print 'set geometry: ', geometry else: self._topLevel.SetSize(wxSize(oogui.DISPLAY_WIDTH, oogui.APP_HEIGHT)) self._topLevel.Show(true) self.MainLoop() def _getCurrentDirectory(self): """Returns the full pathname of the current working directory""" return self._getCWD() def _getSelectedFile(self): """Returns the name of the selected file""" if self._selectedFile: return self._selectedFile return self._fileList.GetStringSelection() def _getSelectedDirectory(self): """Returns the name of the selected directory""" return self._dirList.GetStringSelection() def _getTopLevel(self): """Special access method used by base to ensure top level is created""" return self._topLevel def _displayBinary(self, filename): """Displays binary data files""" self._display.binary(filename) def _displayHTML(self, filename): """Displays HTML files""" self._display.html(filename) # call here instead of within DisplayPanel because of timing self._display.refresh() def _displayImage(self, filename): """Displays image files""" self._display.image(filename) # call here instead of within DisplayPanel because of timing self._display.refresh() def _displayText(self, filename): """Displays text (other) files""" self._display.text(filename) # call here instead of within DisplayPanel because of timing self._display.refresh() # ------------------------------------------------------------------- # private methods # ------------------------------------------------------------------- def _buildCWD(self, w): """Private method to construct the change working directory window""" panel = wxPanel(w, -1) sizer = _setSizer(panel, wxHORIZONTAL) gotoButton = wxButton(panel, _GOTO_BUTTON_ID, label="GoTo") sizer.Add(gotoButton, 0, wxALL, oogui.BORDER) EVT_BUTTON(self._topLevel, _GOTO_BUTTON_ID, self._setDirectory) self._gotoEntry = wxTextCtrl(panel, _GOTO_TEXT_ID, style=wxTE_PROCESS_ENTER) sizer.Add(self._gotoEntry, 1, wxALL, oogui.BORDER) EVT_TEXT_ENTER(self._gotoEntry, _GOTO_TEXT_ID, self._setDirectory) panel.SetSize(wxSize(panel.GetSize().width, self._gotoEntry.GetSize().height + oogui.BORDER*2)) return panel def _buildPanes(self, w): """Use a SplitterWindow with panes for lists and display views""" splitter = wxSplitterWindow(w, -1) listsPanel = self._buildLists(splitter) self._display = DisplayPanel(splitter) splitter.SplitHorizontally(listsPanel, self._display) splitter.SetMinimumPaneSize(oogui.SASH_MIN) splitter.SetSashPosition(oogui.SASH_POSITION) return splitter def _buildLists(self, w): """Builds the panel of directories and files list boxes""" listsPanel = wxPanel(w, -1) # create the directories list dirsSizer, self._dirList = _getLabeledList(listsPanel, oogui.DIRS_LABEL, _DIR_LIST_ID, self._directorySelected) # create the files list filesSizer, self._fileList = _getLabeledList(listsPanel, oogui.FILES_LABEL, _FILE_LIST_ID, self._fileSelected) # put the two lists side-by-side listsSizer = wxBoxSizer(wxHORIZONTAL) listsSizer.Add(dirsSizer, 1, _ANCHOR, oogui.BORDER) listsSizer.Add(filesSizer, 1, _ANCHOR, oogui.BORDER) # create the filename entry fSizer = wxBoxSizer(wxVERTICAL) fSizer.Add(wxStaticText(listsPanel, -1, label='Selected File'), 0) self._filename = wxTextCtrl(listsPanel, _FILE_TEXT_ID, style=wxTE_PROCESS_ENTER) fSizer.Add(self._filename, 0, wxEXPAND | wxSOUTH, oogui.BORDER) EVT_TEXT_ENTER(self._filename, _FILE_TEXT_ID, self._setFile) panelSizer = _setSizer(listsPanel) panelSizer.Add(listsSizer, 1, wxEXPAND) panelSizer.Add(fSizer, 0, _ANCHOR, oogui.BORDER) # set the list entries (finally! :) self._setEntries() return listsPanel def _getCWD(self): """Return the full pathname of current working directory""" return self._topLevel.GetStatusBar().GetStatusText()[len(_PRESTATUS):] def _setCWD(self, cwd): """Updates the GUI by setting the current working directory to 'cwd'""" if not cwd or not os.path.isdir(cwd): return self._setCurrentDirectory(cwd) self._setEntries() def _setDirectory(self, *unused): "Callback for setting the name of the current directory" self._setCWD(self._gotoEntry.GetValue()) def _setFilename(self, filename): """Sets the filename entry field when a file is selected; called by base class's _fileSelected() method """ self._filename.SetValue(filename) def _setEntries(self): """Sets the names of files and directories in the lists for the 'current selected directory'. """ cwd = self._getCWD() if not cwd: cwd = os.getcwd() self._setCurrentDirectory(cwd) self._gotoEntry.SetValue(cwd) # improvement to base class after submission to Python10 dirs, files = self._getEntries(cwd) # populate the list boxes with their respective entries self._dirList.Set(dirs) self._fileList.Set(files) # clear the selection, but only if there is at least one item if len(dirs) > 0: self._dirList.SetSelection(0, false) if len(files) > 0: self._fileList.SetSelection(0, false) def _setFile(self, *unused): """Records the currently selected file as a hook for the call to the base class's 'fileSelected()' method which determines the 'type' of the file, and dispatches the appropriate viewer. """ self._selectedFile = self._filename.GetValue() self._fileSelected() self._selectedFile = None def _setCurrentDirectory(self, pathname): """Use the status bar to indicate name of current working directory""" self._topLevel.SetStatusText(_PRESTATUS + pathname) # ----------------------------------------------------------------- # Specialized panel containing viewers for various file types # ----------------------------------------------------------------- class DisplayPanel(wxPanel): "The Display portion of the GUI (a wxPanel with an appropriate viewer)" # IDs used during GUI construction of DisplayPanel _TEXT_ID = wxNewId() def __init__(self, w): """Constructs the panel used to contain the display widget for file""" wxPanel.__init__(self, w, -1) self._bitmap = None def refresh(self): self.Layout() self.Refresh() def binary(self, unused): """Display nothing for binary files (for now ... maybe use bcat?)""" self._show(None) def html(self, filename): """Display HTML file""" self._show(filename) htmlWindow = wxHtmlWindow(self, -1) htmlWindow.LoadPage(filename) self._display(htmlWindow) def image(self, filename): """Display image file""" self._show(filename) bitmap = filename[filename.find(".")+1:].upper() if bitmap == "JPG": bitmap = 'JPEG' elif bitmap == "TIFF": bitmap = 'TIF' # dynamically create the name of the image type, and get # defined image type attribute from the wx namespace (globals()) imageType = globals().get('wxBITMAP_TYPE_'+bitmap, None) if imageType: sw = wxScrolledWindow(self, -1) bmp = wxImage(filename, imageType).ConvertToBitmap() w, h = bmp.GetWidth(), bmp.GetHeight() bitmap = wxStaticBitmap(sw, -1, bmp, wxPoint(10, 10), wxSize(w, h)) sw.SetScrollbars(w/4, h/4, 4, 4) self._display(sw) else: print 'Could not load image file: %s (%s)' % (filename, bitmap) def text(self, filename): """Display text of specified file""" self._show(filename) fileText, isBinary = oogui._getText(filename) if isBinary: self.binary(filename) return text = wxTextCtrl(self, DisplayPanel._TEXT_ID, size= \ wxSize(oogui.DISPLAY_WIDTH, oogui.DISPLAY_HEIGHT), style = wxTE_MULTILINE|wxTE_RICH, value = fileText) self._display(text) def _display(self, child): """Container needs to repaint window when child widgets change""" self._sizer.Add(child, 1, _ANCHOR, oogui.BORDER) def _show(self, filename): """Manage the display of the specified file for all display types""" self.DestroyChildren() self._sizer = _setSizer(self) if not filename: filename = oogui.DISPLAY_UNAVAIL labelText = oogui.DISPLAY_LABEL + filename # display filename as a label self._sizer.Add(wxStaticText(self, -1, label=labelText), 0, wxALL, oogui.BORDER)