~ubuntu-branches/ubuntu/vivid/grass/vivid-proposed

« back to all changes in this revision

Viewing changes to gui/wxpython/gmodeler/frame.py

  • Committer: Package Import Robot
  • Author(s): Bas Couwenberg
  • Date: 2015-02-20 23:12:08 UTC
  • mfrom: (8.2.6 experimental)
  • Revision ID: package-import@ubuntu.com-20150220231208-1u6qvqm84v430b10
Tags: 7.0.0-1~exp1
* New upstream release.
* Update python-ctypes-ternary.patch to use if/else instead of and/or.
* Drop check4dev patch, rely on upstream check.
* Add build dependency on libpq-dev to grass-dev for libpq-fe.h.
* Drop patches applied upstream, refresh remaining patches.
* Update symlinks for images switched from jpg to png.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""!
 
1
"""
2
2
@package gmodeler.frame
3
3
 
4
4
@brief wxGUI Graphical Modeler for creating, editing, and managing models
11
11
 - frame::ItemPanel
12
12
 - frame::PythonPanel
13
13
 
14
 
(C) 2010-2012 by the GRASS Development Team
 
14
(C) 2010-2014 by the GRASS Development Team
15
15
 
16
16
This program is free software under the GNU General Public License
17
17
(>=v2). Read the file COPYING that comes with GRASS for details.
23
23
import sys
24
24
import time
25
25
import stat
26
 
import textwrap
27
26
import tempfile
28
27
import copy
29
28
import re
30
29
import random
31
30
 
32
 
if __name__ == "__main__":
33
 
    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'wxpython'))
34
 
 
35
31
import wx
36
32
from   wx.lib import ogl
37
33
import wx.lib.flatnotebook    as FN
38
34
 
39
35
from core                 import globalvar
 
36
from core.utils import _
40
37
from gui_core.widgets     import GNotebook
41
 
from gui_core.goutput     import GMConsole, PyStc
 
38
from core.gconsole        import GConsole, \
 
39
    EVT_CMD_RUN, EVT_CMD_DONE, EVT_CMD_PREPARE
 
40
from gui_core.goutput     import GConsoleWindow
42
41
from core.debug           import Debug
43
42
from core.gcmd            import GMessage, GException, GWarning, GError, RunCommand
44
43
from gui_core.dialogs     import GetImageHandlers
 
44
from gui_core.ghelp       import ShowAboutDialog
45
45
from gui_core.preferences import PreferencesBaseDialog
46
46
from core.settings        import UserSettings
47
 
from core.menudata        import MenuData
48
47
from gui_core.menu        import Menu
49
 
from gmodeler.menudata    import ModelerData
 
48
from gmodeler.menudata    import ModelerMenuData
50
49
from gui_core.forms       import GUI
51
50
from gmodeler.preferences import PreferencesDialog, PropertiesDialog
52
51
from gmodeler.toolbars    import ModelerToolbar
53
 
 
 
52
from core.giface import Notification
 
53
from gui_core.pystc       import PyStc
 
54
from gmodeler.giface import GraphicalModelerGrassInterface
54
55
from gmodeler.model       import *
55
56
from gmodeler.dialogs     import *
56
57
 
 
58
from grass.script.utils import try_remove
57
59
from grass.script import core as grass
58
60
 
59
61
class ModelFrame(wx.Frame):
60
 
    def __init__(self, parent, id = wx.ID_ANY,
61
 
                 title = _("GRASS GIS Graphical Modeler (experimental prototype)"), **kwargs):
62
 
        """!Graphical modeler main window
 
62
    def __init__(self, parent, giface, id = wx.ID_ANY,
 
63
                 title = _("GRASS GIS Graphical Modeler"), **kwargs):
 
64
        """Graphical modeler main window
63
65
        
64
 
        @param parent parent window
65
 
        @param id window id
66
 
        @param title window title
 
66
        :param parent: parent window
 
67
        :param id: window id
 
68
        :param title: window title
67
69
 
68
 
        @param kwargs wx.Frames' arguments
 
70
        :param kwargs: wx.Frames' arguments
69
71
        """
70
72
        self.parent = parent
 
73
        self._giface = giface
71
74
        self.searchDialog = None # module search dialog
72
75
        self.baseTitle = title
73
76
        self.modelFile = None    # loaded model
81
84
        
82
85
        wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
83
86
        self.SetName("Modeler")
84
 
        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
85
 
        
86
 
        self.menubar = Menu(parent = self, data = ModelerData())
87
 
        
 
87
        self.SetIcon(wx.Icon(os.path.join(globalvar.ICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
 
88
        
 
89
        self.menubar = Menu(parent = self, model = ModelerMenuData().GetModel(separators=True))        
88
90
        self.SetMenuBar(self.menubar)
89
91
        
90
92
        self.toolbar = ModelerToolbar(parent = self)
91
 
        self.SetToolBar(self.toolbar)
 
93
        # workaround for http://trac.wxwidgets.org/ticket/13888
 
94
        if sys.platform != 'darwin':
 
95
            self.SetToolBar(self.toolbar)
92
96
        
93
97
        self.statusbar = self.CreateStatusBar(number = 1)
94
98
        
99
103
        self.canvas = ModelCanvas(self)
100
104
        self.canvas.SetBackgroundColour(wx.WHITE)
101
105
        self.canvas.SetCursor(self.cursors["default"])
102
 
        
 
106
 
103
107
        self.model = Model(self.canvas)
104
108
        
105
109
        self.variablePanel = VariablePanel(parent = self)
108
112
        
109
113
        self.pythonPanel = PythonPanel(parent = self)
110
114
        
111
 
        self.goutput = GMConsole(parent = self, notebook = self.notebook)
 
115
        self._gconsole = GConsole(guiparent = self)
 
116
        self.goutput = GConsoleWindow(parent = self, gconsole = self._gconsole)
 
117
        self.goutput.showNotification.connect(lambda message: self.SetStatusText(message))
 
118
 
 
119
        # here events are binded twice
 
120
        self._gconsole.Bind(EVT_CMD_RUN,
 
121
            lambda event: self._switchPageHandler(event=event, notification=Notification.MAKE_VISIBLE))
 
122
        self._gconsole.Bind(EVT_CMD_DONE,
 
123
            lambda event: self._switchPageHandler(event=event, notification=Notification.RAISE_WINDOW))
 
124
        self.Bind(EVT_CMD_RUN, self.OnCmdRun)
 
125
        self._gconsole.Bind(EVT_CMD_DONE, self.OnCmdDone) # rewrite default method to avoid hiding progress bar
 
126
        self.Bind(EVT_CMD_PREPARE, self.OnCmdPrepare)
112
127
        
113
128
        self.notebook.AddPage(page = self.canvas, text=_('Model'), name = 'model')
114
129
        self.notebook.AddPage(page = self.itemPanel, text=_('Items'), name = 'items')
131
146
            self.goutput.SetSashPosition(int(self.GetSize()[1] * .75))
132
147
        
133
148
    def _layout(self):
134
 
        """!Do layout"""
 
149
        """Do layout"""
135
150
        sizer = wx.BoxSizer(wx.VERTICAL)
136
151
 
137
152
        sizer.Add(item = self.notebook, proportion = 1,
144
159
        self.Layout()
145
160
 
146
161
    def _addEvent(self, item):
147
 
        """!Add event to item"""
 
162
        """Add event to item"""
148
163
        evthandler = ModelEvtHandler(self.statusbar,
149
164
                                     self)
150
165
        evthandler.SetShape(item)
152
167
        item.SetEventHandler(evthandler)
153
168
 
154
169
    def _randomShift(self):
155
 
        """!Returns random value to shift layout"""
 
170
        """Returns random value to shift layout"""
156
171
        return random.randint(-self.randomness, self.randomness)
157
172
 
158
173
    def GetCanvas(self):
159
 
        """!Get canvas"""
 
174
        """Get canvas"""
160
175
        return self.canvas
161
176
    
162
177
    def GetModel(self):
163
 
        """!Get model"""
 
178
        """Get model"""
164
179
        return self.model
165
180
    
166
181
    def ModelChanged(self, changed = True):
167
 
        """!Update window title"""
 
182
        """Update window title"""
168
183
        self.modelChanged = changed
169
184
        
170
185
        if self.modelFile:
176
191
            self.SetTitle(self.baseTitle)
177
192
 
178
193
    def OnPageChanged(self, event):
179
 
        """!Page in notebook changed"""
 
194
        """Page in notebook changed"""
180
195
        page = event.GetSelection()
181
196
        if page == self.notebook.GetPageIndexByName('python'):
182
197
            if self.pythonPanel.IsEmpty():
190
205
        event.Skip()
191
206
 
192
207
    def OnVariables(self, event):
193
 
        """!Switch to variables page"""
 
208
        """Switch to variables page"""
194
209
        self.notebook.SetSelectionByName('variables')
195
210
        
196
211
    def OnRemoveItem(self, event):
197
 
        """!Remove shape
 
212
        """Remove shape
198
213
        """
199
214
        self.GetCanvas().RemoveSelected()
200
215
        
201
216
    def OnCanvasRefresh(self, event):
202
 
        """!Refresh canvas"""
 
217
        """Refresh canvas"""
203
218
        self.SetStatusText(_("Redrawing model..."), 0)
204
219
        self.GetCanvas().Refresh()
205
220
        self.SetStatusText("", 0)
206
221
 
207
222
    def OnCmdRun(self, event):
208
 
        """!Run command"""
 
223
        """Run command"""
209
224
        try:
210
225
            action = self.GetModel().GetItems()[event.pid]
211
226
            if hasattr(action, "task"):
214
229
            pass
215
230
        
216
231
    def OnCmdPrepare(self, event):
217
 
        """!Prepare for running command"""
 
232
        """Prepare for running command"""
218
233
        if not event.userData:
219
234
            return
220
235
        
222
237
                        params = event.userData['params'])
223
238
        
224
239
    def OnCmdDone(self, event):
225
 
        """!Command done (or aborted)"""
 
240
        """Command done (or aborted)"""
 
241
        self.goutput.GetProgressBar().SetValue(0)
226
242
        try:
227
243
            action = self.GetModel().GetItems()[event.pid]
228
244
            if hasattr(action, "task"):
229
245
                action.Update(running = True)
230
246
        except IndexError:
231
247
            pass
232
 
        
 
248
 
233
249
    def OnCloseWindow(self, event):
234
 
        """!Close window"""
 
250
        """Close window"""
235
251
        if self.modelChanged and \
236
252
                UserSettings.Get(group='manager', key='askOnQuit', subkey='enabled'):
237
253
            if self.modelFile:
265
281
        event.Skip()
266
282
        
267
283
    def OnPreferences(self, event):
268
 
        """!Open preferences dialog"""
269
 
        dlg = PreferencesDialog(parent = self)
 
284
        """Open preferences dialog"""
 
285
        dlg = PreferencesDialog(parent = self, giface = self._giface)
270
286
        dlg.CenterOnParent()
271
287
        
272
288
        dlg.ShowModal()
273
289
        self.canvas.Refresh()
274
290
        
275
291
    def OnHelp(self, event):
276
 
        """!Show help"""
277
 
        if self.parent and self.parent.GetName() == 'LayerManager':
278
 
            log = self.parent.GetLogWindow()
279
 
            log.RunCmd(['g.manual',
280
 
                        'entry=wxGUI.Modeler'])
281
 
        else:
282
 
            RunCommand('g.manual',
283
 
                       quiet = True,
284
 
                       entry = 'wxGUI.Modeler')
285
 
        
 
292
        """Show help"""
 
293
        self._giface.Help(entry = 'wxGUI.gmodeler')
 
294
 
286
295
    def OnModelProperties(self, event):
287
 
        """!Model properties dialog"""
 
296
        """Model properties dialog"""
288
297
        dlg = PropertiesDialog(parent = self)
289
298
        dlg.CentreOnParent()
290
299
        properties = self.model.GetProperties()
299
308
        dlg.Destroy()
300
309
        
301
310
    def OnDeleteData(self, event):
302
 
        """!Delete intermediate data"""
 
311
        """Delete intermediate data"""
303
312
        rast, vect, rast3d, msg = self.model.GetIntermediateData()
304
313
        
305
314
        if not rast and not vect and not rast3d:
317
326
            dlg.Destroy()
318
327
            
319
328
            if rast:
320
 
                self.goutput.RunCmd(['g.remove', 'rast=%s' %','.join(rast)])
 
329
                self._gconsole.RunCmd(['g.remove', '-f', 'type=raster',
 
330
                                       'name=%s' %','.join(rast)])
321
331
            if rast3d:
322
 
                self.goutput.RunCmd(['g.remove', 'rast3d=%s' %','.join(rast3d)])
 
332
                self._gconsole.RunCmd(['g.remove', '-f', 'type=raster_3d',
 
333
                                       'name=%s' %','.join(rast3d)])
323
334
            if vect:
324
 
                self.goutput.RunCmd(['g.remove', 'vect=%s' %','.join(vect)])
 
335
                self._gconsole.RunCmd(['g.remove', '-f', 'type=vector',
 
336
                                       'name=%s' %','.join(vect)])
325
337
            
326
338
            self.SetStatusText(_("%d maps deleted from current mapset") % \
327
339
                                 int(len(rast) + len(rast3d) + len(vect)))
330
342
        dlg.Destroy()
331
343
                
332
344
    def OnModelNew(self, event):
333
 
        """!Create new model"""
 
345
        """Create new model"""
334
346
        Debug.msg(4, "ModelFrame.OnModelNew():")
335
347
        
336
348
        # ask user to save current model
365
377
        self.modelChanged = False
366
378
        self.SetTitle(self.baseTitle)
367
379
        
 
380
    def GetModelFile(self, ext=True):
 
381
        """Get model file
 
382
 
 
383
        :param bool ext: False to avoid extension
 
384
        """
 
385
        if not self.modelFile:
 
386
            return ''
 
387
        if ext:
 
388
            return self.modelFile
 
389
        return os.path.splitext(self.modelFile)[0]
 
390
    
368
391
    def OnModelOpen(self, event):
369
 
        """!Load model from file"""
 
392
        """Load model from file"""
370
393
        filename = ''
371
394
        dlg = wx.FileDialog(parent = self, message=_("Choose model file"),
372
395
                            defaultDir = os.getcwd(),
391
414
                                 'actions' : self.model.GetNumItems(actionOnly = True) }, 0)
392
415
        
393
416
    def OnModelSave(self, event = None):
394
 
        """!Save model to file"""
 
417
        """Save model to file"""
395
418
        if self.modelFile and self.modelChanged:
396
419
            dlg = wx.MessageDialog(self, message=_("Model file <%s> already exists. "
397
420
                                                   "Do you want to overwrite this file?") % \
409
432
            self.OnModelSaveAs(None)
410
433
        
411
434
    def OnModelSaveAs(self, event):
412
 
        """!Create model to file as"""
 
435
        """Create model to file as"""
413
436
        filename = ''
414
437
        dlg = wx.FileDialog(parent = self,
415
438
                            message = _("Choose file to save current model"),
446
469
        self.SetStatusText(_('File <%s> saved') % self.modelFile, 0)
447
470
 
448
471
    def OnModelClose(self, event = None):
449
 
        """!Close model file"""
 
472
        """Close model file"""
450
473
        Debug.msg(4, "ModelFrame.OnModelClose(): file=%s" % self.modelFile)
451
474
        # ask user to save current model
452
475
        if self.modelFile and self.modelChanged:
477
500
        self.canvas.Refresh()
478
501
        
479
502
    def OnRunModel(self, event):
480
 
        """!Run entire model"""
481
 
        self.model.Run(self.goutput, self.OnDone, parent = self)
 
503
        """Run entire model"""
 
504
        self.model.Run(self._gconsole, self.OnDone, parent = self)
482
505
        
483
506
    def OnDone(self, cmd, returncode):
484
 
        """!Computation finished"""
 
507
        """Computation finished
 
508
 
 
509
        .. todo::
 
510
            not called -- must be fixed
 
511
        """
485
512
        self.SetStatusText('', 0)
486
513
        # restore original files
487
514
        if hasattr(self.model, "fileInput"):
498
525
            del self.model.fileInput
499
526
        
500
527
    def OnValidateModel(self, event, showMsg = True):
501
 
        """!Validate entire model"""
 
528
        """Validate entire model"""
502
529
        if self.model.GetNumItems() < 1:
503
530
            GMessage(parent = self, 
504
531
                     message = _('Model is empty. Nothing to validate.'))
517
544
                     message = _('Model is valid.'))
518
545
    
519
546
    def OnExportImage(self, event):
520
 
        """!Export model to image (default image)
 
547
        """Export model to image (default image)
521
548
        """
522
549
        xminImg = 0
523
550
        xmaxImg = 0
580
607
        dlg.Destroy()
581
608
        
582
609
    def OnExportPython(self, event = None, text = None):
583
 
        """!Export model to Python script"""
 
610
        """Export model to Python script"""
584
611
        filename = self.pythonPanel.SaveAs(force = True)
585
612
        self.SetStatusText(_("Model exported to <%s>") % filename)
586
613
 
587
614
    def OnDefineRelation(self, event):
588
 
        """!Define relation between data and action items"""
 
615
        """Define relation between data and action items"""
589
616
        self.canvas.SetCursor(self.cursors["cross"])
590
617
        self.defineRelation = { 'from' : None,
591
618
                                'to'   : None }
592
619
        
593
620
    def OnDefineLoop(self, event):
594
 
        """!Define new loop in the model"""
 
621
        """Define new loop in the model
 
622
 
 
623
        .. todo::
 
624
            move to ModelCanvas?
 
625
        """
595
626
        self.ModelChanged()
596
627
        
597
628
        width, height = self.canvas.GetSize()
606
637
        self.canvas.Refresh()
607
638
        
608
639
    def OnDefineCondition(self, event):
609
 
        """!Define new condition in the model"""
 
640
        """Define new condition in the model
 
641
 
 
642
        .. todo::
 
643
            move to ModelCanvas?
 
644
        """
610
645
        self.ModelChanged()
611
646
        
612
647
        width, height = self.canvas.GetSize()
621
656
        self.canvas.Refresh()
622
657
    
623
658
    def OnAddAction(self, event):
624
 
        """!Add action to model"""
 
659
        """Add action to model"""
625
660
        if self.searchDialog is None:
626
661
            self.searchDialog = ModelSearchDialog(self)
627
662
            self.searchDialog.CentreOnParent()
639
674
        
640
675
        # add action to canvas
641
676
        x, y = self.canvas.GetNewShapePos()
 
677
        label, comment = self.searchDialog.GetLabel()
642
678
        action = ModelAction(self.model, cmd = cmd,
643
679
                             x = x + self._randomShift(),
644
680
                             y = y + self._randomShift(),
645
 
                             id = self.model.GetNextId())
 
681
                             id = self.model.GetNextId(), label = label, comment = comment)
646
682
        overwrite = self.model.GetProperties().get('overwrite', None)
647
683
        if overwrite is not None:
648
684
            action.GetTask().set_flag('overwrite', overwrite)
660
696
        # show properties dialog
661
697
        win = action.GetPropDialog()
662
698
        if not win:
663
 
            if action.IsValid():
 
699
            cmdLength = len(action.GetLog(string=False))
 
700
            if cmdLength > 1 and action.IsValid():
664
701
                self.GetOptData(dcmd = action.GetLog(string = False), layer = action,
665
702
                                params = action.GetParams(), propwin = None)
666
703
            else:
667
 
                GUI(parent = self, show = True).ParseCommand(action.GetLog(string = False),
668
 
                                                             completed = (self.GetOptData, action, action.GetParams()))
 
704
                gmodule = GUI(parent = self, show = True,
 
705
                              giface = GraphicalModelerGrassInterface(self.model))
 
706
                gmodule.ParseCommand(action.GetLog(string = False),
 
707
                                     completed = (self.GetOptData, action, action.GetParams()))
669
708
        elif win and not win.IsShown():
670
709
            win.Show()
671
710
        
673
712
            win.Raise()
674
713
        
675
714
    def OnAddData(self, event):
676
 
        """!Add data item to model
 
715
        """Add data item to model
677
716
        """
678
717
        # add action to canvas
679
718
        width, height = self.canvas.GetSize()
698
737
        self.model.AddItem(data)
699
738
        
700
739
        self.canvas.Refresh()
701
 
        
702
 
        
703
 
    def OnHelp(self, event):
704
 
        """!Display manual page"""
705
 
        grass.run_command('g.manual',
706
 
                          entry = 'wxGUI.Modeler')
 
740
 
 
741
    def OnAddComment(self, event):
 
742
        """Add comment to the model"""
 
743
        dlg = wx.TextEntryDialog(parent = self, message = _("Comment:"), caption = _("Add comment"),
 
744
                                 style = wx.OK | wx.CANCEL | wx.CENTRE | wx.TE_MULTILINE)
 
745
        if dlg.ShowModal() == wx.ID_OK:
 
746
            comment = dlg.GetValue()
 
747
            if not comment:
 
748
                GError(_("Empty comment. Nothing to add to the model."), parent = self)
 
749
            else:
 
750
                x, y = self.canvas.GetNewShapePos()
 
751
                commentObj = ModelComment(self.model, x = x + self._randomShift(), y = y + self._randomShift(),
 
752
                                          id = self.model.GetNextId(), label = comment)
 
753
                self.canvas.diagram.AddShape(commentObj)
 
754
                commentObj.Show(True)
 
755
                self._addEvent(commentObj)
 
756
                self.model.AddItem(commentObj)
 
757
                
 
758
                self.canvas.Refresh()
 
759
                self.ModelChanged()
 
760
        
 
761
        dlg.Destroy()
 
762
        
 
763
    def _switchPageHandler(self, event, notification):
 
764
        self._switchPage(notification=notification)
 
765
        event.Skip()
 
766
 
 
767
    def _switchPage(self, notification):
 
768
        """Manages @c 'output' notebook page according to event notification."""
 
769
        if notification == Notification.HIGHLIGHT:
 
770
            self.notebook.HighlightPageByName('output')
 
771
        if notification == Notification.MAKE_VISIBLE:
 
772
            self.notebook.SetSelectionByName('output')
 
773
        if notification == Notification.RAISE_WINDOW:
 
774
            self.notebook.SetSelectionByName('output')
 
775
            self.SetFocus()
 
776
            self.Raise()
707
777
 
708
778
    def OnAbout(self, event):
709
 
        """!Display About window"""
710
 
        info = wx.AboutDialogInfo()
711
 
 
712
 
        info.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
713
 
        info.SetName(_('wxGUI Graphical Modeler'))
714
 
        info.SetWebSite('http://grass.osgeo.org')
715
 
        year = grass.version()['date']
716
 
        info.SetDescription(_('(C) 2010-%s by the GRASS Development Team\n\n') % year + 
717
 
                            '\n'.join(textwrap.wrap(_('This program is free software under the GNU General Public License'
718
 
                                                      '(>=v2). Read the file COPYING that comes with GRASS for details.'), 75)))
719
 
        
720
 
        wx.AboutBox(info)
 
779
        """Display About window"""
 
780
        ShowAboutDialog(prgName=_('wxGUI Graphical Modeler'), startYear='2010')
721
781
        
722
782
    def GetOptData(self, dcmd, layer, params, propwin):
723
 
        """!Process action data"""
 
783
        """Process action data"""
724
784
        if params: # add data items
725
785
            width, height = self.canvas.GetSize()
726
786
            x = width/2 - 200 + self._randomShift()
727
787
            y = height/2 + self._randomShift()
728
788
            for p in params['params']:
729
 
                if p.get('prompt', '') in ('raster', 'vector', 'raster3d') and \
 
789
                if p.get('prompt', '') in ('raster', 'vector', 'raster_3d') and \
730
790
                        (p.get('value', None) or \
731
791
                             (p.get('age', 'old') != 'old' and p.get('required', 'no') == 'yes')):
732
792
                    data = layer.FindData(p.get('name', ''))
779
839
        self.SetStatusText(layer.GetLog(), 0)
780
840
        
781
841
    def AddLine(self, rel):
782
 
        """!Add connection between model objects
 
842
        """Add connection between model objects
783
843
        
784
 
        @param rel relation
 
844
        :param rel: relation
785
845
        """
786
846
        fromShape = rel.GetFrom()
787
847
        toShape   = rel.GetTo()
806
866
        rel.Show(True)
807
867
        
808
868
    def LoadModelFile(self, filename):
809
 
        """!Load model definition stored in GRASS Model XML file (gxm)
 
869
        """Load model definition stored in GRASS Model XML file (gxm)
810
870
        """
811
871
        try:
812
872
            self.model.LoadModel(filename)
813
 
        except GException, e:
 
873
        except GException as e:
814
874
            GError(parent = self,
815
875
                   message = _("Reading model file <%s> failed.\n"
816
 
                               "Invalid file, unable to parse XML document.") % filename)
 
876
                               "Invalid file, unable to parse XML document.\n\n%s") % \
 
877
                       (filename, e),
 
878
                   showTraceback = False)
 
879
            return
817
880
        
818
881
        self.modelFile = filename
819
882
        self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.modelFile))
854
917
            # connect items in the condition
855
918
            self.DefineCondition(item)
856
919
        
 
920
        # load comments
 
921
        for item in self.model.GetItems(objType = ModelComment):
 
922
            self._addEvent(item)
 
923
            self.canvas.diagram.AddShape(item)
 
924
            item.Show(True)
 
925
        
857
926
        # load variables
858
927
        self.variablePanel.Update()
859
928
        self.itemPanel.Update()
867
936
        self.canvas.Refresh(True)
868
937
        
869
938
    def WriteModelFile(self, filename):
870
 
        """!Save model to model file, recover original file on error.
 
939
        """Save model to model file, recover original file on error.
871
940
        
872
 
        @return True on success
873
 
        @return False on failure
 
941
        :return: True on success
 
942
        :return: False on failure
874
943
        """
875
944
        self.ModelChanged(False)
876
945
        tmpfile = tempfile.TemporaryFile(mode='w+b')
898
967
        return True
899
968
    
900
969
    def DefineLoop(self, loop):
901
 
        """!Define loop with given list of items"""
 
970
        """Define loop with given list of items"""
902
971
        parent = loop
903
 
        items = loop.GetItems()
 
972
        items = loop.GetItems(self.GetModel().GetItems())
904
973
        if not items:
905
974
            return
906
975
        
921
990
            parent = item
922
991
        
923
992
        # close loop
924
 
        item = loop.GetItems()[-1]
 
993
        item = items[-1]
925
994
        rel = ModelRelation(parent = self, fromShape = item, toShape = loop)
926
995
        loop.AddRelation(rel)
927
996
        self.AddLine(rel)
942
1011
        self.canvas.Refresh()
943
1012
 
944
1013
    def DefineCondition(self, condition):
945
 
        """!Define if-else statement with given list of items"""
 
1014
        """Define if-else statement with given list of items"""
 
1015
        items = condition.GetItems(self.model.GetItems(objType=ModelAction))
 
1016
        if not items['if'] and not items['else']:
 
1017
            return
 
1018
        
946
1019
        parent = condition
947
 
        items = condition.GetItems()
948
 
        if not items['if'] and not items['else']:
949
 
            return
950
1020
        
951
1021
        # remove defined relations first
952
1022
        for rel in condition.GetRelations():
973
1043
        self.canvas.Refresh()
974
1044
        
975
1045
class ModelCanvas(ogl.ShapeCanvas):
976
 
    """!Canvas where model is drawn"""
 
1046
    """Canvas where model is drawn"""
977
1047
    def __init__(self, parent):
978
1048
        self.parent = parent
979
1049
        ogl.OGLInitialize()
985
1055
        
986
1056
        self.SetScrollbars(20, 20, 2000/20, 2000/20)
987
1057
        
988
 
        self.Bind(wx.EVT_CHAR,  self.OnChar)
989
 
        
990
 
    def OnChar(self, event):
991
 
        """!Key pressed"""
 
1058
        self.Bind(wx.EVT_KEY_UP,  self.OnKeyUp)
 
1059
        self.Bind(wx.EVT_LEFT_DOWN,  self.OnLeftDown)
 
1060
 
 
1061
    def OnKeyUp(self, event):
 
1062
        """Key pressed"""
992
1063
        kc = event.GetKeyCode()
993
 
        diagram = self.GetDiagram()
994
1064
        if kc == wx.WXK_DELETE:
995
1065
            self.RemoveSelected()
996
1066
        
 
1067
    def OnLeftDown(self, evt):
 
1068
        self.SetFocus()
 
1069
        evt.Skip()
 
1070
 
997
1071
    def RemoveSelected(self):
998
 
        """!Remove selected shapes"""
 
1072
        """Remove selected shapes"""
999
1073
        self.parent.ModelChanged()
1000
1074
        
1001
1075
        diagram = self.GetDiagram()
1003
1077
        self.RemoveShapes(shapes)
1004
1078
 
1005
1079
    def RemoveShapes(self, shapes):
1006
 
        """!Removes shapes"""
 
1080
        """Removes shapes"""
1007
1081
        self.parent.ModelChanged()
1008
1082
        diagram = self.GetDiagram()
1009
1083
        for shape in shapes:
1021
1095
        self.Refresh()
1022
1096
        
1023
1097
    def GetNewShapePos(self):
1024
 
        """!Determine optimal position for newly added object
 
1098
        """Determine optimal position for newly added object
1025
1099
 
1026
 
        @return x,y
 
1100
        :return: x,y
1027
1101
        """
1028
1102
        xNew, yNew = map(lambda x: x / 2, self.GetSize())
1029
1103
        diagram = self.GetDiagram()
1035
1109
                yNew += yBox * 3
1036
1110
 
1037
1111
        return xNew, yNew
1038
 
    
 
1112
 
 
1113
    def GetShapesSelected(self):
 
1114
        """Get list of selected shapes"""
 
1115
        selected = list()
 
1116
        diagram = self.GetDiagram()
 
1117
        for shape in diagram.GetShapeList():
 
1118
            if shape.Selected():
 
1119
                selected.append(shape)
 
1120
 
 
1121
        return selected
 
1122
 
1039
1123
class ModelEvtHandler(ogl.ShapeEvtHandler):
1040
 
    """!Model event handler class"""
 
1124
    """Model event handler class"""
1041
1125
    def __init__(self, log, frame):
1042
1126
        ogl.ShapeEvtHandler.__init__(self)
1043
1127
        self.log = log
1045
1129
        self.x = self.y = None
1046
1130
        
1047
1131
    def OnLeftClick(self, x, y, keys = 0, attachment = 0):
1048
 
        """!Left mouse button pressed -> select item & update statusbar"""
 
1132
        """Left mouse button pressed -> select item & update statusbar"""
1049
1133
        shape = self.GetShape()
1050
1134
        canvas = shape.GetCanvas()
1051
1135
        dc = wx.ClientDC(canvas)
 
1136
        
1052
1137
        # probably does nothing, removed from wxPython 2.9
1053
1138
        # canvas.PrepareDC(dc)
1054
1139
        
1079
1164
                del self.frame.defineRelation
1080
1165
        
1081
1166
        # select object
1082
 
        self._onSelectShape(shape)
 
1167
        self._onSelectShape(shape, append = True if keys == 1 else False)
1083
1168
        
1084
1169
        if hasattr(shape, "GetLog"):
1085
1170
            self.log.SetStatusText(shape.GetLog(), 0)
1087
1172
            self.log.SetStatusText('', 0)
1088
1173
        
1089
1174
    def OnLeftDoubleClick(self, x, y, keys = 0, attachment = 0):
1090
 
        """!Left mouse button pressed (double-click) -> show properties"""
 
1175
        """Left mouse button pressed (double-click) -> show properties"""
1091
1176
        self.OnProperties()
1092
1177
        
1093
1178
    def OnProperties(self, event = None):
1094
 
        """!Show properties dialog"""
 
1179
        """Show properties dialog"""
1095
1180
        self.frame.ModelChanged()
1096
1181
        shape = self.GetShape()
1097
1182
        if isinstance(shape, ModelAction):
1098
 
            module = GUI(parent = self.frame, show = True).ParseCommand(shape.GetLog(string = False),
1099
 
                                                                        completed = (self.frame.GetOptData, shape, shape.GetParams()))
 
1183
            gmodule = GUI(parent = self.frame, show = True,
 
1184
                          giface = GraphicalModelerGrassInterface(self.frame.GetModel()))
 
1185
            gmodule.ParseCommand(shape.GetLog(string = False),
 
1186
                                 completed = (self.frame.GetOptData, shape, shape.GetParams()))
1100
1187
        
1101
1188
        elif isinstance(shape, ModelData):
1102
1189
            dlg = ModelDataDialog(parent = self.frame, shape = shape)
1108
1195
            dlg = ModelLoopDialog(parent = self.frame, shape = shape)
1109
1196
            dlg.CentreOnParent()
1110
1197
            if dlg.ShowModal() == wx.ID_OK:
1111
 
                shape.SetText(dlg.GetCondition())
 
1198
                shape.SetLabel(dlg.GetCondition())
 
1199
                model = self.frame.GetModel()
 
1200
                ids = dlg.GetItems()
1112
1201
                alist = list()
1113
 
                ids = dlg.GetItems()
1114
1202
                for aId in ids['unchecked']:
1115
 
                    action = self.frame.GetModel().GetItem(aId)
1116
 
                    action.UnSetBlock(shape)
 
1203
                    action = model.GetItem(aId, objType=ModelAction)
 
1204
                    if action:
 
1205
                        action.UnSetBlock(shape)
1117
1206
                for aId in ids['checked']:
1118
 
                    action = self.frame.GetModel().GetItem(aId)
1119
 
                    action.SetBlock(shape)
 
1207
                    action = model.GetItem(aId, objType=ModelAction)
1120
1208
                    if action:
1121
 
                        alist.append(action)
 
1209
                        action.SetBlock(shape)
 
1210
                        alist.append(aId)
1122
1211
                shape.SetItems(alist)
1123
1212
                self.frame.DefineLoop(shape)
1124
1213
                self.frame.SetStatusText(shape.GetLog(), 0)
1130
1219
            dlg = ModelConditionDialog(parent = self.frame, shape = shape)
1131
1220
            dlg.CentreOnParent()
1132
1221
            if dlg.ShowModal() == wx.ID_OK:
1133
 
                shape.SetText(dlg.GetCondition())
 
1222
                shape.SetLabel(dlg.GetCondition())
 
1223
                model = self.frame.GetModel()
1134
1224
                ids = dlg.GetItems()
1135
1225
                for b in ids.keys():
1136
1226
                    alist = list()
1137
1227
                    for aId in ids[b]['unchecked']:
1138
 
                        action = self.frame.GetModel().GetItem(aId)
 
1228
                        action = model.GetItem(aId, objType=ModelAction)
1139
1229
                        action.UnSetBlock(shape)
1140
1230
                    for aId in ids[b]['checked']:
1141
 
                        action = self.frame.GetModel().GetItem(aId)
 
1231
                        action = model.GetItem(aId, objType=ModelAction)
1142
1232
                        action.SetBlock(shape)
1143
1233
                        if action:
1144
 
                            alist.append(action)
 
1234
                            alist.append(aId)
1145
1235
                    shape.SetItems(alist, branch = b)
1146
1236
                self.frame.DefineCondition(shape)
1147
1237
            self.frame.GetCanvas().Refresh()
1149
1239
            dlg.Destroy()
1150
1240
                   
1151
1241
    def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0):
1152
 
        """!Drag shape (begining)"""
 
1242
        """Drag shape (begining)"""
1153
1243
        self.frame.ModelChanged()
1154
1244
        if self._previousHandler:
1155
1245
            self._previousHandler.OnBeginDragLeft(x, y, keys, attachment)
1156
1246
        
1157
1247
    def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
1158
 
        """!Drag shape (end)"""
 
1248
        """Drag shape (end)"""
1159
1249
        if self._previousHandler:
1160
1250
            self._previousHandler.OnEndDragLeft(x, y, keys, attachment)
1161
1251
        
1170
1260
                self.frame.DefineLoop(mo)
1171
1261
            elif isinstance(mo, ModelCondition):
1172
1262
                self.frame.DefineCondition(mo)
1173
 
        
 
1263
 
 
1264
        shape = self.GetShape()
 
1265
        canvas = shape.GetCanvas()
 
1266
        canvas.Refresh()
 
1267
 
1174
1268
    def OnEndSize(self, x, y):
1175
 
        """!Resize shape"""
 
1269
        """Resize shape"""
1176
1270
        self.frame.ModelChanged()
1177
1271
        if self._previousHandler:
1178
1272
            self._previousHandler.OnEndSize(x, y)
1179
1273
        
1180
1274
    def OnRightClick(self, x, y, keys = 0, attachment = 0):
1181
 
        """!Right click -> pop-up menu"""
 
1275
        """Right click -> pop-up menu"""
1182
1276
        if not hasattr (self, "popupID"):
1183
1277
            self.popupID = dict()
1184
1278
            for key in ('remove', 'enable', 'addPoint',
1185
 
                        'delPoint', 'intermediate', 'props', 'id'):
 
1279
                        'delPoint', 'intermediate', 'props', 'id',
 
1280
                        'label', 'comment'):
1186
1281
                self.popupID[key] = wx.NewId()
1187
1282
        
1188
1283
        # record coordinates
1203
1298
            else:
1204
1299
                popupMenu.Append(self.popupID['enable'], text=_('Enable'))
1205
1300
                self.frame.Bind(wx.EVT_MENU, self.OnEnable, id = self.popupID['enable'])
1206
 
        
 
1301
        if isinstance(shape, ModelAction) or isinstance(shape, ModelComment):
 
1302
            popupMenu.AppendSeparator()
 
1303
        if isinstance(shape, ModelAction):
 
1304
            popupMenu.Append(self.popupID['label'], text=_('Set label'))
 
1305
            self.frame.Bind(wx.EVT_MENU, self.OnSetLabel, id = self.popupID['label'])
 
1306
        if isinstance(shape, ModelAction) or isinstance(shape, ModelComment):
 
1307
            popupMenu.Append(self.popupID['comment'], text=_('Set comment'))
 
1308
            self.frame.Bind(wx.EVT_MENU, self.OnSetComment, id = self.popupID['comment'])
 
1309
 
1207
1310
        if isinstance(shape, ModelRelation):
1208
1311
            popupMenu.AppendSeparator()
1209
1312
            popupMenu.Append(self.popupID['addPoint'], text=_('Add control point'))
1233
1336
        popupMenu.Destroy()
1234
1337
 
1235
1338
    def OnDisable(self, event):
1236
 
        """!Disable action"""
 
1339
        """Disable action"""
1237
1340
        self._onEnable(False)
1238
1341
        
1239
1342
    def OnEnable(self, event):
1240
 
        """!Disable action"""
 
1343
        """Disable action"""
1241
1344
        self._onEnable(True)
1242
1345
        
1243
1346
    def _onEnable(self, enable):
1245
1348
        shape.Enable(enable)
1246
1349
        self.frame.ModelChanged()
1247
1350
        self.frame.canvas.Refresh()
1248
 
        
1249
 
    def _onSelectShape(self, shape):
 
1351
 
 
1352
    def OnSetLabel(self, event):
 
1353
        shape = self.GetShape()
 
1354
        dlg = wx.TextEntryDialog(parent = self.frame, message = _("Label:"), caption = _("Set label"),
 
1355
                                 defaultValue = shape.GetLabel())
 
1356
        if dlg.ShowModal() == wx.ID_OK:
 
1357
            label = dlg.GetValue()
 
1358
            shape.SetLabel(label)
 
1359
            self.frame.ModelChanged()
 
1360
            self.frame.itemPanel.Update()
 
1361
            self.frame.canvas.Refresh()
 
1362
        dlg.Destroy()
 
1363
    
 
1364
    def OnSetComment(self, event):
 
1365
        shape = self.GetShape()
 
1366
        dlg = wx.TextEntryDialog(parent = self.frame, message = _("Comment:"), caption = _("Set comment"),
 
1367
                                 defaultValue = shape.GetComment(), style = wx.OK | wx.CANCEL | wx.CENTRE | wx.TE_MULTILINE)
 
1368
        if dlg.ShowModal() == wx.ID_OK:
 
1369
            comment = dlg.GetValue()
 
1370
            shape.SetComment(comment)
 
1371
            self.frame.ModelChanged()
 
1372
        dlg.Destroy()
 
1373
 
 
1374
    def _onSelectShape(self, shape, append=False):
1250
1375
        canvas = shape.GetCanvas()
1251
1376
        dc = wx.ClientDC(canvas)
1252
1377
        
1257
1382
            shapeList = canvas.GetDiagram().GetShapeList()
1258
1383
            toUnselect = list()
1259
1384
            
1260
 
            for s in shapeList:
1261
 
                if s.Selected():
1262
 
                    toUnselect.append(s)
 
1385
            if not append:
 
1386
                for s in shapeList:
 
1387
                    if s.Selected():
 
1388
                        toUnselect.append(s)
1263
1389
            
1264
1390
            shape.Select(True, dc)
1265
1391
            
1269
1395
        canvas.Refresh(False)
1270
1396
        
1271
1397
    def OnAddPoint(self, event):
1272
 
        """!Add control point"""
 
1398
        """Add control point"""
1273
1399
        shape = self.GetShape()
1274
1400
        shape.InsertLineControlPoint(point = wx.RealPoint(self.x, self.y))
1275
1401
        shape.ResetShapes()
1278
1404
        self.frame.canvas.Refresh()
1279
1405
        
1280
1406
    def OnRemovePoint(self, event):
1281
 
        """!Remove control point"""
 
1407
        """Remove control point"""
1282
1408
        shape = self.GetShape()
1283
1409
        shape.DeleteLineControlPoint()
1284
1410
        shape.Select(False)
1287
1413
        self.frame.canvas.Refresh()
1288
1414
        
1289
1415
    def OnIntermediate(self, event):
1290
 
        """!Mark data as intermediate"""
 
1416
        """Mark data as intermediate"""
1291
1417
        self.frame.ModelChanged()
1292
1418
        shape = self.GetShape()
1293
1419
        shape.SetIntermediate(event.IsChecked())
1294
1420
        self.frame.canvas.Refresh()
1295
1421
 
1296
1422
    def OnRemove(self, event):
1297
 
        """!Remove shape
 
1423
        """Remove shape
1298
1424
        """
1299
1425
        self.frame.GetCanvas().RemoveShapes([self.GetShape()])
1300
1426
        self.frame.itemPanel.Update()
1301
 
        
 
1427
       
1302
1428
class VariablePanel(wx.Panel):
1303
1429
    def __init__(self, parent, id = wx.ID_ANY,
1304
1430
                 **kwargs):
1305
 
        """!Manage model variables panel
 
1431
        """Manage model variables panel
1306
1432
        """
1307
1433
        self.parent = parent
1308
1434
        
1313
1439
        
1314
1440
        self.list = VariableListCtrl(parent = self,
1315
1441
                                     columns = [_("Name"), _("Data type"),
1316
 
                                                _("Default value"), _("Description")])
 
1442
                                                _("Default value"), _("Description")],
 
1443
                                     frame = self.parent)
1317
1444
        
1318
1445
        # add new category
1319
1446
        self.addBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1326
1453
                                         _("string"),
1327
1454
                                         _("raster"),
1328
1455
                                         _("vector"),
 
1456
                                         _("region"),
1329
1457
                                         _("mapset"),
1330
1458
                                         _("file")])
1331
1459
        self.type.SetSelection(2) # string
1346
1474
        self._layout()
1347
1475
 
1348
1476
    def _layout(self):
1349
 
        """!Layout dialog"""
 
1477
        """Layout dialog"""
1350
1478
        listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL)
1351
1479
        listSizer.Add(item = self.list, proportion = 1,
1352
1480
                      flag = wx.EXPAND)
1397
1525
        mainSizer.Fit(self)
1398
1526
        
1399
1527
    def OnText(self, event):
1400
 
        """!Text entered"""
 
1528
        """Text entered"""
1401
1529
        if self.name.GetValue():
1402
1530
            self.btnAdd.Enable()
1403
1531
        else:
1404
1532
            self.btnAdd.Enable(False)
1405
1533
    
1406
1534
    def OnAdd(self, event):
1407
 
        """!Add new variable to the list"""
 
1535
        """Add new variable to the list"""
1408
1536
        msg = self.list.Append(self.name.GetValue(),
1409
1537
                               self.type.GetStringSelection(),
1410
1538
                               self.value.GetValue(),
1422
1550
            self.UpdateModelVariables()
1423
1551
        
1424
1552
    def UpdateModelVariables(self):
1425
 
        """!Update model variables"""
 
1553
        """Update model variables"""
1426
1554
        variables = dict()
1427
1555
        for values in self.list.GetData().itervalues():
1428
1556
            name = values[0]
1436
1564
        self.parent.ModelChanged()
1437
1565
 
1438
1566
    def Update(self):
1439
 
        """!Reload list of variables"""
 
1567
        """Reload list of variables"""
1440
1568
        self.list.OnReload(None)
1441
1569
        
1442
1570
    def Reset(self):
1443
 
        """!Remove all variables"""
 
1571
        """Remove all variables"""
1444
1572
        self.list.DeleteAllItems()
1445
1573
        self.parent.GetModel().SetVariables([])
1446
1574
        
1447
1575
class ItemPanel(wx.Panel):
1448
1576
    def __init__(self, parent, id = wx.ID_ANY,
1449
1577
                 **kwargs):
1450
 
        """!Manage model items
 
1578
        """Manage model items
1451
1579
        """
1452
1580
        self.parent = parent
1453
1581
        
1457
1585
                                    label=" %s " % _("List of items - right-click to delete"))
1458
1586
        
1459
1587
        self.list = ItemListCtrl(parent = self,
1460
 
                                 columns = [_("ID"), _("Name"), _("In block"),
1461
 
                                            _("Command / Condition")])
 
1588
                                 columns = [_("Label"), _("In loop"),
 
1589
                                            _("Command")],
 
1590
                                 columnsNotEditable = [1, 2],
 
1591
                                 frame = self.parent)
 
1592
        
 
1593
        self.btnMoveUp = wx.Button(parent=self, id=wx.ID_UP)
 
1594
        self.btnMoveDown = wx.Button(parent=self, id=wx.ID_DOWN)
 
1595
        
 
1596
        self.btnMoveUp.Bind(wx.EVT_BUTTON, self.OnMoveItemsUp)
 
1597
        self.btnMoveDown.Bind(wx.EVT_BUTTON, self.OnMoveItemsDown)
1462
1598
        
1463
1599
        self._layout()
1464
1600
 
1465
1601
    def _layout(self):
1466
 
        """!Layout dialog"""
 
1602
        """Layout dialog"""
1467
1603
        listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL)
1468
1604
        listSizer.Add(item = self.list, proportion = 1,
1469
1605
                      flag = wx.EXPAND)
1470
1606
        
1471
 
        mainSizer = wx.BoxSizer(wx.VERTICAL)
 
1607
        manageSizer = wx.BoxSizer(wx.VERTICAL)
 
1608
        manageSizer.Add(item=self.btnMoveUp, border = 5, flag = wx.ALL)
 
1609
        manageSizer.Add(item=self.btnMoveDown, border = 5,
 
1610
                        flag = wx.LEFT | wx.RIGHT)
 
1611
        
 
1612
        mainSizer = wx.BoxSizer(wx.HORIZONTAL)
1472
1613
        mainSizer.Add(item = listSizer, proportion = 1,
1473
 
                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
1474
 
        
 
1614
                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 3)
 
1615
        mainSizer.Add(item = manageSizer, proportion = 0,
 
1616
                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 3)
 
1617
 
1475
1618
        self.SetSizer(mainSizer)
1476
1619
        mainSizer.Fit(self)
1477
1620
        
1478
1621
    def Update(self):
1479
 
        """!Reload list of variables"""
 
1622
        """Reload list of variables"""
1480
1623
        self.list.OnReload(None)
1481
1624
 
 
1625
    def _getSelectedItems(self):
 
1626
        """Get list of selected items, indeces start at 0"""
 
1627
        items = []
 
1628
        current = -1
 
1629
        while True:
 
1630
            next = self.list.GetNextSelected(current)
 
1631
            if next == -1:
 
1632
                break
 
1633
            items.append(next)
 
1634
            current = next
 
1635
        
 
1636
        if not items:
 
1637
            GMessage(_("No items to selected."), parent = self)
 
1638
            
 
1639
        return items
 
1640
    
 
1641
    def OnMoveItemsUp(self, event):
 
1642
        """Item moved up, update action ids"""
 
1643
        items = self._getSelectedItems()
 
1644
        if not items:
 
1645
            return
 
1646
        self.list.MoveItems(items, up = True)
 
1647
        self.parent.GetCanvas().Refresh()
 
1648
        self.parent.ModelChanged()
 
1649
 
 
1650
    def OnMoveItemsDown(self, event):
 
1651
        """Item moved up, update action ids"""
 
1652
        items = self._getSelectedItems()
 
1653
        if not items:
 
1654
            return
 
1655
        self.list.MoveItems(items, up = False)
 
1656
        self.parent.GetCanvas().Refresh()
 
1657
        self.parent.ModelChanged()
 
1658
    
1482
1659
class PythonPanel(wx.Panel):
1483
1660
    def __init__(self, parent, id = wx.ID_ANY,
1484
1661
                 **kwargs):
1485
 
        """!Model as python script
 
1662
        """Model as python script
1486
1663
        """
1487
1664
        self.parent = parent
1488
1665
        
1533
1710
        self.SetSizer(sizer)
1534
1711
 
1535
1712
    def OnRun(self, event):
1536
 
        """!Run Python script"""
 
1713
        """Run Python script"""
1537
1714
        self.filename = grass.tempfile()
1538
1715
        try:
1539
1716
            fd = open(self.filename, "w")
1540
1717
            fd.write(self.body.GetText())
1541
 
        except IOError, e:
 
1718
        except IOError as e:
1542
1719
            GError(_("Unable to launch Python script. %s") % e,
1543
1720
                   parent = self)
1544
1721
            return
1547
1724
            mode = stat.S_IMODE(os.lstat(self.filename)[stat.ST_MODE])
1548
1725
            os.chmod(self.filename, mode | stat.S_IXUSR)
1549
1726
        
1550
 
        self.parent.goutput.RunCmd([fd.name], switchPage = True,
1551
 
                                   skipInterface = True, onDone = self.OnDone)
 
1727
        self.parent._gconsole.RunCmd([fd.name], skipInterface=True, onDone=self.OnDone)
1552
1728
        
1553
1729
        event.Skip()
1554
1730
 
1555
1731
    def OnDone(self, cmd, returncode):
1556
 
        """!Python script finished"""
1557
 
        grass.try_remove(self.filename)
 
1732
        """Python script finished"""
 
1733
        try_remove(self.filename)
1558
1734
        self.filename = None
1559
1735
        
1560
1736
    def SaveAs(self, force = False):
1561
 
        """!Save python script to file
 
1737
        """Save python script to file
1562
1738
 
1563
 
        @return filename
 
1739
        :return: filename
1564
1740
        """
1565
1741
        filename = ''
1566
1742
        dlg = wx.FileDialog(parent = self,
1567
1743
                            message = _("Choose file to save"),
 
1744
                            defaultFile = self.parent.GetModelFile(ext=False) + '.py',
1568
1745
                            defaultDir = os.getcwd(),
1569
1746
                            wildcard = _("Python script (*.py)|*.py"),
1570
1747
                            style = wx.FD_SAVE)
1605
1782
        return filename
1606
1783
    
1607
1784
    def OnSaveAs(self, event):
1608
 
        """!Save python script to file"""
 
1785
        """Save python script to file"""
1609
1786
        self.SaveAs(force = False)
1610
1787
        event.Skip()
1611
1788
        
1612
1789
    def RefreshScript(self):
1613
 
        """!Refresh Python script
 
1790
        """Refresh Python script
1614
1791
 
1615
 
        @return True on refresh
1616
 
        @return False script hasn't been updated
 
1792
        :return: True on refresh
 
1793
        :return: False script hasn't been updated
1617
1794
        """
1618
1795
        if self.body.modified:
1619
1796
            dlg = wx.MessageDialog(self,
1639
1816
        return True
1640
1817
    
1641
1818
    def OnRefresh(self, event):
1642
 
        """!Refresh Python script"""
 
1819
        """Refresh Python script"""
1643
1820
        if self.RefreshScript():
1644
1821
            self.parent.SetStatusText(_('Python script is up-to-date'), 0)
1645
1822
        event.Skip()
1646
1823
        
1647
1824
    def IsModified(self):
1648
 
        """!Check if python script has been modified"""
 
1825
        """Check if python script has been modified"""
1649
1826
        return self.body.modified
1650
1827
    
1651
1828
    def IsEmpty(self):
1652
 
        """!Check if python script is empty"""
 
1829
        """Check if python script is empty"""
1653
1830
        return len(self.body.GetText()) == 0
1654
 
        
1655
 
def main():
1656
 
    import gettext
1657
 
    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
1658
 
    
1659
 
    app = wx.PySimpleApp()
1660
 
    if not globalvar.CheckWxVersion([2, 9]):
1661
 
        wx.InitAllImageHandlers()
1662
 
    frame = ModelFrame(parent = None)
1663
 
    if len(sys.argv) > 1:
1664
 
        frame.LoadModelFile(sys.argv[1])
1665
 
    frame.Show()
1666
 
    
1667
 
    app.MainLoop()
1668
 
    
1669
 
if __name__ == "__main__":
1670
 
    main()