"""! @package help.py @brief Help window @todo Needs improvements... Classes: - HelpWindow - MenuTreeWindow - AboutWindow (C) 2008-2009 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. @author Martin Landa """ import os import wx try: import wx.lib.agw.customtreectrl as CT except ImportError: import wx.lib.customtreectrl as CT import wx.lib.flatnotebook as FN import wx.lib.scrolledpanel as scrolled from wx.lib.wordwrap import wordwrap import menudata import gcmd import globalvar class HelpWindow(wx.Frame): """!GRASS Quickstart help window""" def __init__(self, parent, id, title, size, file): wx.Frame.__init__(self, parent=parent, id=id, title=title, size=size) sizer = wx.BoxSizer(wx.VERTICAL) # text helpFrame = wx.html.HtmlWindow(parent=self, id=wx.ID_ANY) helpFrame.SetStandardFonts (size = 10) helpFrame.SetBorders(10) wx.InitAllImageHandlers() helpFrame.LoadFile(file) self.Ok = True sizer.Add(item=helpFrame, proportion=1, flag=wx.EXPAND) self.SetAutoLayout(True) self.SetSizer(sizer) # sizer.Fit(self) # sizer.SetSizeHints(self) self.Layout() class MenuTreeWindow(wx.Frame): """!Show menu tree""" def __init__(self, parent, id = wx.ID_ANY, title = _("Menu tree window"), **kwargs): self.parent = parent # LayerManager wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs) self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) self.panel = wx.Panel(parent = self, id = wx.ID_ANY) self.dataBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, label=" %s " % _("Menu tree (double-click to run command)")) # tree self.tree = MenuTree(parent = self.panel, data = menudata.Data()) self.tree.Load() self.searchDict = { _('label') : 'label', # i18n workaround _('help') : 'help', _('command') : 'command', _('keywords') : 'keywords' } # search self.searchBy = wx.Choice(parent = self.panel, id = wx.ID_ANY, choices = [_('label'), _('help'), _('command'), _('keywords')]) self.search = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY, value = "", size = (-1, 25), style = wx.TE_PROCESS_ENTER) # statusbar self.statusbar = self.CreateStatusBar(number=1) # close on run self.closeOnRun = wx.CheckBox(parent = self.panel, id = wx.ID_ANY, label = _("Close dialog on run")) self.closeOnRun.SetValue(True) # buttons self.btnRun = wx.Button(self.panel, id = wx.ID_OK, label = _("Run")) self.btnRun.SetToolTipString(_("Run selected command")) self.btnRun.Enable(False) self.btnClose = wx.Button(self.panel, id = wx.ID_CLOSE) self.btnClose.SetToolTipString(_("Close dialog")) # bindings self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow) self.btnRun.Bind(wx.EVT_BUTTON, self.OnRun) self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem) self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar) self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated) self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected) self.__Layout() self.search.SetFocus() def __Layout(self): """!Do dialog layout""" sizer = wx.BoxSizer(wx.VERTICAL) # body dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL) dataSizer.Add(item = self.tree, proportion =1, flag = wx.EXPAND) # search searchSizer = wx.BoxSizer(wx.HORIZONTAL) searchSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY, label = _("Search:")), proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 3) searchSizer.Add(item = self.searchBy, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT, border = 5) searchSizer.Add(item = self.search, proportion = 1, flag = wx.EXPAND | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, border = 5) # buttons btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add(item = self.btnRun, proportion = 0, flag = wx.LEFT | wx.RIGHT, border = 5) btnSizer.Add(item = self.btnClose, proportion = 0, flag = wx.LEFT | wx.RIGHT, border = 5) sizer.Add(item = dataSizer, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5) sizer.Add(item = searchSizer, proportion=0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5) sizer.Add(item = btnSizer, proportion=0, flag = wx.ALIGN_RIGHT | wx.ALL, border = 5) sizer.Add(item = self.closeOnRun, proportion=0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5) self.panel.SetAutoLayout(True) self.panel.SetSizer(sizer) sizer.Fit(self.panel) self.Layout() self.SetSize((530, 370)) def OnCloseWindow(self, event): """!Close window""" self.Destroy() def OnRun(self, event): """!Run selected command""" if not self.tree.GetSelected(): return # should not happen data = self.tree.GetPyData(self.tree.GetSelected()) if not data: return handler = 'self.parent.' + data['handler'].lstrip('self.') if data['handler'] == 'self.OnXTerm': wx.MessageBox(parent = self, message = _('You must run this command from the menu or command line', 'This command require an XTerm'), caption = _('Message'), style=wx.OK | wx.ICON_ERROR | wx.CENTRE) elif data['command']: eval(handler)(event = None, cmd = data['command'].split()) else: eval(handler)(None) if self.closeOnRun.IsChecked(): self.OnCloseWindow(None) def OnItemActivated(self, event): """!Item activated (double-click)""" item = event.GetItem() if not item or not item.IsOk(): return data = self.tree.GetPyData(item) if not data or not data.has_key('command'): return self.tree.itemSelected = item self.OnRun(None) def OnItemSelected(self, event): """!Item selected""" item = event.GetItem() if not item or not item.IsOk(): return data = self.tree.GetPyData(item) if not data or not data.has_key('command'): return if data['command']: label = data['command'] + ' -- ' + data['help'] else: label = data['help'] self.statusbar.SetStatusText(label, 0) def OnShowItem(self, event): """!Highlight first found item in menu tree""" if len(self.tree.itemsMarked) > 0: if self.tree.GetSelected(): self.tree.ToggleItemSelection(self.tree.GetSelected()) idx = self.tree.itemsMarked.index(self.tree.GetSelected()) + 1 else: idx = 0 try: self.tree.ToggleItemSelection(self.tree.itemsMarked[idx]) self.tree.itemSelected = self.tree.itemsMarked[idx] self.tree.EnsureVisible(self.tree.itemsMarked[idx]) except IndexError: self.tree.ToggleItemSelection(self.tree.itemsMarked[0]) # reselect first item self.tree.EnsureVisible(self.tree.itemsMarked[0]) self.tree.itemSelected = self.tree.itemsMarked[0] else: for item in self.tree.root.GetChildren(): self.tree.Collapse(item) itemSelected = self.tree.GetSelection() if itemSelected: self.tree.ToggleItemSelection(itemSelected) self.tree.itemSelected = None if self.tree.itemSelected: self.btnRun.Enable() else: self.btnRun.Enable(False) def OnUpdateStatusBar(self, event): """!Update statusbar text""" element = self.searchDict[self.searchBy.GetStringSelection()] self.tree.itemsMarked = self.SearchItems(element = element, value = event.GetString()) self.tree.itemSelected = None nItems = len(self.tree.itemsMarked) if event.GetString(): self.statusbar.SetStatusText(_("%d items match") % nItems, 0) else: self.statusbar.SetStatusText("", 0) event.Skip() def SearchItems(self, element, value): """!Search item @param element element index (see self.searchBy) @param value @return list of found tree items """ items = list() if not value: return items item = self.tree.GetFirstChild(self.tree.root)[0] self.__ProcessItem(item, element, value, items) return items def __ProcessItem(self, item, element, value, listOfItems): """!Search items (used by SearchItems) @param item reference item @param listOfItems list of found items """ while item and item.IsOk(): subItem = self.tree.GetFirstChild(item)[0] if subItem: self.__ProcessItem(subItem, element, value, listOfItems) data = self.tree.GetPyData(item) if data and data.has_key(element) and \ value.lower() in data[element].lower(): listOfItems.append(item) item = self.tree.GetNextSibling(item) class MenuTree(CT.CustomTreeCtrl): """!Menu tree class""" def __init__(self, parent, data, id = wx.ID_ANY, ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS | \ CT.TR_LINES_AT_ROOT | CT.TR_SINGLE, **kwargs): self.parent = parent self.menudata = data super(MenuTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs) self.root = self.AddRoot(_("Menu tree")) self.itemsMarked = [] # list of marked items self.itemSelected = None def Load(self, data = None): """!Load menu data tree @param data menu data (None to use self.menudata) """ if not data: data = self.menudata self.itemsMarked = [] # list of marked items for eachMenuData in data.GetMenu(): for label, items in eachMenuData: item = self.AppendItem(parentId = self.root, text = label.replace('&', '')) self.__AppendItems(item, items) def __AppendItems(self, item, data): """!Append items into tree (used by Load() @param item tree item (parent) @parent data menu data""" for eachItem in data: if len(eachItem) == 2: if eachItem[0]: itemSub = self.AppendItem(parentId = item, text = eachItem[0]) self.__AppendItems(itemSub, eachItem[1]) else: if eachItem[0]: itemNew = self.AppendItem(parentId = item, text = eachItem[0]) data = { 'label' : eachItem[0], 'help' : eachItem[1], 'handler' : eachItem[2], 'command' : eachItem[3], 'keywords' : eachItem[4] } self.SetPyData(itemNew, data) def GetSelected(self): """!Get selected item""" return self.itemSelected class AboutWindow(wx.Frame): def __init__(self, parent): """!Create custom About Window @todo improve styling """ wx.Frame.__init__(self, parent=parent, id=wx.ID_ANY, size=(550,400), title=_('About GRASS GIS')) panel = wx.Panel(parent = self, id = wx.ID_ANY) # icon self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) # get version and web site version, svn_gis_h_rev, svn_gis_h_date = gcmd.RunCommand('g.version', flags = 'r', read = True).splitlines() infoTxt = wx.Panel(parent = panel, id = wx.ID_ANY) infoSizer = wx.BoxSizer(wx.VERTICAL) logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass.ico") logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY, bitmap = wx.Bitmap(name = logo, type = wx.BITMAP_TYPE_ICO)) infoSizer.Add(item = logoBitmap, proportion = 0, flag = wx.ALL | wx.ALIGN_CENTER, border = 10) i = 0 for label in [version.replace('GRASS', 'GRASS GIS').strip() + '\n\n', _('Official GRASS site: http://grass.osgeo.org') + '\n\n', _('GIS Library') + ' ' + svn_gis_h_rev + '(' + svn_gis_h_date.split(' ')[1] + ')']: info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, label = label) if i == 0: info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) infoSizer.Add(item = info, proportion = 0, flag = wx.TOP | wx.ALIGN_CENTER, border = 5) i += 1 # # create pages # copyrightwin = self.PageCopyright() licensewin = self.PageLicense() authorwin = self.PageCredit() contribwin = self.PageContributors() transwin = self.PageTranslators() # create a flat notebook for displaying information about GRASS nbstyle = FN.FNB_VC8 | \ FN.FNB_BACKGROUND_GRADIENT | \ FN.FNB_TABS_BORDER_SIMPLE | \ FN.FNB_NO_X_BUTTON aboutNotebook = FN.FlatNotebook(panel, id=wx.ID_ANY, style=nbstyle) aboutNotebook.SetTabAreaColour(globalvar.FNPageColor) # make pages for About GRASS notebook pg1 = aboutNotebook.AddPage(infoTxt, text=_("Info")) pg2 = aboutNotebook.AddPage(copyrightwin, text=_("Copyright")) pg3 = aboutNotebook.AddPage(licensewin, text=_("License")) pg4 = aboutNotebook.AddPage(authorwin, text=_("Authors")) pg5 = aboutNotebook.AddPage(contribwin, text=_("Contributors")) pg5 = aboutNotebook.AddPage(transwin, text=_("Translators")) wx.CallAfter(aboutNotebook.SetSelection, 0) # buttons btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE) btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add(item = btnClose, proportion = 0, flag = wx.ALL | wx.ALIGN_RIGHT, border = 5) # bindings # self.aboutNotebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnAGPageChanged) btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow) infoTxt.SetSizer(infoSizer) infoSizer.Fit(infoTxt) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(item=aboutNotebook, proportion=1, flag=wx.EXPAND | wx.ALL, border=1) sizer.Add(item=btnSizer, proportion=0, flag=wx.ALL | wx.ALIGN_RIGHT, border=1) panel.SetSizer(sizer) self.Layout() def PageCopyright(self): """Copyright information""" copyfile = os.path.join(os.getenv("GISBASE"), "COPYING") if os.path.exists(copyfile): copyrightFile = open(copyfile, 'r') copytext = copyrightFile.read() copyrightFile.close() else: copytext = _('%s file missing') % 'COPYING' # put text into a scrolling panel copyrightwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY, size=wx.DefaultSize, style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER) copyrighttxt = wx.StaticText(copyrightwin, id=wx.ID_ANY, label=copytext) copyrightwin.SetAutoLayout(True) copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL) copyrightwin.sizer.Add(item=copyrighttxt, proportion=1, flag=wx.EXPAND | wx.ALL, border=3) copyrightwin.SetSizer(copyrightwin.sizer) copyrightwin.Layout() copyrightwin.SetupScrolling() return copyrightwin def PageLicense(self): """Licence about""" licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT") if os.path.exists(licfile): licenceFile = open(licfile, 'r') license = ''.join(licenceFile.readlines()) licenceFile.close() else: license = _('%s file missing') % 'GPL.TXT' # put text into a scrolling panel licensewin = scrolled.ScrolledPanel(self, id=wx.ID_ANY, style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER) licensetxt = wx.StaticText(licensewin, id=wx.ID_ANY, label=license) licensewin.SetAutoLayout(True) licensewin.sizer = wx.BoxSizer(wx.VERTICAL) licensewin.sizer.Add(item=licensetxt, proportion=1, flag=wx.EXPAND | wx.ALL, border=3) licensewin.SetSizer(licensewin.sizer) licensewin.Layout() licensewin.SetupScrolling() return licensewin def PageCredit(self): """Credit about""" # credits authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS") if os.path.exists(authfile): authorsFile = open(authfile, 'r') authors = unicode(''.join(authorsFile.readlines()), "utf-8") authorsFile.close() else: authors = _('%s file missing') % 'AUTHORS' authorwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY, style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) authortxt = wx.StaticText(authorwin, id=wx.ID_ANY, label=authors) authorwin.SetAutoLayout(1) authorwin.SetupScrolling() authorwin.sizer = wx.BoxSizer(wx.VERTICAL) authorwin.sizer.Add(item=authortxt, proportion=1, flag=wx.EXPAND | wx.ALL, border=3) authorwin.SetSizer(authorwin.sizer) authorwin.Layout() return authorwin def PageContributors(self): """Contributors info""" contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv") if os.path.exists(contribfile): contribFile = open(contribfile, 'r') contribs = list() for line in contribFile.readlines(): cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',') contribs.append((name, email, country, osgeo_id)) contribs[0] = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID')) contribFile.close() else: contribs = None contribwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY, style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) contribwin.SetAutoLayout(1) contribwin.SetupScrolling() contribwin.sizer = wx.BoxSizer(wx.VERTICAL) if not contribs: contribtxt = wx.StaticText(contribwin, id=wx.ID_ANY, label=_('%s file missing') % 'contibutors.csv') contribwin.sizer.Add(item=contribtxt, proportion=1, flag=wx.EXPAND | wx.ALL, border=3) else: contribBox = wx.FlexGridSizer(cols=4, vgap=5, hgap=5) for developer in contribs: for item in developer: contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY, label = item)) contribwin.sizer.Add(item=contribBox, proportion=1, flag=wx.EXPAND | wx.ALL, border=3) contribwin.SetSizer(contribwin.sizer) contribwin.Layout() return contribwin def PageTranslators(self): """Translators info""" translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv") if os.path.exists(translatorsfile): translatorsFile = open(translatorsfile, 'r') translators = dict() for line in translatorsFile.readlines()[1:]: name, email, languages = line.rstrip('\n').split(',') for language in languages.split(' '): if not translators.has_key(language): translators[language] = list() translators[language].append((name, email)) translatorsFile.close() else: translators = None translatorswin = scrolled.ScrolledPanel(self, id=wx.ID_ANY, style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) translatorswin.SetAutoLayout(1) translatorswin.SetupScrolling() translatorswin.sizer = wx.BoxSizer(wx.VERTICAL) if not translators: translatorstxt = wx.StaticText(translatorswin, id=wx.ID_ANY, label=_('%s file missing') % 'translators.csv') translatorswin.sizer.Add(item=translatorstxt, proportion=1, flag=wx.EXPAND | wx.ALL, border=3) else: translatorsBox = wx.FlexGridSizer(cols=3, vgap=5, hgap=5) languages = translators.keys() languages.sort() translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, label = _('Name'))) translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, label = _('E-mail'))) translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, label = _('Language'))) for lang in languages: for translator in translators[lang]: name, email = translator translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, label = unicode(name, "utf-8"))) translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, label = email)) translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, label = lang)) translatorswin.sizer.Add(item=translatorsBox, proportion=1, flag=wx.EXPAND | wx.ALL, border=3) translatorswin.SetSizer(translatorswin.sizer) translatorswin.Layout() return translatorswin def OnCloseWindow(self, event): """!Close window""" self.Close()