GRASS Programmer's Manual  6.5.svn(2012)-r51648
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
psmap/frame.py
Go to the documentation of this file.
00001 """!
00002 @package psmap.frame
00003 
00004 @brief GUI for ps.map
00005 
00006 Classes:
00007  - frame::PsMapFrame
00008  - frame::PsMapBufferedWindow
00009 
00010 (C) 2011-2012 by Anna Kratochvilova, and the GRASS Development Team
00011 This program is free software under the GNU General Public License
00012 (>=v2). Read the file COPYING that comes with GRASS for details.
00013 
00014 @author Anna Kratochvilova <kratochanna gmail.com> (bachelor's project)
00015 @author Martin Landa <landa.martin gmail.com> (mentor)
00016 """
00017 
00018 import os
00019 import sys
00020 import textwrap
00021 import Queue
00022 from math import sin, cos, pi, sqrt
00023 
00024 if __name__ == "__main__":
00025     sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython'))
00026 from core             import globalvar
00027 import wx
00028 
00029 try:
00030     import wx.lib.agw.flatnotebook as fnb
00031 except ImportError:
00032     import wx.lib.flatnotebook as fnb
00033 
00034 import grass.script as grass
00035 
00036 from gui_core.menu      import Menu
00037 from gui_core.goutput   import CmdThread, EVT_CMD_DONE
00038 from psmap.toolbars     import PsMapToolbar
00039 from core.gcmd          import RunCommand, GError, GMessage
00040 from core.settings      import UserSettings
00041 from gui_core.forms     import GUI
00042 from psmap.menudata     import PsMapData
00043 
00044 from psmap.dialogs      import *
00045 from psmap.instructions import *
00046 from psmap.utils        import *
00047 
00048 class PsMapFrame(wx.Frame):
00049     def __init__(self, parent = None, id = wx.ID_ANY,
00050                  title = _("GRASS GIS Cartographic Composer (experimental prototype)"), **kwargs):
00051         """!Main window of ps.map GUI
00052         
00053         @param parent parent window
00054         @param id window id
00055         @param title window title
00056         
00057         @param kwargs wx.Frames' arguments
00058         """
00059         self.parent = parent
00060 
00061         wx.Frame.__init__(self, parent = parent, id = id, title = title, name = "PsMap", **kwargs)
00062         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
00063         #menubar
00064         self.menubar = Menu(parent = self, data = PsMapData())
00065         self.SetMenuBar(self.menubar)
00066         #toolbar
00067 
00068         self.toolbar = PsMapToolbar(parent = self)
00069         self.SetToolBar(self.toolbar)
00070         
00071         self.actionOld = self.toolbar.action['id']
00072         self.iconsize = (16, 16)
00073         #satusbar
00074         self.statusbar = self.CreateStatusBar(number = 1)
00075         
00076         # mouse attributes -- position on the screen, begin and end of
00077         # dragging, and type of drawing
00078         self.mouse = {
00079             'begin': [0, 0], # screen coordinates
00080             'end'  : [0, 0],
00081             'use'  : "pointer",
00082             }
00083         # available cursors
00084         self.cursors = {
00085             "default" : wx.StockCursor(wx.CURSOR_ARROW),
00086             "cross"   : wx.StockCursor(wx.CURSOR_CROSS),
00087             "hand"    : wx.StockCursor(wx.CURSOR_HAND),
00088             "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
00089             }
00090         # pen and brush
00091         self.pen = {
00092             'paper': wx.Pen(colour = "BLACK", width = 1),
00093             'margins': wx.Pen(colour = "GREY", width = 1),
00094             'map': wx.Pen(colour = wx.Color(86, 122, 17), width = 2),
00095             'rasterLegend': wx.Pen(colour = wx.Color(219, 216, 4), width = 2),
00096             'vectorLegend': wx.Pen(colour = wx.Color(219, 216, 4), width = 2),
00097             'mapinfo': wx.Pen(colour = wx.Color(5, 184, 249), width = 2),
00098             'scalebar': wx.Pen(colour = wx.Color(150, 150, 150), width = 2),
00099             'image': wx.Pen(colour = wx.Color(255, 150, 50), width = 2),
00100             'northArrow': wx.Pen(colour = wx.Color(200, 200, 200), width = 2),
00101             'point': wx.Pen(colour = wx.Color(100, 100, 100), width = 2),
00102             'line': wx.Pen(colour = wx.Color(0, 0, 0), width = 2),
00103             'box': wx.Pen(colour = 'RED', width = 2, style = wx.SHORT_DASH),
00104             'select': wx.Pen(colour = 'BLACK', width = 1, style = wx.SHORT_DASH),
00105             'resize': wx.Pen(colour = 'BLACK', width = 1)
00106             }
00107         self.brush = {
00108             'paper': wx.WHITE_BRUSH,
00109             'margins': wx.TRANSPARENT_BRUSH,
00110             'map': wx.Brush(wx.Color(151, 214, 90)),
00111             'rasterLegend': wx.Brush(wx.Color(250, 247, 112)),
00112             'vectorLegend': wx.Brush(wx.Color(250, 247, 112)),
00113             'mapinfo': wx.Brush(wx.Color(127, 222, 252)),
00114             'scalebar': wx.Brush(wx.Color(200, 200, 200)),
00115             'image': wx.Brush(wx.Color(255, 200, 50)),
00116             'northArrow': wx.Brush(wx.Color(255, 255, 255)),
00117             'point': wx.Brush(wx.Color(200, 200, 200)),
00118             'line': wx.TRANSPARENT_BRUSH,
00119             'box': wx.TRANSPARENT_BRUSH,
00120             'select':wx.TRANSPARENT_BRUSH,
00121             'resize': wx.BLACK_BRUSH
00122             } 
00123         
00124 
00125         # list of objects to draw
00126         self.objectId = []
00127         
00128         # instructions
00129         self.instruction = Instruction(parent = self, objectsToDraw = self.objectId)
00130         # open dialogs
00131         self.openDialogs = dict()
00132         
00133         self.pageId = wx.NewId()
00134         #current page of flatnotebook
00135         self.currentPage = 0
00136         #canvas for draft mode
00137         self.canvas = PsMapBufferedWindow(parent = self, mouse = self.mouse, pen = self.pen,
00138                                           brush = self.brush, cursors = self.cursors, 
00139                                           instruction = self.instruction, openDialogs = self.openDialogs,
00140                                           pageId = self.pageId, objectId = self.objectId,
00141                                           preview = False)
00142         
00143         self.canvas.SetCursor(self.cursors["default"])
00144         self.getInitMap()
00145         
00146         
00147         # image path
00148         env = grass.gisenv()
00149         self.imgName = grass.tempfile()
00150         
00151         #canvas for preview
00152         self.previewCanvas = PsMapBufferedWindow(parent = self, mouse = self.mouse, cursors = self.cursors,
00153                                                  pen = self.pen, brush = self.brush, preview = True)
00154         
00155         # set WIND_OVERRIDE
00156         grass.use_temp_region()
00157         
00158         # create queues
00159         self.requestQ = Queue.Queue()
00160         self.resultQ = Queue.Queue()
00161         # thread
00162         self.cmdThread = CmdThread(self, self.requestQ, self.resultQ)
00163         
00164         self._layout()
00165         self.SetMinSize(wx.Size(750, 600))
00166         
00167         self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGING, self.OnPageChanging)
00168         self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
00169         self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
00170         self.Bind(EVT_CMD_DONE, self.OnCmdDone)
00171         
00172         if not havePILImage:
00173             wx.CallAfter(self._showErrMsg)
00174         
00175     def _showErrMsg(self):
00176         """!Show error message (missing preview)
00177         """
00178         GError(parent = self,
00179                message = _("Python Imaging Library is not available.\n"
00180                            "'Preview' functionality won't work."),
00181                showTraceback = False)
00182         
00183     def _layout(self):
00184         """!Do layout
00185         """
00186         mainSizer = wx.BoxSizer(wx.VERTICAL)
00187         if globalvar.hasAgw:
00188             self.book = fnb.FlatNotebook(parent = self, id = wx.ID_ANY,
00189                                          agwStyle = fnb.FNB_FANCY_TABS | fnb.FNB_BOTTOM |
00190                                          fnb.FNB_NO_NAV_BUTTONS | fnb.FNB_NO_X_BUTTON)
00191         else:
00192             self.book = fnb.FlatNotebook(parent = self, id = wx.ID_ANY,
00193                                          style = fnb.FNB_FANCY_TABS | fnb.FNB_BOTTOM |
00194                                          fnb.FNB_NO_NAV_BUTTONS | fnb.FNB_NO_X_BUTTON)
00195         #self.book = fnb.FlatNotebook(self, wx.ID_ANY, style = fnb.FNB_BOTTOM)
00196         self.book.AddPage(self.canvas, "Draft mode")
00197         self.book.AddPage(self.previewCanvas, "Preview")
00198         self.book.SetSelection(0)
00199         
00200         mainSizer.Add(self.book,1, wx.EXPAND)
00201         
00202         self.SetSizer(mainSizer)
00203         mainSizer.Fit(self)
00204 
00205         
00206     def InstructionFile(self):
00207         """!Creates mapping instructions"""
00208         
00209         return str(self.instruction)
00210 
00211     def OnPSFile(self, event):
00212         """!Generate PostScript"""
00213         filename = self.getFile(wildcard = "PostScript (*.ps)|*.ps|Encapsulated PostScript (*.eps)|*.eps")
00214         if filename:
00215             self.PSFile(filename)
00216     
00217     def OnPsMapDialog(self, event):
00218         """!Launch ps.map dialog
00219         """
00220         GUI(parent = self).ParseCommand(cmd = ['ps.map'])
00221 
00222     def OnPDFFile(self, event):
00223         """!Generate PDF from PS with ps2pdf if available"""
00224         try:
00225             p = grass.Popen(["ps2pdf"], stderr = grass.PIPE)
00226             p.stderr.close()
00227         
00228         except OSError:
00229             GMessage(parent = self,
00230                      message = _("Program ps2pdf is not available. Please install it first to create PDF."))
00231             return
00232         
00233         filename = self.getFile(wildcard = "PDF (*.pdf)|*.pdf")
00234         if filename:  
00235             self.PSFile(filename, pdf = True)   
00236             
00237     def OnPreview(self, event):
00238         """!Run ps.map and show result"""
00239         self.PSFile()
00240         
00241     def PSFile(self, filename = None, pdf = False):
00242         """!Create temporary instructions file and run ps.map with output = filename"""
00243         instrFile = grass.tempfile()
00244         instrFileFd = open(instrFile, mode = 'w')
00245         instrFileFd.write(self.InstructionFile())
00246         instrFileFd.flush()
00247         instrFileFd.close()
00248         
00249         temp = False
00250         regOld = grass.region()
00251         
00252         if pdf:
00253             pdfname = filename
00254         else:
00255             pdfname = None
00256         #preview or pdf
00257         if not filename or (filename and pdf):
00258             temp = True
00259             filename = grass.tempfile()
00260             if not pdf: # lower resolution for preview
00261                 if self.instruction.FindInstructionByType('map'):
00262                     mapId = self.instruction.FindInstructionByType('map').id
00263                     SetResolution(dpi = 100, width = self.instruction[mapId]['rect'][2],
00264                                   height = self.instruction[mapId]['rect'][3])
00265         
00266         cmd = ['ps.map', '--overwrite']
00267         if os.path.splitext(filename)[1] == '.eps':
00268             cmd.append('-e')
00269         if self.instruction[self.pageId]['Orientation'] == 'Landscape':
00270             cmd.append('-r')
00271         cmd.append('input=%s' % instrFile)
00272         cmd.append('output=%s' % filename)
00273         if pdf:
00274             self.SetStatusText(_('Generating PDF...'), 0)
00275         elif not temp:
00276             self.SetStatusText(_('Generating PostScript...'), 0)
00277         else:
00278             self.SetStatusText(_('Generating preview...'), 0)
00279             
00280         self.cmdThread.RunCmd(cmd, userData = {'instrFile' : instrFile, 'filename' : filename,
00281                                                'pdfname' : pdfname, 'temp' : temp, 'regionOld' : regOld})
00282         
00283     def OnCmdDone(self, event):
00284         """!ps.map process finished"""
00285         
00286         if event.returncode != 0:
00287             GMessage(parent = self,
00288                      message = _("Ps.map exited with return code %s") % event.returncode)
00289             
00290             grass.try_remove(event.userData['instrFile'])
00291             if event.userData['temp']:
00292                 grass.try_remove(event.userData['filename']) 
00293             return
00294         
00295         if event.userData['pdfname']:
00296             try:
00297                 proc = grass.Popen(['ps2pdf', '-dPDFSETTINGS=/prepress', '-r1200', 
00298                                     event.userData['filename'], event.userData['pdfname']])
00299                 
00300                 ret = proc.wait()                        
00301                 if ret > 0:
00302                     GMessage(parent = self,
00303                              message = _("ps2pdf exited with return code %s") % ret)
00304 
00305             except OSError, e:
00306                 GError(parent = self,
00307                        message = _("Program ps2pdf is not available. Please install it to create PDF.\n\n %s") % e)
00308                 
00309         # show preview only when user doesn't want to create ps or pdf 
00310         if havePILImage and event.userData['temp'] and not event.userData['pdfname']:
00311             RunCommand('g.region', cols = event.userData['regionOld']['cols'], rows = event.userData['regionOld']['rows'])
00312 ## wx.BusyInfo does not display the message
00313 ##            busy = wx.BusyInfo(message = "Generating preview, wait please", parent = self)
00314 
00315             try:
00316                 im = PILImage.open(event.userData['filename'])
00317                 if self.instruction[self.pageId]['Orientation'] == 'Landscape':
00318                     im = im.rotate(270)
00319                 
00320                 im.save(self.imgName, format = 'PNG')
00321                 
00322             except IOError, e:
00323                 GError(parent = self,
00324                        message = _("Unable to generate preview. %s") % e)
00325                 return
00326             
00327                 
00328             rect = self.previewCanvas.ImageRect()
00329             self.previewCanvas.image = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG)
00330             self.previewCanvas.DrawImage(rect = rect)
00331             
00332 ##            busy.Destroy()
00333             self.SetStatusText(_('Preview generated'), 0)
00334             self.book.SetSelection(1)
00335             self.currentPage = 1
00336         
00337         grass.try_remove(event.userData['instrFile'])
00338         if event.userData['temp']:
00339             grass.try_remove(event.userData['filename'])
00340         
00341     def getFile(self, wildcard):
00342         suffix = []
00343         for filter in wildcard.split('|')[1::2]:
00344             s = filter.strip('*').split('.')[1]
00345             if s:
00346                 s = '.' + s
00347             suffix.append(s)
00348         raster = self.instruction.FindInstructionByType('raster')
00349         if raster:
00350             rasterId = raster.id 
00351         else:
00352             rasterId = None
00353 
00354 
00355         if rasterId and self.instruction[rasterId]['raster']:
00356             mapName = self.instruction[rasterId]['raster'].split('@')[0] + suffix[0]
00357         else:
00358             mapName = ''
00359             
00360         filename = ''
00361         dlg = wx.FileDialog(self, message = _("Save file as"), defaultDir = "", 
00362                             defaultFile = mapName, wildcard = wildcard,
00363                             style = wx.CHANGE_DIR | wx.SAVE | wx.OVERWRITE_PROMPT)
00364         if dlg.ShowModal() == wx.ID_OK:
00365             filename = dlg.GetPath()
00366             suffix = suffix[dlg.GetFilterIndex()]
00367             if not os.path.splitext(filename)[1]:
00368                 filename = filename + suffix
00369             elif os.path.splitext(filename)[1] != suffix and suffix != '':
00370                 filename = os.path.splitext(filename)[0] + suffix
00371             
00372         dlg.Destroy()
00373         return filename
00374     
00375     def OnInstructionFile(self, event):
00376         filename = self.getFile(wildcard = "*.psmap|*.psmap|Text file(*.txt)|*.txt|All files(*.*)|*.*")        
00377         if filename:    
00378             instrFile = open(filename, "w")
00379             instrFile.write(self.InstructionFile())
00380             instrFile.close()   
00381             
00382     def OnLoadFile(self, event):
00383         """!Load file and read instructions"""
00384         #find file
00385         filename = ''
00386         dlg = wx.FileDialog(self, message = "Find instructions file", defaultDir = "", 
00387                             defaultFile = '', wildcard = "All files (*.*)|*.*",
00388                             style = wx.CHANGE_DIR|wx.OPEN)
00389         if dlg.ShowModal() == wx.ID_OK:
00390             filename = dlg.GetPath()
00391         dlg.Destroy()
00392         if not filename:
00393             return
00394         # load instructions
00395         readObjectId = []
00396         readInstruction = Instruction(parent = self, objectsToDraw = readObjectId)
00397         ok = readInstruction.Read(filename)
00398         if not ok:
00399             GMessage(_("Failed to read file %s.") % filename)
00400         else:
00401             self.instruction = self.canvas.instruction = readInstruction
00402             self.objectId = self.canvas.objectId = readObjectId
00403             self.pageId = self.canvas.pageId = self.instruction.FindInstructionByType('page').id
00404             self.canvas.UpdateMapLabel()
00405             self.canvas.dragId = -1
00406             self.canvas.Clear()
00407             self.canvas.SetPage()
00408             #self.canvas.ZoomAll()
00409             
00410             self.DialogDataChanged(self.objectId)
00411             
00412     def OnPageSetup(self, event = None):
00413         """!Specify paper size, margins and orientation"""
00414         id = self.instruction.FindInstructionByType('page').id
00415         dlg = PageSetupDialog(self, id = id, settings = self.instruction) 
00416         dlg.CenterOnScreen()
00417         val = dlg.ShowModal()
00418         if val == wx.ID_OK:
00419             self.canvas.SetPage()
00420             self.getInitMap()
00421             self.canvas.RecalculatePosition(ids = self.objectId)
00422         dlg.Destroy()
00423         
00424     def OnPointer(self, event):
00425         self.toolbar.OnTool(event)
00426         self.mouse["use"] = "pointer"
00427         self.canvas.SetCursor(self.cursors["default"])
00428         self.previewCanvas.SetCursor(self.cursors["default"])
00429         
00430     def OnPan(self, event):
00431         self.toolbar.OnTool(event)
00432         self.mouse["use"] = "pan"
00433         self.canvas.SetCursor(self.cursors["hand"])
00434         self.previewCanvas.SetCursor(self.cursors["hand"])
00435         
00436     def OnZoomIn(self, event):
00437         self.toolbar.OnTool(event)
00438         self.mouse["use"] = "zoomin"
00439         self.canvas.SetCursor(self.cursors["cross"])
00440         self.previewCanvas.SetCursor(self.cursors["cross"])
00441         
00442     def OnZoomOut(self, event):
00443         self.toolbar.OnTool(event)
00444         self.mouse["use"] = "zoomout"
00445         self.canvas.SetCursor(self.cursors["cross"])
00446         self.previewCanvas.SetCursor(self.cursors["cross"])
00447         
00448     def OnZoomAll(self, event):
00449         self.mouseOld = self.mouse['use']
00450         if self.currentPage == 0:
00451             self.cursorOld = self.canvas.GetCursor() 
00452         else:
00453             self.cursorOld = self.previewCanvas.GetCursor()
00454             self.previewCanvas.GetCursor()
00455         self.mouse["use"] = "zoomin"
00456         if self.currentPage == 0:
00457             self.canvas.ZoomAll()
00458         else:
00459             self.previewCanvas.ZoomAll()
00460         self.mouse["use"] = self.mouseOld 
00461         if self.currentPage == 0:
00462             self.canvas.SetCursor(self.cursorOld)
00463         else:
00464             self.previewCanvas.SetCursor(self.cursorOld)
00465         
00466         
00467     def OnAddMap(self, event, notebook = False):
00468         """!Add or edit map frame"""
00469         if event is not None:
00470             if event.GetId() != self.toolbar.action['id']:
00471                 self.actionOld = self.toolbar.action['id']
00472                 self.mouseOld = self.mouse['use']
00473                 self.cursorOld = self.canvas.GetCursor()
00474             self.toolbar.OnTool(event)
00475         
00476         if self.instruction.FindInstructionByType('map'):
00477             mapId = self.instruction.FindInstructionByType('map').id
00478         else: mapId = None
00479         id = [mapId, None, None]
00480         
00481         if notebook:
00482             if self.instruction.FindInstructionByType('vector'):
00483                 vectorId = self.instruction.FindInstructionByType('vector').id
00484             else: vectorId = None
00485             if self.instruction.FindInstructionByType('raster'):
00486                 rasterId = self.instruction.FindInstructionByType('raster').id
00487             else: rasterId = None
00488             id[1] = rasterId
00489             id[2] = vectorId
00490         
00491         
00492         if mapId: # map exists
00493             
00494             self.toolbar.ToggleTool(self.actionOld, True)
00495             self.toolbar.ToggleTool(self.toolbar.action['id'], False)
00496             self.toolbar.action['id'] = self.actionOld
00497             try:
00498                 self.canvas.SetCursor(self.cursorOld) 
00499             except AttributeError:
00500                 pass
00501             
00502 ##            dlg = MapDialog(parent = self, id  = id, settings = self.instruction,
00503 ##                            notebook = notebook)
00504 ##            dlg.ShowModal()  
00505             if notebook:
00506                 #check map, raster, vector and save, destroy them
00507                 if 'map' in self.openDialogs:
00508                     self.openDialogs['map'].OnOK(event = None)
00509                 if 'raster' in self.openDialogs:
00510                     self.openDialogs['raster'].OnOK(event = None)
00511                 if 'vector' in self.openDialogs:
00512                     self.openDialogs['vector'].OnOK(event = None)
00513                     
00514                 if 'mapNotebook' not in self.openDialogs:
00515                     dlg = MapDialog(parent = self, id  = id, settings = self.instruction,
00516                                     notebook = notebook)
00517                     self.openDialogs['mapNotebook'] = dlg
00518                 self.openDialogs['mapNotebook'].Show()
00519             else:
00520                 if 'mapNotebook' in self.openDialogs:
00521                     self.openDialogs['mapNotebook'].notebook.ChangeSelection(0)
00522                 else:
00523                     if 'map' not in self.openDialogs:
00524                         dlg = MapDialog(parent = self, id  = id, settings = self.instruction,
00525                                         notebook = notebook)
00526                         self.openDialogs['map'] = dlg
00527                     self.openDialogs['map'].Show()
00528                     
00529 
00530         else:    # sofar no map
00531             self.mouse["use"] = "addMap"
00532             self.canvas.SetCursor(self.cursors["cross"])
00533             if self.currentPage == 1:
00534                 self.book.SetSelection(0)
00535                 self.currentPage = 0
00536                 
00537     def OnAddRaster(self, event):
00538         """!Add raster map"""
00539         if self.instruction.FindInstructionByType('raster'):
00540             id = self.instruction.FindInstructionByType('raster').id
00541         else: id = None
00542         if self.instruction.FindInstructionByType('map'):
00543             mapId = self.instruction.FindInstructionByType('map').id
00544         else: mapId = None
00545 
00546         if not id:
00547             if not mapId:
00548                 GMessage(message = _("Please, create map frame first."))
00549                 return
00550             
00551 ##        dlg = RasterDialog(self, id = id, settings = self.instruction)
00552 ##        dlg.ShowModal()
00553         if 'mapNotebook' in self.openDialogs:
00554             self.openDialogs['mapNotebook'].notebook.ChangeSelection(1)
00555         else:
00556             if 'raster' not in self.openDialogs:
00557                 dlg = RasterDialog(self, id = id, settings = self.instruction)
00558                 self.openDialogs['raster'] = dlg
00559             self.openDialogs['raster'].Show()
00560             
00561     def OnAddVect(self, event):
00562         """!Add vector map"""
00563         if self.instruction.FindInstructionByType('vector'):
00564             id = self.instruction.FindInstructionByType('vector').id
00565         else: id = None
00566         if self.instruction.FindInstructionByType('map'):
00567             mapId = self.instruction.FindInstructionByType('map').id
00568         else: mapId = None
00569         if not id:
00570             if not mapId:
00571                 GMessage(message = _("Please, create map frame first."))
00572                 return
00573             
00574 ##        dlg = MainVectorDialog(self, id = id, settings = self.instruction)
00575 ##        dlg.ShowModal()
00576         if 'mapNotebook' in self.openDialogs:
00577             self.openDialogs['mapNotebook'].notebook.ChangeSelection(2)
00578         else:
00579             if 'vector' not in self.openDialogs:
00580                 dlg =  MainVectorDialog(self, id = id, settings = self.instruction)
00581                 self.openDialogs['vector'] = dlg
00582             self.openDialogs['vector'].Show()
00583        
00584     def OnAddScalebar(self, event):
00585         """!Add scalebar"""
00586         if projInfo()['proj'] == 'll':
00587             GMessage(message = _("Scalebar is not appropriate for this projection"))
00588             return
00589         if self.instruction.FindInstructionByType('scalebar'):
00590             id = self.instruction.FindInstructionByType('scalebar').id
00591         else: id = None
00592         
00593         if 'scalebar' not in self.openDialogs:
00594             dlg = ScalebarDialog(self, id = id, settings = self.instruction)
00595             self.openDialogs['scalebar'] = dlg
00596         self.openDialogs['scalebar'].Show()
00597         
00598     def OnAddLegend(self, event, page = 0):
00599         """!Add raster or vector legend"""
00600         if self.instruction.FindInstructionByType('rasterLegend'):
00601             idR = self.instruction.FindInstructionByType('rasterLegend').id
00602         else: idR = None
00603         if self.instruction.FindInstructionByType('vectorLegend'):
00604             idV = self.instruction.FindInstructionByType('vectorLegend').id
00605         else: idV = None
00606 
00607         if 'rasterLegend' not in self.openDialogs:    
00608             dlg = LegendDialog(self, id = [idR, idV], settings = self.instruction, page = page)
00609             self.openDialogs['rasterLegend'] = dlg
00610             self.openDialogs['vectorLegend'] = dlg
00611         self.openDialogs['rasterLegend'].notebook.ChangeSelection(page)
00612         self.openDialogs['rasterLegend'].Show()
00613 
00614     def OnAddMapinfo(self, event):
00615         if self.instruction.FindInstructionByType('mapinfo'):
00616             id = self.instruction.FindInstructionByType('mapinfo').id
00617         else: id = None
00618         
00619         if 'mapinfo' not in self.openDialogs:
00620             dlg = MapinfoDialog(self, id = id, settings = self.instruction)
00621             self.openDialogs['mapinfo'] = dlg
00622         self.openDialogs['mapinfo'].Show()
00623         
00624     def OnAddImage(self, event, id = None):
00625         """!Show dialog for image adding and editing"""
00626         position = None
00627         if 'image' in self.openDialogs:
00628             position = self.openDialogs['image'].GetPosition()
00629             self.openDialogs['image'].OnApply(event = None)
00630             self.openDialogs['image'].Destroy()
00631         dlg = ImageDialog(self, id = id, settings = self.instruction)
00632         self.openDialogs['image'] = dlg 
00633         if position: 
00634             dlg.SetPosition(position)
00635         dlg.Show()
00636         
00637     def OnAddNorthArrow(self, event, id = None):
00638         """!Show dialog for north arrow adding and editing"""
00639         if self.instruction.FindInstructionByType('northArrow'):
00640             id = self.instruction.FindInstructionByType('northArrow').id
00641         else: id = None
00642         
00643         if 'northArrow' not in self.openDialogs:
00644             dlg = NorthArrowDialog(self, id = id, settings = self.instruction)
00645             self.openDialogs['northArrow'] = dlg
00646         self.openDialogs['northArrow'].Show()
00647         
00648     def OnAddText(self, event, id = None):
00649         """!Show dialog for text adding and editing"""
00650         position = None
00651         if 'text' in self.openDialogs:
00652             position = self.openDialogs['text'].GetPosition()
00653             self.openDialogs['text'].OnApply(event = None)
00654             self.openDialogs['text'].Destroy()
00655         dlg = TextDialog(self, id = id, settings = self.instruction)
00656         self.openDialogs['text'] = dlg 
00657         if position: 
00658             dlg.SetPosition(position)
00659         dlg.Show()
00660         
00661     def OnAddPoint(self, event):
00662         """!Add point action selected"""
00663         self.mouse["use"] = "addPoint"
00664         self.canvas.SetCursor(self.cursors["cross"])
00665         
00666     def AddPoint(self, id = None, coordinates = None):
00667         """!Add point and open property dialog.
00668 
00669         @param id id point id (None if creating new point)
00670         @param coordinates coordinates of new point
00671         """
00672         position = None
00673         if 'point' in self.openDialogs:
00674             position = self.openDialogs['point'].GetPosition()
00675             self.openDialogs['point'].OnApply(event = None)
00676             self.openDialogs['point'].Destroy()
00677         dlg = PointDialog(self, id = id, settings = self.instruction,
00678                           coordinates = coordinates)
00679         self.openDialogs['point'] = dlg
00680         if position: 
00681             dlg.SetPosition(position)
00682         if coordinates:
00683             dlg.OnApply(event = None)
00684         dlg.Show()
00685         
00686     def OnAddLine(self, event):
00687         """!Add line action selected"""
00688         self.mouse["use"] = "addLine"
00689         self.canvas.SetCursor(self.cursors["cross"])
00690 
00691     def AddLine(self, id = None, coordinates = None):
00692         """!Add line and open property dialog.
00693         
00694         @param id id line id (None if creating new line)
00695         @param coordinates coordinates of new line
00696         """
00697         position = None
00698         if 'line' in self.openDialogs:
00699             position = self.openDialogs['line'].GetPosition()
00700             self.openDialogs['line'].OnApply(event = None)
00701             self.openDialogs['line'].Destroy()
00702         dlg = RectangleDialog(self, id = id, settings = self.instruction,
00703                               type = 'line', coordinates = coordinates)
00704         self.openDialogs['line'] = dlg
00705         if position: 
00706             dlg.SetPosition(position)
00707         if coordinates:
00708             dlg.OnApply(event = None)
00709         dlg.Show()
00710 
00711     def OnAddRectangle(self, event):
00712         """!Add rectangle action selected"""
00713         self.mouse["use"] = "addRectangle"
00714         self.canvas.SetCursor(self.cursors["cross"])
00715 
00716     def AddRectangle(self, id = None, coordinates = None):
00717         """!Add rectangle and open property dialog.
00718         
00719         @param id id rectangle id (None if creating new rectangle)
00720         @param coordinates coordinates of new rectangle
00721         """
00722         position = None
00723         if 'rectangle' in self.openDialogs:
00724             position = self.openDialogs['rectangle'].GetPosition()
00725             self.openDialogs['rectangle'].OnApply(event = None)
00726             self.openDialogs['rectangle'].Destroy()
00727         dlg = RectangleDialog(self, id = id, settings = self.instruction,
00728                               type = 'rectangle', coordinates = coordinates)
00729         self.openDialogs['rectangle'] = dlg
00730         if position: 
00731             dlg.SetPosition(position)
00732         if coordinates:
00733             dlg.OnApply(event = None)
00734         dlg.Show()
00735 
00736     def getModifiedTextBounds(self, x, y, textExtent, rotation):
00737         """!computes bounding box of rotated text, not very precisely"""
00738         w, h = textExtent
00739         rotation = float(rotation)/180*pi
00740         H = float(w) * sin(rotation)
00741         W = float(w) * cos(rotation)
00742         X, Y = x, y
00743         if pi/2 < rotation <= 3*pi/2:
00744             X = x + W 
00745         if 0 < rotation < pi:
00746             Y = y - H
00747         if rotation == 0:
00748             return wx.Rect(x,y, *textExtent)
00749         else:
00750             return wx.Rect(X, Y, abs(W), abs(H)).Inflate(h,h) 
00751 
00752     def makePSFont(self, textDict):
00753         """!creates a wx.Font object from selected postscript font. To be
00754         used for estimating bounding rectangle of text"""
00755         
00756         fontsize = textDict['fontsize'] * self.canvas.currScale
00757         fontface = textDict['font'].split('-')[0]
00758         try:
00759             fontstyle = textDict['font'].split('-')[1]
00760         except IndexError:
00761             fontstyle = ''
00762         
00763         if fontface == "Times":
00764             family = wx.FONTFAMILY_ROMAN
00765             face = "times"
00766         elif fontface == "Helvetica":
00767             family = wx.FONTFAMILY_SWISS
00768             face = 'helvetica'
00769         elif fontface == "Courier":
00770             family = wx.FONTFAMILY_TELETYPE
00771             face = 'courier'
00772         else:
00773             family = wx.FONTFAMILY_DEFAULT
00774             face = ''
00775             
00776         style = wx.FONTSTYLE_NORMAL
00777         weight = wx.FONTWEIGHT_NORMAL
00778             
00779         if 'Oblique' in fontstyle:
00780             style =  wx.FONTSTYLE_SLANT
00781             
00782         if 'Italic' in fontstyle:
00783             style =  wx.FONTSTYLE_ITALIC
00784             
00785         if 'Bold' in fontstyle:
00786             weight = wx.FONTWEIGHT_BOLD
00787         
00788         try:
00789             fn = wx.Font(pointSize = fontsize, family = family, style = style,
00790                          weight = weight, face = face)
00791         except:
00792             fn = wx.Font(pointSize = fontsize, family = wx.FONTFAMILY_DEFAULT, 
00793                          style = wx.FONTSTYLE_NORMAL, weight = wx.FONTWEIGHT_NORMAL)
00794 
00795         return fn
00796        
00797        
00798     def getTextExtent(self, textDict):
00799         """!Estimates bounding rectangle of text"""
00800         #fontsize = str(fontsize if fontsize >= 4 else 4)
00801         dc = wx.ClientDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks
00802        
00803         fn = self.makePSFont(textDict)
00804 
00805         try:
00806             dc.SetFont(fn)
00807             w,h,lh = dc.GetMultiLineTextExtent(textDict['text'])
00808             return (w,h)
00809         except:
00810             return (0,0)
00811     
00812     def getInitMap(self):
00813         """!Create default map frame when no map is selected, needed for coordinates in map units"""
00814         instrFile = grass.tempfile()
00815         instrFileFd = open(instrFile, mode = 'w')
00816         instrFileFd.write(self.InstructionFile())
00817         instrFileFd.flush()
00818         instrFileFd.close()
00819         
00820         page = self.instruction.FindInstructionByType('page')
00821         mapInitRect = GetMapBounds(instrFile, portrait = (page['Orientation'] == 'Portrait'))
00822         grass.try_remove(instrFile)
00823         
00824         region = grass.region()
00825         units = UnitConversion(self)
00826         realWidth = units.convert(value = abs(region['w'] - region['e']), fromUnit = 'meter', toUnit = 'inch')
00827         scale = mapInitRect.Get()[2]/realWidth  
00828         
00829         initMap = self.instruction.FindInstructionByType('initMap')
00830         if initMap:
00831             id = initMap.id 
00832         else:
00833             id = None
00834 
00835         
00836         if not id:
00837             id = wx.NewId()
00838             initMap = InitMap(id)
00839             self.instruction.AddInstruction(initMap)
00840         self.instruction[id].SetInstruction(dict(rect = mapInitRect, scale = scale))
00841 
00842     def OnDelete(self, event):
00843         if self.canvas.dragId != -1 and self.currentPage == 0:
00844             if self.instruction[self.canvas.dragId].type == 'map':
00845                 self.deleteObject(self.canvas.dragId)
00846                 self.getInitMap()
00847                 self.canvas.RecalculateEN()
00848             else:
00849                 self.deleteObject(self.canvas.dragId)   
00850     
00851     def deleteObject(self, id):
00852         """!Deletes object, his id and redraws"""
00853         #delete from canvas
00854         self.canvas.pdcObj.RemoveId(id)
00855         if id == self.canvas.dragId:
00856             self.canvas.pdcTmp.RemoveAll()
00857             self.canvas.dragId = -1
00858         self.canvas.Refresh()
00859         
00860         # delete from instructions
00861         del self.instruction[id]
00862 
00863     def DialogDataChanged(self, id):
00864         ids = id
00865         if type(id) == int:
00866             ids = [id]
00867         for id in ids:
00868             itype = self.instruction[id].type
00869             
00870             if itype in ('scalebar', 'mapinfo', 'image'):
00871                 drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
00872                 self.canvas.UpdateLabel(itype = itype, id = id)
00873                 self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
00874                                  pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
00875                 self.canvas.RedrawSelectBox(id)
00876             if itype == 'northArrow':
00877                 self.canvas.UpdateLabel(itype = itype, id = id)
00878                 drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
00879                 self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
00880                                  pdc = self.canvas.pdcObj, drawid = id, pdctype = 'bitmap', bb = drawRectangle)
00881                 self.canvas.RedrawSelectBox(id)
00882 
00883             if itype in ('point', 'line', 'rectangle'):
00884                 drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
00885                 # coords only for line
00886                 coords = None
00887                 if itype == 'line':
00888                     point1 = self.instruction[id]['where'][0]
00889                     point2 = self.instruction[id]['where'][1]
00890                     point1Coords = self.canvas.CanvasPaperCoordinates(rect = Rect2DPS(point1, (0, 0)), canvasToPaper = False)[:2]
00891                     point2Coords = self.canvas.CanvasPaperCoordinates(rect = Rect2DPS(point2, (0, 0)), canvasToPaper = False)[:2]
00892                     coords = (point1Coords, point2Coords)
00893 
00894                 # fill color is not in line
00895                 fcolor = None
00896                 if 'fcolor' in self.instruction[id].GetInstruction():
00897                     fcolor = self.instruction[id]['fcolor']
00898                 # width is not in point
00899                 width = None
00900                 if 'width' in self.instruction[id].GetInstruction():
00901                     width = self.instruction[id]['width']
00902 
00903                 self.canvas.DrawGraphics(drawid = id, color = self.instruction[id]['color'], shape = itype,
00904                                        fcolor = fcolor, width = width, bb = drawRectangle, lineCoords = coords)
00905 
00906                 self.canvas.RedrawSelectBox(id)
00907 
00908             if itype == 'text':
00909                 
00910                 if self.instruction[id]['rotate']:
00911                     rot = float(self.instruction[id]['rotate']) 
00912                 else:
00913                     rot = 0
00914 
00915                 extent = self.getTextExtent(textDict = self.instruction[id].GetInstruction())
00916                 rect = Rect2DPS(self.instruction[id]['where'], (0, 0))
00917                 self.instruction[id]['coords'] = list(self.canvas.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)[:2])
00918                 
00919                 #computes text coordinates according to reference point, not precisely
00920                 if self.instruction[id]['ref'].split()[0] == 'lower':
00921                     self.instruction[id]['coords'][1] -= extent[1]
00922                 elif self.instruction[id]['ref'].split()[0] == 'center':
00923                     self.instruction[id]['coords'][1] -= extent[1]/2
00924                 if self.instruction[id]['ref'].split()[1] == 'right':
00925                     self.instruction[id]['coords'][0] -= extent[0] * cos(rot/180*pi)
00926                     self.instruction[id]['coords'][1] += extent[0] * sin(rot/180*pi)
00927                 elif self.instruction[id]['ref'].split()[1] == 'center':
00928                     self.instruction[id]['coords'][0] -= extent[0]/2 * cos(rot/180*pi)
00929                     self.instruction[id]['coords'][1] += extent[0]/2 * sin(rot/180*pi)
00930                     
00931                 self.instruction[id]['coords'][0] += self.instruction[id]['xoffset']
00932                 self.instruction[id]['coords'][1] -= self.instruction[id]['yoffset']
00933                 coords = self.instruction[id]['coords']
00934                 self.instruction[id]['rect'] = bounds = self.getModifiedTextBounds(coords[0], coords[1], extent, rot)
00935                 self.canvas.DrawRotText(pdc = self.canvas.pdcObj, drawId = id,
00936                                         textDict = self.instruction[id].GetInstruction(),
00937                                         coords = coords, bounds = bounds)
00938                 self.canvas.RedrawSelectBox(id)
00939                 
00940             if itype in ('map', 'vector', 'raster'):
00941                 
00942                 if itype == 'raster':#set resolution
00943                     info = grass.raster_info(self.instruction[id]['raster'])
00944                     RunCommand('g.region', nsres = info['nsres'], ewres = info['ewres'])
00945                     # change current raster in raster legend
00946                     
00947                 if 'rasterLegend' in self.openDialogs:
00948                     self.openDialogs['rasterLegend'].updateDialog()
00949                 id = self.instruction.FindInstructionByType('map').id
00950                 
00951                 #check resolution
00952                 if itype == 'raster':
00953                     SetResolution(dpi = self.instruction[id]['resolution'], 
00954                                   width = self.instruction[id]['rect'].width,
00955                                   height = self.instruction[id]['rect'].height)   
00956                 rectCanvas = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'],
00957                                                                 canvasToPaper = False)
00958                 self.canvas.RecalculateEN()
00959                 self.canvas.UpdateMapLabel()
00960                 
00961                 self.canvas.Draw(pen = self.pen['map'], brush = self.brush['map'],
00962                                  pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = rectCanvas)
00963                 # redraw select box  
00964                 self.canvas.RedrawSelectBox(id)
00965                 self.canvas.pdcTmp.RemoveId(self.canvas.idZoomBoxTmp)
00966                 # redraw to get map to the bottom layer
00967                 #self.canvas.Zoom(zoomFactor = 1, view = (0, 0))
00968                 
00969             if itype == 'rasterLegend':
00970                 if self.instruction[id]['rLegend']:
00971                     self.canvas.UpdateLabel(itype = itype, id = id)
00972                     drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
00973                     self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
00974                                      pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
00975                     self.canvas.RedrawSelectBox(id)
00976                 else:
00977                     self.deleteObject(id)
00978                     
00979             if itype == 'vectorLegend':
00980                 if not self.instruction.FindInstructionByType('vector'):
00981                     self.deleteObject(id)
00982                 elif self.instruction[id]['vLegend']:
00983                     self.canvas.UpdateLabel(itype = itype, id = id)
00984                     drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
00985                     self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
00986                                      pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
00987                     self.canvas.RedrawSelectBox(id)
00988 
00989                 else:
00990                     self.deleteObject(id)
00991                 
00992     def OnPageChanged(self, event):
00993         """!Flatnotebook page has changed"""
00994         self.currentPage = self.book.GetPageIndex(self.book.GetCurrentPage())
00995         
00996         
00997     def OnPageChanging(self, event):
00998         """!Flatnotebook page is changing"""
00999         if self.currentPage == 0 and self.mouse['use'] == 'addMap':
01000             event.Veto()
01001 
01002     def OnHelp(self, event):
01003         """!Show help"""
01004         if self.parent and self.parent.GetName() == 'LayerManager':
01005             log = self.parent.GetLogWindow()
01006             log.RunCmd(['g.manual',
01007                         'entry=wxGUI.PsMap'])
01008         else:
01009             RunCommand('g.manual',
01010                        quiet = True,
01011                        entry = 'wxGUI.PsMap')
01012         
01013     def OnAbout(self, event):
01014         """!Display About window"""
01015         info = wx.AboutDialogInfo()
01016         
01017         info.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
01018         info.SetName(_('wxGUI Cartographic Composer'))
01019         info.SetWebSite('http://grass.osgeo.org')
01020         info.SetDescription(_('(C) 2011 by the GRASS Development Team\n\n') + 
01021                             '\n'.join(textwrap.wrap(_('This program is free software under the GNU General Public License'
01022                                                       '(>=v2). Read the file COPYING that comes with GRASS for details.'), 75)))
01023         
01024         wx.AboutBox(info)
01025 
01026     def OnCloseWindow(self, event):
01027         """!Close window"""
01028         try:
01029             os.remove(self.imgName)
01030         except OSError:
01031             pass
01032         grass.set_raise_on_error(False)
01033         self.Destroy()
01034 
01035 
01036 
01037 class PsMapBufferedWindow(wx.Window):
01038     """!A buffered window class.
01039     
01040     @param parent parent window
01041     @param kwargs other wx.Window parameters
01042     """
01043     def __init__(self, parent, id =  wx.ID_ANY,
01044                  style = wx.NO_FULL_REPAINT_ON_RESIZE,
01045                  **kwargs):
01046         wx.Window.__init__(self, parent, id = id, style = style)
01047         self.parent = parent
01048     
01049         self.FitInside()
01050         
01051         # store an off screen empty bitmap for saving to file
01052         self._buffer = None
01053         # indicates whether or not a resize event has taken place
01054         self.resize = False 
01055         
01056         self.mouse = kwargs['mouse']
01057         self.cursors = kwargs['cursors']
01058         self.preview = kwargs['preview']
01059         self.pen = kwargs['pen']
01060         self.brush = kwargs['brush']
01061         
01062         if kwargs.has_key('instruction'):
01063             self.instruction = kwargs['instruction']
01064         if kwargs.has_key('openDialogs'):
01065             self.openDialogs = kwargs['openDialogs']
01066         if kwargs.has_key('pageId'):
01067             self.pageId = kwargs['pageId']
01068         if kwargs.has_key('objectId'):
01069             self.objectId = kwargs['objectId']
01070         
01071         
01072         #labels
01073         self.itemLabelsDict = { 'map': 'MAP FRAME',
01074                                 'rasterLegend': 'RASTER LEGEND',
01075                                 'vectorLegend': 'VECTOR LEGEND',
01076                                 'mapinfo': 'MAP INFO',
01077                                 'scalebar': 'SCALE BAR',
01078                                 'image': 'IMAGE',
01079                                 'northArrow': 'NORTH ARROW'}
01080         self.itemLabels = {}
01081         
01082         # define PseudoDC
01083         self.pdc = wx.PseudoDC()
01084         self.pdcObj = wx.PseudoDC()
01085         self.pdcPaper = wx.PseudoDC()
01086         self.pdcTmp = wx.PseudoDC()
01087         self.pdcImage = wx.PseudoDC()
01088         dc = wx.ClientDC(self)
01089         self.font = dc.GetFont()
01090         
01091         self.SetClientSize((700,510))#?
01092         self._buffer = wx.EmptyBitmap(*self.GetClientSize())
01093         
01094         self.idBoxTmp = wx.NewId()
01095         self.idZoomBoxTmp = wx.NewId()
01096         self.idResizeBoxTmp = wx.NewId()
01097         self.idLinePointsTmp = (wx.NewId(), wx.NewId()) # ids of marks for moving line vertices
01098 
01099         self.resizeBoxSize = wx.Size(8, 8)
01100         
01101         
01102 
01103         self.dragId = -1
01104         
01105         if self.preview:
01106             self.image = None
01107             self.imageId = 2000
01108             self.imgName = self.parent.imgName
01109             
01110             
01111             
01112         self.currScale = None
01113         
01114         self.Clear()
01115         
01116         self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
01117         
01118         self.Bind(wx.EVT_PAINT, self.OnPaint)
01119         self.Bind(wx.EVT_SIZE,  self.OnSize)
01120         self.Bind(wx.EVT_IDLE,  self.OnIdle)
01121         # self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
01122         self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
01123 
01124 
01125     def Clear(self):
01126         """!Clear canvas and set paper
01127         """
01128         bg = wx.LIGHT_GREY_BRUSH
01129         self.pdcPaper.BeginDrawing()
01130         self.pdcPaper.SetBackground(bg)
01131         self.pdcPaper.Clear()
01132         self.pdcPaper.EndDrawing()
01133         
01134         self.pdcObj.RemoveAll()
01135         self.pdcTmp.RemoveAll()
01136         
01137 
01138 
01139         if not self.preview:
01140             self.SetPage()
01141 
01142     
01143     def CanvasPaperCoordinates(self, rect, canvasToPaper = True):
01144         """!Converts canvas (pixel) -> paper (inch) coordinates and size and vice versa"""
01145         
01146         units = UnitConversion(self)
01147         
01148         fromU = 'pixel'
01149         toU = 'inch'
01150         pRect = self.pdcPaper.GetIdBounds(self.pageId)
01151         pRectx, pRecty = pRect.x, pRect.y 
01152         scale = 1/self.currScale
01153         if not canvasToPaper: # paper -> canvas
01154             fromU = 'inch'
01155             toU = 'pixel'
01156             scale = self.currScale
01157             pRectx = units.convert(value =  - pRect.x, fromUnit = 'pixel', toUnit = 'inch' ) /scale #inch, real, negative
01158             pRecty = units.convert(value =  - pRect.y, fromUnit = 'pixel', toUnit = 'inch' ) /scale 
01159         Width = units.convert(value = rect.GetWidth(), fromUnit = fromU, toUnit = toU) * scale
01160         Height = units.convert(value = rect.GetHeight(), fromUnit = fromU, toUnit = toU) * scale
01161         X = units.convert(value = (rect.GetX() - pRectx), fromUnit = fromU, toUnit = toU) * scale
01162         Y = units.convert(value = (rect.GetY() - pRecty), fromUnit = fromU, toUnit = toU) * scale
01163 
01164         return Rect2D(X, Y, Width, Height)
01165 
01166     
01167     
01168     def SetPage(self):
01169         """!Sets and changes page, redraws paper"""
01170         
01171         page = self.instruction[self.pageId]
01172         if not page:
01173             page = PageSetup(id = self.pageId)
01174             self.instruction.AddInstruction(page)
01175         
01176         ppi = wx.ClientDC(self).GetPPI()
01177         cW, cH = self.GetClientSize()
01178         pW, pH = page['Width']*ppi[0], page['Height']*ppi[1]
01179 
01180         if self.currScale is None:
01181             self.currScale = min(cW/pW, cH/pH)
01182         pW = pW * self.currScale
01183         pH = pH * self.currScale
01184         
01185         x = cW/2 - pW/2
01186         y = cH/2 - pH/2
01187         self.DrawPaper(wx.Rect(x, y, pW, pH))
01188 
01189 
01190     def modifyRectangle(self, r):
01191         """! Recalculates rectangle not to have negative size"""
01192         if r.GetWidth() < 0:
01193             r.SetX(r.GetX() + r.GetWidth())
01194         if r.GetHeight() < 0:
01195             r.SetY(r.GetY() + r.GetHeight())
01196         r.SetWidth(abs(r.GetWidth()))
01197         r.SetHeight(abs(r.GetHeight()))
01198         return r 
01199     
01200     def RecalculateEN(self):
01201         """!Recalculate east and north for texts (eps, points) after their or map's movement"""
01202         try:
01203             mapId = self.instruction.FindInstructionByType('map').id
01204         except AttributeError:
01205             mapId = self.instruction.FindInstructionByType('initMap').id
01206         
01207         for itemType in ('text', 'image', 'northArrow', 'point', 'line', 'rectangle'):
01208             items = self.instruction.FindInstructionByType(itemType, list = True)
01209             for item in items:
01210                 instr = self.instruction[item.id]
01211                 if itemType in ('line', 'rectangle'):
01212                     if itemType == 'line':
01213                         e1, n1 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['where'][0][0],
01214                                                      y = instr['where'][0][1], paperToMap = True)
01215                         e2, n2 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['where'][1][0],
01216                                                      y = instr['where'][1][1], paperToMap = True)
01217                     else: 
01218                         e1, n1 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['rect'].GetLeft(),
01219                                                      y = instr['rect'].GetTop(), paperToMap = True)
01220                         e2, n2 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['rect'].GetRight(),
01221                                                      y = instr['rect'].GetBottom(), paperToMap = True)
01222                     instr['east1'] = e1
01223                     instr['north1'] = n1
01224                     instr['east2'] = e2
01225                     instr['north2'] = n2
01226                 else:
01227                     e, n = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['where'][0],
01228                                                y = instr['where'][1], paperToMap = True)
01229                     instr['east'], instr['north'] = e, n
01230                 
01231     def OnPaint(self, event):
01232         """!Draw pseudo DC to buffer
01233         """
01234         if not self._buffer:
01235             return
01236         dc = wx.BufferedPaintDC(self, self._buffer)
01237         # use PrepareDC to set position correctly
01238         self.PrepareDC(dc)
01239         
01240         dc.SetBackground(wx.LIGHT_GREY_BRUSH)
01241         dc.Clear()
01242         
01243         # draw paper
01244         if not self.preview:
01245             self.pdcPaper.DrawToDC(dc)
01246         # draw to the DC using the calculated clipping rect
01247 
01248         rgn = self.GetUpdateRegion()
01249         
01250         if not self.preview:
01251             self.pdcObj.DrawToDCClipped(dc, rgn.GetBox())
01252         else: 
01253             self.pdcImage.DrawToDCClipped(dc, rgn.GetBox())
01254         self.pdcTmp.DrawToDCClipped(dc, rgn.GetBox())
01255         
01256     def MouseActions(self, event):
01257         """!Mouse motion and button click notifier
01258         """
01259         # zoom with mouse wheel
01260         if event.GetWheelRotation() != 0:
01261             self.OnMouseWheel(event)
01262             
01263         # left mouse button pressed
01264         elif event.LeftDown():
01265             self.OnLeftDown(event)
01266         
01267         # left mouse button released
01268         elif event.LeftUp():
01269             self.OnLeftUp(event)
01270         
01271         # dragging
01272         elif event.Dragging():
01273             self.OnDragging(event)
01274         
01275         # double click
01276         elif event.ButtonDClick():
01277             self.OnButtonDClick(event)
01278         
01279         # middle mouse button pressed
01280         elif event.MiddleDown():
01281             self.OnMiddleDown(event)
01282         
01283         elif event.Moving():
01284             self.OnMouseMoving(event)
01285                 
01286     def OnMouseWheel(self, event):
01287         """!Mouse wheel scrolled.
01288 
01289         Changes zoom."""
01290         if not UserSettings.Get(group = 'display',
01291                                 key = 'mouseWheelZoom',
01292                                 subkey = 'enabled'):
01293             event.Skip()
01294             return
01295 
01296         zoom = event.GetWheelRotation()
01297         oldUse = self.mouse['use']
01298         self.mouse['begin'] = event.GetPosition()
01299         
01300         if UserSettings.Get(group = 'display',
01301                             key = 'mouseWheelZoom',
01302                             subkey = 'selection'):
01303             zoom *= -1
01304             
01305         if zoom > 0:
01306             self.mouse['use'] = 'zoomin'
01307         else:
01308             self.mouse['use'] = 'zoomout'
01309             
01310         zoomFactor, view = self.ComputeZoom(wx.Rect(0, 0, 0, 0))
01311         self.Zoom(zoomFactor, view)
01312         self.mouse['use'] = oldUse
01313 
01314     def OnMouseMoving(self, event):
01315         """!Mouse cursor moving.
01316 
01317         Change cursor when moving over resize marker.
01318         """
01319         if self.mouse['use'] in ('pointer', 'resize'):
01320             pos = event.GetPosition()
01321             foundResize = self.pdcTmp.FindObjects(pos[0], pos[1])
01322             if foundResize and foundResize[0] in (self.idResizeBoxTmp,) + self.idLinePointsTmp:
01323                 self.SetCursor(self.cursors["sizenwse"])
01324                 self.parent.SetStatusText(_('Click and drag to resize object'), 0)
01325             else:
01326                 self.parent.SetStatusText('', 0)
01327                 self.SetCursor(self.cursors["default"])
01328                 
01329     def OnLeftDown(self, event):
01330         """!Left mouse button pressed.
01331 
01332         Select objects, redraw, prepare for moving/resizing.
01333         """
01334         self.mouse['begin'] = event.GetPosition()
01335         self.begin = self.mouse['begin']
01336         
01337         # select
01338         if self.mouse['use'] == 'pointer':
01339             found = self.pdcObj.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1])
01340             foundResize = self.pdcTmp.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1])
01341 
01342             if foundResize and foundResize[0] in (self.idResizeBoxTmp,) + self.idLinePointsTmp:
01343                 self.mouse['use'] = 'resize'
01344                 
01345                 # when resizing, proportions match region
01346                 if self.instruction[self.dragId].type == 'map':
01347                     self.constraint = False
01348                     self.mapBounds = self.pdcObj.GetIdBounds(self.dragId)
01349                     if self.instruction[self.dragId]['scaleType'] in (0, 1, 2):
01350                         self.constraint = True
01351                         self.mapBounds = self.pdcObj.GetIdBounds(self.dragId)
01352 
01353                 if self.instruction[self.dragId].type == 'line':
01354                     self.currentLinePoint = self.idLinePointsTmp.index(foundResize[0])
01355                 
01356             elif found:
01357                 self.dragId = found[0]
01358                 self.RedrawSelectBox(self.dragId)
01359                 if self.instruction[self.dragId].type not in ('map', 'rectangle'):
01360                     self.pdcTmp.RemoveId(self.idResizeBoxTmp)
01361                     self.Refresh()
01362                 if self.instruction[self.dragId].type != 'line':
01363                     for id in self.idLinePointsTmp:
01364                         self.pdcTmp.RemoveId(id)
01365                     self.Refresh()
01366                     
01367             else:
01368                 self.dragId = -1
01369                 self.pdcTmp.RemoveId(self.idBoxTmp)
01370                 self.pdcTmp.RemoveId(self.idResizeBoxTmp)
01371                 for id in self.idLinePointsTmp:
01372                     self.pdcTmp.RemoveId(id)
01373                 self.Refresh()
01374 
01375     def OnLeftUp(self, event):
01376         """!Left mouse button released.
01377 
01378         Recalculate zooming/resizing/moving and redraw.
01379         """
01380         # zoom in, zoom out
01381         if self.mouse['use'] in ('zoomin','zoomout'):
01382             zoomR = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp)
01383             self.pdcTmp.RemoveId(self.idZoomBoxTmp)
01384             self.Refresh()
01385             zoomFactor, view = self.ComputeZoom(zoomR)
01386             self.Zoom(zoomFactor, view)
01387 
01388         # draw map frame
01389         if self.mouse['use'] == 'addMap':
01390             rectTmp = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp)
01391             # too small rectangle, it's usually some mistake
01392             if rectTmp.GetWidth() < 20 or rectTmp.GetHeight() < 20:
01393                 self.pdcTmp.RemoveId(self.idZoomBoxTmp)
01394                 self.Refresh()
01395                 return
01396             rectPaper = self.CanvasPaperCoordinates(rect = rectTmp, canvasToPaper = True)
01397 
01398             dlg = MapDialog(parent = self.parent, id = [None, None, None], settings = self.instruction, 
01399                             rect = rectPaper)
01400             self.openDialogs['map'] = dlg
01401             self.openDialogs['map'].Show()
01402             
01403             self.mouse['use'] = self.parent.mouseOld
01404 
01405             self.SetCursor(self.parent.cursorOld)
01406             self.parent.toolbar.ToggleTool(self.parent.actionOld, True)
01407             self.parent.toolbar.ToggleTool(self.parent.toolbar.action['id'], False)
01408             self.parent.toolbar.action['id'] = self.parent.actionOld
01409             return
01410 
01411         # resize resizable objects (map, line, rectangle)
01412         if self.mouse['use'] == 'resize':
01413             mapObj = self.instruction.FindInstructionByType('map')
01414             if not mapObj:
01415                 mapObj = self.instruction.FindInstructionByType('initMap')
01416             mapId = mapObj.id
01417             
01418             if self.dragId == mapId:
01419                 # necessary to change either map frame (scaleType 0,1,2) or region (scaletype 3)
01420                 newRectCanvas = self.pdcObj.GetIdBounds(mapId)
01421                 newRectPaper = self.CanvasPaperCoordinates(rect = newRectCanvas, canvasToPaper = True)
01422                 self.instruction[mapId]['rect'] = newRectPaper
01423                 
01424                 if self.instruction[mapId]['scaleType'] in (0, 1, 2):
01425                     if self.instruction[mapId]['scaleType'] == 0:
01426                         
01427                         scale, foo, rect = AutoAdjust(self, scaleType = 0,
01428                                                       map = self.instruction[mapId]['map'],
01429                                                       mapType = self.instruction[mapId]['mapType'], 
01430                                                       rect = self.instruction[mapId]['rect'])
01431                         
01432                     elif self.instruction[mapId]['scaleType'] == 1:
01433                         scale, foo, rect = AutoAdjust(self, scaleType = 1,
01434                                                       region = self.instruction[mapId]['region'],
01435                                                       rect = self.instruction[mapId]['rect'])
01436                     else:
01437                         scale, foo, rect = AutoAdjust(self, scaleType = 2,
01438                                                       rect = self.instruction[mapId]['rect'])
01439                     self.instruction[mapId]['rect'] = rect
01440                     self.instruction[mapId]['scale'] = scale
01441                     
01442                     rectCanvas = self.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)
01443                     self.Draw(pen = self.pen['map'], brush = self.brush['map'],
01444                               pdc = self.pdcObj, drawid = mapId, pdctype = 'rectText', bb = rectCanvas)
01445                     
01446                 elif self.instruction[mapId]['scaleType'] == 3:
01447                     ComputeSetRegion(self, mapDict = self.instruction[mapId].GetInstruction())
01448                 #check resolution
01449                 SetResolution(dpi = self.instruction[mapId]['resolution'],
01450                               width = self.instruction[mapId]['rect'].width,
01451                               height = self.instruction[mapId]['rect'].height)
01452                 
01453                 self.RedrawSelectBox(mapId)
01454                 self.Zoom(zoomFactor = 1, view = (0, 0))
01455 
01456             elif self.instruction[self.dragId].type == 'line':
01457                 points = self.instruction[self.dragId]['where']
01458                 self.instruction[self.dragId]['rect'] = Rect2DPP(points[0], points[1])
01459                 self.RecalculatePosition(ids = [self.dragId])
01460 
01461             elif self.instruction[self.dragId].type == 'rectangle':
01462                 self.RecalculatePosition(ids = [self.dragId])
01463 
01464             self.mouse['use'] = 'pointer'
01465             
01466         # recalculate the position of objects after dragging
01467         if self.mouse['use'] in ('pointer', 'resize') and self.dragId != -1:
01468             if self.mouse['begin'] != event.GetPosition(): #for double click
01469                 
01470                 self.RecalculatePosition(ids = [self.dragId])
01471                 if self.instruction[self.dragId].type in self.openDialogs:
01472                     self.openDialogs[self.instruction[self.dragId].type].updateDialog()
01473         
01474         elif self.mouse['use'] in ('addPoint', 'addLine', 'addRectangle'):
01475             endCoordinates = self.CanvasPaperCoordinates(rect = wx.Rect(event.GetX(), event.GetY(), 0, 0),
01476                                                          canvasToPaper = True)[:2]
01477 
01478             diffX = event.GetX() - self.mouse['begin'][0]
01479             diffY = event.GetY() - self.mouse['begin'][1]
01480 
01481             if self.mouse['use'] == 'addPoint':
01482                 self.parent.AddPoint(coordinates = endCoordinates)
01483             elif self.mouse['use'] in ('addLine', 'addRectangle'):
01484                 # not too small lines/rectangles
01485                 if sqrt(diffX * diffX + diffY * diffY) < 5:
01486                     self.pdcTmp.RemoveId(self.idZoomBoxTmp)
01487                     self.Refresh()
01488                     return
01489 
01490                 beginCoordinates = self.CanvasPaperCoordinates(rect = wx.Rect(self.mouse['begin'][0],
01491                                                                               self.mouse['begin'][1], 0, 0),
01492                                                                canvasToPaper = True)[:2]
01493                 if self.mouse['use'] == 'addLine':
01494                     self.parent.AddLine(coordinates = [beginCoordinates, endCoordinates])
01495                 else:
01496                     self.parent.AddRectangle(coordinates = [beginCoordinates, endCoordinates])
01497                 self.pdcTmp.RemoveId(self.idZoomBoxTmp)
01498                 self.Refresh()
01499 
01500     def OnButtonDClick(self, event):
01501         """!Open object dialog for editing."""
01502         if self.mouse['use'] == 'pointer' and self.dragId != -1:
01503             itemCall = {'text':self.parent.OnAddText,
01504                         'mapinfo': self.parent.OnAddMapinfo,
01505                         'scalebar': self.parent.OnAddScalebar,
01506                         'image': self.parent.OnAddImage,
01507                         'northArrow' : self.parent.OnAddNorthArrow,
01508                         'point': self.parent.AddPoint,
01509                         'line': self.parent.AddLine,
01510                         'rectangle': self.parent.AddRectangle,
01511                         'rasterLegend': self.parent.OnAddLegend,
01512                         'vectorLegend': self.parent.OnAddLegend,
01513                         'map': self.parent.OnAddMap}
01514 
01515             itemArg = { 'text': dict(event = None, id = self.dragId),
01516                         'mapinfo': dict(event = None),
01517                         'scalebar': dict(event = None),
01518                         'image': dict(event = None, id = self.dragId),
01519                         'northArrow': dict(event = None, id = self.dragId),
01520                         'point': dict(id = self.dragId),
01521                         'line': dict(id = self.dragId),
01522                         'rectangle': dict(id = self.dragId),
01523                         'rasterLegend': dict(event = None),
01524                         'vectorLegend': dict(event = None, page = 1),
01525                         'map': dict(event = None, notebook = True)}
01526 
01527             type = self.instruction[self.dragId].type
01528             itemCall[type](**itemArg[type])
01529 
01530     def OnDragging(self, event):
01531         """!Process panning/resizing/drawing/moving."""
01532         if event.MiddleIsDown():
01533             # panning
01534             self.mouse['end'] = event.GetPosition()
01535             self.Pan(begin = self.mouse['begin'], end = self.mouse['end'])
01536             self.mouse['begin'] = event.GetPosition()
01537 
01538         elif event.LeftIsDown():
01539             # draw box when zooming, creating map 
01540             if self.mouse['use'] in ('zoomin', 'zoomout', 'addMap', 'addLine', 'addRectangle'):
01541                 self.mouse['end'] = event.GetPosition()
01542                 r = wx.Rect(self.mouse['begin'][0], self.mouse['begin'][1],
01543                             self.mouse['end'][0]-self.mouse['begin'][0], self.mouse['end'][1]-self.mouse['begin'][1])
01544                 r = self.modifyRectangle(r)
01545 
01546                 if self.mouse['use'] in ('addLine', 'addRectangle'):
01547                     if self.mouse['use'] == 'addLine':
01548                         pdcType = 'line'
01549                         lineCoords = (self.mouse['begin'], self.mouse['end'])
01550                     else:
01551                         pdcType = 'rect'
01552                         lineCoords = None
01553                         if r[2] < 2 or r[3] < 2:
01554                             # to avoid strange behavoiur
01555                             return
01556 
01557                     self.Draw(pen = self.pen['line'], brush = self.brush['line'],
01558                               pdc = self.pdcTmp, drawid = self.idZoomBoxTmp,
01559                               pdctype = pdcType, bb = r, lineCoords = lineCoords)
01560 
01561                 else:
01562                     self.Draw(pen = self.pen['box'], brush = self.brush['box'],
01563                               pdc = self.pdcTmp, drawid = self.idZoomBoxTmp,
01564                               pdctype = 'rect', bb = r)
01565 
01566             # panning
01567             if self.mouse["use"] == 'pan':
01568                 self.mouse['end'] = event.GetPosition()
01569                 self.Pan(begin = self.mouse['begin'], end = self.mouse['end'])
01570                 self.mouse['begin'] = event.GetPosition()
01571 
01572             # move object
01573             if self.mouse['use'] == 'pointer' and self.dragId != -1:
01574                 self.mouse['end'] = event.GetPosition()
01575                 dx, dy = self.mouse['end'][0] - self.begin[0], self.mouse['end'][1] - self.begin[1]
01576                 self.pdcObj.TranslateId(self.dragId, dx, dy)
01577                 self.pdcTmp.TranslateId(self.idBoxTmp, dx, dy)
01578                 self.pdcTmp.TranslateId(self.idResizeBoxTmp, dx, dy)
01579                 for id in self.idLinePointsTmp:
01580                     self.pdcTmp.TranslateId(id, dx, dy)
01581                 if self.instruction[self.dragId].type == 'text': 
01582                     self.instruction[self.dragId]['coords'] = self.instruction[self.dragId]['coords'][0] + dx,\
01583                         self.instruction[self.dragId]['coords'][1] + dy
01584                 self.begin = event.GetPosition()
01585                 self.Refresh()
01586 
01587             # resize object
01588             if self.mouse['use'] == 'resize':
01589                 pos = event.GetPosition()
01590                 diffX = pos[0] - self.mouse['begin'][0]
01591                 diffY = pos[1] - self.mouse['begin'][1]
01592                 if self.instruction[self.dragId].type == 'map':
01593                     x, y = self.mapBounds.GetX(), self.mapBounds.GetY()
01594                     width, height = self.mapBounds.GetWidth(), self.mapBounds.GetHeight()
01595                     # match given region
01596                     if self.constraint:
01597                         if width > height:
01598                             newWidth = width + diffX
01599                             newHeight = height + diffX * (float(height) / width)
01600                         else:
01601                             newWidth = width + diffY * (float(width) / height)
01602                             newHeight = height + diffY
01603                     else:
01604                         newWidth = width + diffX
01605                         newHeight = height + diffY
01606                         
01607                     if newWidth < 10 or newHeight < 10:
01608                         return
01609 
01610                     bounds = wx.Rect(x, y, newWidth, newHeight)
01611                     self.Draw(pen = self.pen['map'], brush = self.brush['map'], pdc = self.pdcObj, drawid = self.dragId,
01612                               pdctype = 'rectText', bb = bounds)
01613 
01614                 elif self.instruction[self.dragId].type == 'rectangle':
01615                     instr = self.instruction[self.dragId]
01616                     rect = self.CanvasPaperCoordinates(rect = instr['rect'], canvasToPaper = False)
01617                     rect.SetWidth(rect.GetWidth() + diffX)
01618                     rect.SetHeight(rect.GetHeight() + diffY)
01619 
01620                     if rect.GetWidth() < 5 or rect.GetHeight() < 5:
01621                         return
01622 
01623                     self.DrawGraphics(drawid = self.dragId, shape = 'rectangle', color = instr['color'],
01624                                       fcolor = instr['fcolor'], width = instr['width'], bb = rect)
01625 
01626                 elif self.instruction[self.dragId].type == 'line':
01627                     instr = self.instruction[self.dragId]
01628                     points = instr['where']
01629                     # moving point
01630                     if self.currentLinePoint == 0:
01631                         pPaper = points[1]
01632                     else:
01633                         pPaper = points[0]
01634                     pCanvas = self.CanvasPaperCoordinates(rect = Rect2DPS(pPaper, (0, 0)),
01635                                                           canvasToPaper = False)[:2]
01636                     bounds = wx.RectPP(pCanvas, pos)
01637                     self.DrawGraphics(drawid = self.dragId, shape = 'line', color = instr['color'],
01638                                       width = instr['width'], bb = bounds, lineCoords = (pos, pCanvas))
01639 
01640                     # update paper coordinates
01641                     points[self.currentLinePoint] = self.CanvasPaperCoordinates(rect = wx.RectPS(pos, (0, 0)),
01642                                                                                 canvasToPaper = True)[:2]
01643 
01644                 self.RedrawSelectBox(self.dragId)
01645 
01646     def OnMiddleDown(self, event):
01647         """!Middle mouse button pressed."""
01648         self.mouse['begin'] = event.GetPosition()
01649 
01650     def Pan(self, begin, end):
01651         """!Move canvas while dragging.
01652         
01653         @param begin x,y coordinates of first point
01654         @param end x,y coordinates of second point
01655         """
01656         view = begin[0] - end[0], begin[1] - end[1]
01657         zoomFactor = 1
01658         self.Zoom(zoomFactor, view)
01659                 
01660     def RecalculatePosition(self, ids):
01661         for id in ids:
01662             itype = self.instruction[id].type
01663             if itype in ('map', 'rectangle'):
01664                 self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
01665                                                                            canvasToPaper = True)
01666                 self.RecalculateEN()
01667                 
01668             elif itype in ('mapinfo' ,'rasterLegend', 'vectorLegend', 'image', 'northArrow'):
01669                 self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
01670                                                                            canvasToPaper = True)
01671                 self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
01672                                                                             canvasToPaper = True)[:2] 
01673                 if itype in ('image', 'northArrow'):
01674                     self.RecalculateEN()
01675 
01676             elif itype == 'point':
01677                 rect = self.pdcObj.GetIdBounds(id)
01678                 self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = rect,
01679                                                                            canvasToPaper = True)
01680                 rect.OffsetXY(rect.GetWidth()/2, rect.GetHeight()/2)
01681                 self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = rect,
01682                                                                             canvasToPaper = True)[:2]
01683                 self.RecalculateEN()
01684 
01685             elif itype == 'line':
01686                 rect = self.pdcObj.GetIdBounds(id)
01687                 oldRect = self.instruction[id]['rect']
01688                 newRect = self.CanvasPaperCoordinates(rect = rect, canvasToPaper = True)
01689                 xDiff = newRect[0] - oldRect[0]
01690                 yDiff = newRect[1] - oldRect[1]
01691                 self.instruction[id]['rect'] = newRect
01692 
01693                 point1 = wx.Point2D(xDiff, yDiff) + self.instruction[id]['where'][0]
01694                 point2 = wx.Point2D(xDiff, yDiff) + self.instruction[id]['where'][1]
01695                 self.instruction[id]['where'] = [point1, point2]
01696                 
01697                 self.RecalculateEN()
01698 
01699             elif  itype == 'scalebar':
01700                 self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
01701                                                                            canvasToPaper = True)
01702 
01703                 
01704                 self.instruction[id]['where'] = self.instruction[id]['rect'].GetCentre()
01705                 
01706             elif  itype == 'text':
01707                 x, y = self.instruction[id]['coords'][0] - self.instruction[id]['xoffset'],\
01708                     self.instruction[id]['coords'][1] + self.instruction[id]['yoffset']
01709                 extent = self.parent.getTextExtent(textDict = self.instruction[id])
01710                 if self.instruction[id]['rotate'] is not None:
01711                     rot = float(self.instruction[id]['rotate'])/180*pi 
01712                 else:
01713                     rot = 0
01714 
01715                 if self.instruction[id]['ref'].split()[0] == 'lower':
01716                     y += extent[1]
01717                 elif self.instruction[id]['ref'].split()[0] == 'center':
01718                     y += extent[1]/2
01719                 if self.instruction[id]['ref'].split()[1] == 'right':
01720                     x += extent[0] * cos(rot)
01721                     y -= extent[0] * sin(rot)
01722                 elif self.instruction[id]['ref'].split()[1] == 'center':
01723                     x += extent[0]/2 * cos(rot)
01724                     y -= extent[0]/2 * sin(rot)
01725                 
01726                 self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = Rect2D(x, y, 0, 0),
01727                                                                             canvasToPaper = True)[:2]
01728                 self.RecalculateEN()
01729         
01730     def ComputeZoom(self, rect):
01731         """!Computes zoom factor and scroll view"""
01732         zoomFactor = 1
01733         cW, cH = self.GetClientSize()
01734         cW = float(cW)
01735         if rect.IsEmpty(): # clicked on canvas
01736             zoomFactor = 1.5
01737             if self.mouse['use'] == 'zoomout':
01738                 zoomFactor = 1./zoomFactor
01739             x,y = self.mouse['begin']
01740             xView = x - x/zoomFactor#x - cW/(zoomFactor * 2)
01741             yView = y - y/zoomFactor#y - cH/(zoomFactor * 2)
01742 
01743         else:   #dragging    
01744             rW, rH = float(rect.GetWidth()), float(rect.GetHeight())
01745             try:
01746                 zoomFactor = 1/max(rW/cW, rH/cH)
01747             except ZeroDivisionError:
01748                 zoomFactor = 1
01749             # when zooming to full extent, in some cases, there was zoom 1.01..., which causes problem
01750             if abs(zoomFactor - 1) > 0.01:
01751                 zoomFactor = zoomFactor 
01752             else:
01753                 zoomFactor = 1.
01754 
01755 
01756             if self.mouse['use'] == 'zoomout':
01757                 zoomFactor = min(rW/cW, rH/cH) 
01758             try:
01759                 if rW/rH > cW/cH:
01760                     yView = rect.GetY() - (rW*(cH/cW) - rH)/2
01761                     xView = rect.GetX()
01762                     
01763                     if self.mouse['use'] == 'zoomout':
01764                         x,y = rect.GetX() + (rW-(cW/cH)*rH)/2, rect.GetY()
01765                         xView, yView = -x, -y
01766                 else:
01767                     xView = rect.GetX() - (rH*(cW/cH) - rW)/2
01768                     yView = rect.GetY()
01769                     if self.mouse['use'] == 'zoomout':
01770                         x,y = rect.GetX(), rect.GetY() + (rH-(cH/cW)*rW)/2
01771                         xView, yView = -x, -y
01772             except ZeroDivisionError:
01773                 xView, yView = rect.GetX(), rect.GetY()
01774         return zoomFactor, (int(xView), int(yView))
01775     
01776     
01777     def Zoom(self, zoomFactor, view):
01778         """! Zoom to specified region, scroll view, redraw"""
01779         if not self.currScale:
01780             return
01781         self.currScale = self.currScale*zoomFactor
01782         
01783         if self.currScale > 10 or self.currScale < 0.1:
01784             self.currScale = self.currScale/zoomFactor
01785             return 
01786         if not self.preview:
01787             # redraw paper
01788             pRect = self.pdcPaper.GetIdBounds(self.pageId)
01789             pRect.OffsetXY(-view[0], -view[1])
01790             pRect = self.ScaleRect(rect = pRect, scale = zoomFactor)
01791             self.DrawPaper(pRect)
01792             
01793             #redraw objects
01794             for id in self.objectId:
01795                 oRect = self.CanvasPaperCoordinates(
01796                     rect = self.instruction[id]['rect'], canvasToPaper = False)
01797                 
01798                 type = self.instruction[id].type
01799                 if type == 'text':
01800                     coords = self.instruction[id]['coords']# recalculate coordinates, they are not equal to BB
01801                     self.instruction[id]['coords'] = coords = [(int(coord) - view[i]) * zoomFactor
01802                                                                for i, coord in enumerate(coords)]
01803                     self.DrawRotText(pdc = self.pdcObj, drawId = id, textDict = self.instruction[id],
01804                                      coords = coords, bounds = oRect )
01805                     extent = self.parent.getTextExtent(textDict = self.instruction[id])
01806                     if self.instruction[id]['rotate']:
01807                         rot = float(self.instruction[id]['rotate']) 
01808                     else:
01809                         rot = 0
01810 
01811                     self.instruction[id]['rect'] = bounds = self.parent.getModifiedTextBounds(coords[0], coords[1], extent, rot)
01812                     self.pdcObj.SetIdBounds(id, bounds)
01813                 elif type == 'northArrow':
01814                     self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj,
01815                               drawid = id, pdctype = 'bitmap', bb = oRect)
01816 
01817                 elif type in ('point', 'line', 'rectangle'):
01818                     instr = self.instruction[id]
01819                     color = self.instruction[id]['color']
01820                     width = fcolor = coords = None
01821 
01822                     if type in ('point', 'rectangle'):
01823                         fcolor = self.instruction[id]['fcolor']
01824                     if type in ('line', 'rectangle'):
01825                         width = self.instruction[id]['width']
01826                     if type in ('line'):
01827                         point1, point2 = instr['where'][0], instr['where'][1]
01828                         point1 = self.CanvasPaperCoordinates(rect = Rect2DPS(point1, (0, 0)),
01829                                                              canvasToPaper = False)[:2]
01830                         point2 = self.CanvasPaperCoordinates(rect = Rect2DPS(point2, (0, 0)),
01831                                                              canvasToPaper = False)[:2]
01832                         coords = (point1, point2)
01833 
01834                     self.DrawGraphics(drawid = id, shape = type, bb = oRect, lineCoords = coords,
01835                                     color = color, fcolor = fcolor, width = width)
01836 
01837                 else:
01838                     self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj,
01839                               drawid = id, pdctype = 'rectText', bb = oRect)
01840             #redraw tmp objects
01841             if self.dragId != -1:
01842                 self.RedrawSelectBox(self.dragId)
01843                 
01844         #redraw preview
01845         else: # preview mode    
01846             imageRect = self.pdcImage.GetIdBounds(self.imageId)
01847             imageRect.OffsetXY(-view[0], -view[1])
01848             imageRect = self.ScaleRect(rect = imageRect, scale = zoomFactor)
01849             self.DrawImage(imageRect)
01850         
01851     def ZoomAll(self):
01852         """! Zoom to full extent"""  
01853         if not self.preview:
01854             bounds = self.pdcPaper.GetIdBounds(self.pageId)
01855         else:
01856             bounds = self.pdcImage.GetIdBounds(self.imageId)
01857         zoomP = bounds.Inflate(bounds.width/20, bounds.height/20)
01858         zoomFactor, view = self.ComputeZoom(zoomP)
01859         self.Zoom(zoomFactor, view)
01860         
01861     def Draw(self, pen, brush, pdc, drawid = None, pdctype = 'rect', bb = wx.Rect(0,0,0,0), lineCoords = None): 
01862         """! Draw object with given pen and brush.
01863 
01864         @param pdc PseudoDC
01865         @param pdctype 'bitmap'/'rectText'/'rect'/'point'/'line'
01866         @param bb bounding box
01867         @param lineCoords coordinates of line start, end points (wx.Point, wx.Point)
01868         """    
01869         if drawid is None:
01870             drawid = wx.NewId()
01871         bb = bb.Get()
01872         pdc.BeginDrawing()
01873         pdc.RemoveId(drawid)
01874         pdc.SetId(drawid)
01875         pdc.SetPen(pen)
01876         pdc.SetBrush(brush)
01877         
01878         if pdctype == 'bitmap':
01879             if havePILImage:
01880                 file = self.instruction[drawid]['epsfile']
01881                 rotation = self.instruction[drawid]['rotate']
01882                 self.DrawBitmap(pdc = pdc, filePath = file, rotation = rotation, bbox = bb)
01883             else: # draw only rectangle with label
01884                 pdctype = 'rectText'
01885                 
01886         if pdctype in ('rect', 'rectText'):
01887             pdc.DrawRectangle(*bb)
01888             
01889         if pdctype == 'rectText':
01890             dc = wx.ClientDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks
01891             font = self.font
01892             size = 10
01893             font.SetPointSize(size)
01894             font.SetStyle(wx.ITALIC)
01895             dc.SetFont(font)
01896             pdc.SetFont(font)
01897             text = '\n'.join(self.itemLabels[drawid])
01898             w,h,lh = dc.GetMultiLineTextExtent(text)
01899             textExtent = (w,h)
01900             textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb)
01901             r = map(int, bb)
01902             while not wx.Rect(*r).ContainsRect(textRect) and size >= 8:
01903                 size -= 2
01904                 font.SetPointSize(size)
01905                 dc.SetFont(font)
01906                 pdc.SetFont(font)
01907                 textExtent = dc.GetTextExtent(text)
01908                 textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb)
01909             pdc.SetTextForeground(wx.Color(100,100,100,200)) 
01910             pdc.SetBackgroundMode(wx.TRANSPARENT)
01911             pdc.DrawText(text = text, x = textRect.x, y = textRect.y)
01912                 
01913         elif pdctype == 'point':
01914             pdc.DrawCircle(x = bb[0] + bb[2] / 2,
01915                            y = bb[1] + bb[3] / 2,
01916                            radius = bb[2] / 2)
01917                            
01918         elif pdctype == 'line':
01919             pdc.DrawLinePoint(lineCoords[0], lineCoords[1])
01920 
01921         pdc.SetIdBounds(drawid, bb)
01922         pdc.EndDrawing()
01923         self.Refresh()
01924 
01925         return drawid
01926     
01927     def DrawGraphics(self, drawid, shape, color, bb, width = None, fcolor = None, lineCoords = None):
01928         """!Draw point/line/rectangle with given color and width
01929 
01930         @param drawid id of drawn object
01931         @param shape drawn shape: 'point'/'line'/'rectangle'
01932         @param color pen outline color ('RRR:GGG:BBB')
01933         @param fcolor brush fill color, if meaningful ('RRR:GGG:BBB')
01934         @param width pen width
01935         @param bb bounding box
01936         @param lineCoords line coordinates (for line only)
01937         """
01938         pdctype = {'point'     : 'point',
01939                    'line'      : 'line',
01940                    'rectangle' : 'rect'}
01941 
01942         if color == 'none':
01943             pen = wx.TRANSPARENT_PEN
01944         else:
01945             if width is not None:
01946                 units = UnitConversion(self)
01947                 width = int(units.convert(value = width, fromUnit = 'point', toUnit = 'pixel') * self.currScale)
01948             else:
01949                 width = 2
01950             pen = wx.Pen(colour = convertRGB(color), width = width)
01951             pen.SetCap(wx.CAP_BUTT) # this is how ps.map draws
01952 
01953         brush = wx.TRANSPARENT_BRUSH
01954         if fcolor and fcolor != 'none':
01955             brush = wx.Brush(colour = convertRGB(fcolor))
01956         
01957         self.Draw(pen = pen, brush = brush, pdc = self.pdcObj, pdctype = pdctype[shape],
01958                   drawid = drawid, bb = bb, lineCoords = lineCoords)
01959 
01960     def DrawBitmap(self, pdc, filePath, rotation, bbox):
01961         """!Draw bitmap using PIL"""
01962         pImg = PILImage.open(filePath)
01963         if rotation:
01964             # get rid of black background
01965             pImg = pImg.convert("RGBA")
01966             rot = pImg.rotate(rotation, expand = 1)
01967             new = PILImage.new('RGBA', rot.size, (255,) * 4)
01968             pImg = PILImage.composite(rot, new, rot)
01969         pImg = pImg.resize((int(bbox[2]), int(bbox[3])), resample = PILImage.BICUBIC)
01970         img = PilImageToWxImage(pImg)
01971         bitmap = img.ConvertToBitmap()
01972         mask = wx.Mask(bitmap, wx.WHITE)
01973         bitmap.SetMask(mask)
01974         pdc.DrawBitmap(bitmap, bbox[0], bbox[1], useMask = True)
01975         
01976     def DrawRotText(self, pdc, drawId, textDict, coords, bounds):
01977         if textDict['rotate']:
01978             rot = float(textDict['rotate']) 
01979         else:
01980             rot = 0
01981 
01982         fontsize = textDict['fontsize'] * self.currScale
01983         if textDict['background'] != 'none':
01984             background = textDict['background'] 
01985         else:
01986             background = None
01987 
01988         pdc.RemoveId(drawId)
01989         pdc.SetId(drawId)
01990         pdc.BeginDrawing()
01991         
01992         # border is not redrawn when zoom changes, why?
01993 ##        if textDict['border'] != 'none' and not rot:
01994 ##            units = UnitConversion(self)
01995 ##            borderWidth = units.convert(value = textDict['width'],
01996 ##                                        fromUnit = 'point', toUnit = 'pixel' ) * self.currScale
01997 ##            pdc.SetPen(wx.Pen(colour = convertRGB(textDict['border']), width = borderWidth))
01998 ##            pdc.DrawRectangle(*bounds)
01999             
02000         if background:
02001             pdc.SetTextBackground(convertRGB(background))
02002             pdc.SetBackgroundMode(wx.SOLID)
02003         else:
02004             pdc.SetBackgroundMode(wx.TRANSPARENT)
02005         
02006         fn = self.parent.makePSFont(textDict)
02007 
02008         pdc.SetFont(fn)
02009         pdc.SetTextForeground(convertRGB(textDict['color']))        
02010         pdc.DrawRotatedText(textDict['text'], coords[0], coords[1], rot)
02011         
02012         pdc.SetIdBounds(drawId, wx.Rect(*bounds))
02013         self.Refresh()
02014         pdc.EndDrawing()
02015         
02016     def DrawImage(self, rect):
02017         """!Draw preview image to pseudoDC"""
02018         self.pdcImage.ClearId(self.imageId)
02019         self.pdcImage.SetId(self.imageId)
02020         img = self.image
02021         
02022 
02023         if img.GetWidth() != rect.width or img.GetHeight() != rect.height:
02024             img = img.Scale(rect.width, rect.height)
02025         bitmap = img.ConvertToBitmap()
02026         
02027         self.pdcImage.BeginDrawing()
02028         self.pdcImage.DrawBitmap(bitmap, rect.x, rect.y)
02029         self.pdcImage.SetIdBounds(self.imageId, rect)
02030         self.pdcImage.EndDrawing()
02031         self.Refresh()
02032         
02033     def DrawPaper(self, rect):
02034         """!Draw paper and margins"""
02035         page = self.instruction[self.pageId]
02036         scale = page['Width'] / rect.GetWidth()
02037         w = (page['Width'] - page['Right'] - page['Left']) / scale
02038         h = (page['Height'] - page['Top'] - page['Bottom']) / scale
02039         x = page['Left'] / scale + rect.GetX()
02040         y = page['Top'] / scale + rect.GetY()
02041         
02042         self.pdcPaper.BeginDrawing()
02043         self.pdcPaper.RemoveId(self.pageId)
02044         self.pdcPaper.SetId(self.pageId)
02045         self.pdcPaper.SetPen(self.pen['paper'])
02046         self.pdcPaper.SetBrush(self.brush['paper'])
02047         self.pdcPaper.DrawRectangleRect(rect)
02048         
02049         self.pdcPaper.SetPen(self.pen['margins'])
02050         self.pdcPaper.SetBrush(self.brush['margins'])
02051         self.pdcPaper.DrawRectangle(x, y, w, h)
02052         
02053         self.pdcPaper.SetIdBounds(self.pageId, rect)
02054         self.pdcPaper.EndDrawing()
02055         self.Refresh()
02056 
02057         
02058     def ImageRect(self):
02059         """!Returns image centered in canvas, computes scale"""
02060         img = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG)
02061         cW, cH = self.GetClientSize()
02062         iW, iH = img.GetWidth(), img.GetHeight()
02063 
02064         self.currScale = min(float(cW)/iW, float(cH)/iH)
02065         iW = iW * self.currScale
02066         iH = iH * self.currScale
02067         x = cW/2 - iW/2
02068         y = cH/2 - iH/2
02069         imageRect = wx.Rect(x, y, iW, iH)
02070 
02071         return imageRect 
02072     
02073     def RedrawSelectBox(self, id):
02074         """!Redraws select box when selected object changes its size"""
02075         if self.dragId == id:
02076             rect = self.pdcObj.GetIdBounds(id)
02077             if self.instruction[id].type != 'line':
02078                 rect = rect.Inflate(3,3)
02079             # draw select box around object
02080             self.Draw(pen = self.pen['select'], brush = self.brush['select'], pdc = self.pdcTmp,
02081                       drawid = self.idBoxTmp, pdctype = 'rect', bb = rect)
02082             
02083             # draw small marks signalizing resizing
02084             if self.instruction[id].type in ('map', 'rectangle'):
02085                 controlP = self.pdcObj.GetIdBounds(id).GetBottomRight()
02086                 rect  = wx.RectPS(controlP, self.resizeBoxSize)
02087                 self.Draw(pen = self.pen['resize'], brush = self.brush['resize'], pdc = self.pdcTmp,
02088                           drawid = self.idResizeBoxTmp, pdctype = 'rect', bb = rect)
02089 
02090             elif self.instruction[id].type == 'line':
02091                 p1Paper = self.instruction[id]['where'][0]
02092                 p2Paper = self.instruction[id]['where'][1]
02093                 p1Canvas = self.CanvasPaperCoordinates(rect = Rect2DPS(p1Paper, (0, 0)), canvasToPaper = False)[:2]
02094                 p2Canvas = self.CanvasPaperCoordinates(rect = Rect2DPS(p2Paper, (0, 0)), canvasToPaper = False)[:2]
02095                 rect = []
02096                 box = wx.RectS(self.resizeBoxSize)
02097                 rect.append(box.CenterIn(wx.RectPS(p1Canvas, wx.Size())))
02098                 rect.append(box.CenterIn(wx.RectPS(p2Canvas, wx.Size())))
02099                 for i, point in enumerate((p1Canvas, p2Canvas)):
02100                     self.Draw(pen = self.pen['resize'], brush = self.brush['resize'], pdc = self.pdcTmp,
02101                               drawid = self.idLinePointsTmp[i], pdctype = 'rect', bb = rect[i])
02102         
02103     def UpdateMapLabel(self):
02104         """!Updates map frame label"""
02105 
02106         vector = self.instruction.FindInstructionByType('vector')
02107         if vector:
02108             vectorId = vector.id 
02109         else:
02110             vectorId = None
02111 
02112         raster = self.instruction.FindInstructionByType('raster')
02113         if raster:
02114             rasterId = raster.id 
02115         else:
02116             rasterId = None
02117 
02118         rasterName = 'None'
02119         if rasterId:
02120             rasterName = self.instruction[rasterId]['raster'].split('@')[0]
02121             
02122         mapId = self.instruction.FindInstructionByType('map').id
02123         self.itemLabels[mapId] = []
02124         self.itemLabels[mapId].append(self.itemLabelsDict['map'])
02125         self.itemLabels[mapId].append("raster: " + rasterName)
02126         if vectorId: 
02127             for map in self.instruction[vectorId]['list']:
02128                 self.itemLabels[mapId].append('vector: ' + map[0].split('@')[0])
02129             
02130     def UpdateLabel(self, itype, id):
02131         self.itemLabels[id] = []
02132         self.itemLabels[id].append(self.itemLabelsDict[itype])
02133         if itype == 'image':
02134             file = os.path.basename(self.instruction[id]['epsfile'])
02135             self.itemLabels[id].append(file)
02136         
02137     def OnSize(self, event):
02138         """!Init image size to match window size
02139         """
02140         # not zoom all when notebook page is changed
02141         if self.preview and self.parent.currentPage == 1 or not self.preview and self.parent.currentPage == 0:
02142             self.ZoomAll()
02143         self.OnIdle(None)
02144         event.Skip()
02145         
02146     def OnIdle(self, event):
02147         """!Only re-render a image during idle time instead of
02148         multiple times during resizing.
02149         """ 
02150         
02151         width, height = self.GetClientSize()
02152         # Make new off screen bitmap: this bitmap will always have the
02153         # current drawing in it, so it can be used to save the image
02154         # to a file, or whatever.
02155         self._buffer = wx.EmptyBitmap(width, height)
02156         # re-render image on idle
02157         self.resize = True
02158         
02159     def ScaleRect(self, rect, scale):
02160         """! Scale rectangle"""
02161         return wx.Rect(rect.GetLeft()*scale, rect.GetTop()*scale,
02162                        rect.GetSize()[0]*scale, rect.GetSize()[1]*scale)   
02163     
02164 def main():
02165     import gettext
02166     gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
02167     
02168     app = wx.PySimpleApp()
02169     wx.InitAllImageHandlers()
02170     frame = PsMapFrame()
02171     frame.Show()
02172     
02173     app.MainLoop()
02174 
02175 if __name__ == "__main__":
02176     main()