[GRASS-SVN] r53437 - in grass-addons/grass7/gui/wxpython: . wx.animation wx.animation/animation
svn_grass at osgeo.org
svn_grass at osgeo.org
Wed Oct 17 05:24:38 PDT 2012
Author: annakrat
Date: 2012-10-17 05:24:37 -0700 (Wed, 17 Oct 2012)
New Revision: 53437
Added:
grass-addons/grass7/gui/wxpython/wx.animation/
grass-addons/grass7/gui/wxpython/wx.animation/animation/
grass-addons/grass7/gui/wxpython/wx.animation/animation/animation.py
grass-addons/grass7/gui/wxpython/wx.animation/animation/controller.py
grass-addons/grass7/gui/wxpython/wx.animation/animation/dialogs.py
grass-addons/grass7/gui/wxpython/wx.animation/animation/frame.py
grass-addons/grass7/gui/wxpython/wx.animation/animation/mapwindow.py
grass-addons/grass7/gui/wxpython/wx.animation/animation/nviztask.py
grass-addons/grass7/gui/wxpython/wx.animation/animation/temporal_manager.py
grass-addons/grass7/gui/wxpython/wx.animation/animation/toolbars.py
grass-addons/grass7/gui/wxpython/wx.animation/animation/utils.py
grass-addons/grass7/gui/wxpython/wx.animation/wxGUI.Animation.html
grass-addons/grass7/gui/wxpython/wx.animation/wxGUI_animation_tool.jpg
Log:
wxGUI: animation tool added
Added: grass-addons/grass7/gui/wxpython/wx.animation/animation/animation.py
===================================================================
--- grass-addons/grass7/gui/wxpython/wx.animation/animation/animation.py (rev 0)
+++ grass-addons/grass7/gui/wxpython/wx.animation/animation/animation.py 2012-10-17 12:24:37 UTC (rev 53437)
@@ -0,0 +1,185 @@
+"""!
+ at package animation.animation
+
+ at brief Animation class controls frame order
+
+Classes:
+ - animation::Animation
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import wx
+from utils import Orientation, ReplayMode
+
+class Animation(wx.EvtHandler):
+ """!Animation class specifies which frame to show at which instance."""
+ def __init__(self):
+ wx.EvtHandler.__init__(self)
+
+ self.currentIndex = 0
+ self.frames = []
+ # states
+ self.orientation = Orientation.FORWARD
+ self.replayMode = ReplayMode.ONESHOT
+
+ self.callbackUpdateFrame = None
+ self.callbackEndAnimation = None
+ self.callbackOrientationChanged = None
+
+ self.isActive = False
+
+ def IsActive(self):
+ """!Returns if the animation is active or not"""
+ return self.isActive
+
+ def SetActive(self, active):
+ self.isActive = active
+
+ def SetFrames(self, frames):
+ """!Sets animation frames.
+
+ @param frames list of strings
+ """
+ self.frames = frames
+
+ def GetCount(self):
+ """!Get frame count."""
+ return len(self.frames)
+
+ count = property(fget = GetCount)
+
+ def GetReplayMode(self):
+ """!Returns replay mode (loop)."""
+ return self._replayMode
+
+ def SetReplayMode(self, mode):
+ self._replayMode = mode
+
+ replayMode = property(fset = SetReplayMode, fget = GetReplayMode)
+
+ def GetOrientation(self):
+ return self._orientation
+
+ def SetOrientation(self, mode):
+ self._orientation = mode
+
+ orientation = property(fset = SetOrientation, fget = GetOrientation)
+
+ def SetCallbackUpdateFrame(self, callback):
+ """!Sets function to be called when updating frame."""
+ self.callbackUpdateFrame = callback
+
+ def SetCallbackEndAnimation(self, callback):
+ """!Sets function to be called when animation ends."""
+ self.callbackEndAnimation = callback
+
+ def SetCallbackOrientationChanged(self, callback):
+ """!Sets function to be called when orientation changes."""
+ self.callbackOrientationChanged = callback
+
+ def Start(self):
+ if not self.IsActive():
+ return
+
+ def Pause(self, paused):
+ if not self.IsActive():
+ return
+
+ def Stop(self):
+ if not self.IsActive():
+ return
+ self.currentIndex = 0
+ self.callbackEndAnimation(self.currentIndex, self.frames[self.currentIndex])
+
+ def _arrivedToEnd(self):
+ """!Decides which action to do after animation end (stop, repeat)."""
+ if not self.IsActive():
+ return
+ if self.replayMode == ReplayMode.ONESHOT:
+ self.Stop()
+
+ if self.orientation == Orientation.FORWARD:
+ if self.replayMode == ReplayMode.REPEAT:
+ self.currentIndex = 0
+ elif self.replayMode == ReplayMode.REVERSE:
+ self.orientation = Orientation.BACKWARD
+ self.currentIndex = self.count - 2 # -1
+ self.callbackOrientationChanged(Orientation.BACKWARD)
+ else:
+ if self.replayMode == ReplayMode.REPEAT:
+ self.currentIndex = self.count - 1
+ elif self.replayMode == ReplayMode.REVERSE:
+ self.orientation = Orientation.FORWARD
+ self.currentIndex = 1 # 0
+ self.callbackOrientationChanged(Orientation.FORWARD)
+
+ def Update(self):
+ """!Updates frame."""
+ if not self.IsActive():
+ return
+
+ self.callbackUpdateFrame(self.currentIndex, self.frames[self.currentIndex])
+ if self.orientation == Orientation.FORWARD:
+ self.currentIndex += 1
+ if self.currentIndex == self.count:
+ self._arrivedToEnd()
+ else:
+ self.currentIndex -= 1
+ if self.currentIndex == -1:
+ self._arrivedToEnd()
+
+ def FrameChangedFromOutside(self, index):
+ """!Let the animation know that frame was changed from outside."""
+ if not self.IsActive():
+ return
+ self.currentIndex = index
+ self.callbackUpdateFrame(self.currentIndex, self.frames[self.currentIndex])
+
+ def PreviousFrameIndex(self):
+ if not self.IsActive():
+ return
+ if self.orientation == Orientation.FORWARD:
+ self.currentIndex -= 1
+ if self.currentIndex == -1:
+ self.currentIndex = 0
+ else:
+ self.currentIndex += 1
+ if self.currentIndex == self.count:
+ self.currentIndex = self.count - 1
+
+ def NextFrameIndex(self):
+ if not self.IsActive():
+ return
+ if self.orientation == Orientation.FORWARD:
+ self.currentIndex += 1
+ if self.currentIndex == self.count:
+ self.currentIndex = self.count - 1
+ else:
+ self.currentIndex -= 1
+ if self.currentIndex == -1:
+ self.currentIndex = 0
+
+#def test():
+# import wx
+# app = wx.PySimpleApp()
+# a = Animation()
+#
+#
+# frame = wx.Frame(None)
+# frame.Show()
+#
+# a.SetReplayMode(ReplayMode.REVERSE)
+# a.Start()
+# app.MainLoop()
+#
+#
+#if __name__ == '__main__':
+# test()
+
+
Added: grass-addons/grass7/gui/wxpython/wx.animation/animation/controller.py
===================================================================
--- grass-addons/grass7/gui/wxpython/wx.animation/animation/controller.py (rev 0)
+++ grass-addons/grass7/gui/wxpython/wx.animation/animation/controller.py 2012-10-17 12:24:37 UTC (rev 53437)
@@ -0,0 +1,477 @@
+"""!
+ at package animation.controller
+
+ at brief Animations management
+
+Classes:
+ - controller::AnimationController
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+import wx
+
+from core.gcmd import GException, GError, GMessage
+import grass.script as grass
+
+from temporal_manager import TemporalManager
+from dialogs import InputDialog, EditDialog, AnimationData
+from utils import TemporalMode, Orientation
+
+class AnimationController(wx.EvtHandler):
+ def __init__(self, frame, sliders, animations, mapwindows, providers, bitmapPool):
+ wx.EvtHandler.__init__(self)
+
+ self.mapwindows = mapwindows
+
+ self.frame = frame
+ self.sliders = sliders
+ self.slider = self.sliders['temporal']
+ self.animationToolbar = None
+
+ self.temporalMode = None
+ self.animationData = []
+
+ self.timer = wx.Timer(self, id = wx.NewId())
+
+ self.animations = animations
+ self.bitmapPool = bitmapPool
+ self.bitmapProviders = providers
+ for anim, win, provider in zip(self.animations, self.mapwindows, self.bitmapProviders):
+ anim.SetCallbackUpdateFrame(lambda index, dataId, win = win, provider = provider : self.UpdateFrame(index, win, provider, dataId))
+ anim.SetCallbackEndAnimation(lambda index, dataId, win = win, provider = provider: self.UpdateFrameEnd(index, win, provider, dataId))
+ anim.SetCallbackOrientationChanged(self.OrientationChangedInReverseMode)
+
+ for slider in self.sliders.values():
+ slider.SetCallbackSliderChanging(self.SliderChanging)
+ slider.SetCallbackSliderChanged(self.SliderChanged)
+ slider.SetCallbackFrameIndexChanged(self.ChangeFrame)
+
+ self.runAfterReleasingSlider = None
+
+ self.temporalManager = TemporalManager()
+ self.Bind(wx.EVT_TIMER, self.OnTimerTick, self.timer)
+
+ self.timeTick = 200
+
+ def SetAnimationToolbar(self, toolbar):
+ self.animationToolbar = toolbar
+
+ def GetTimeTick(self):
+ return self._timeTick
+
+ def SetTimeTick(self, value):
+ self._timeTick = value
+ if self.timer.IsRunning():
+ self.timer.Stop()
+ self.timer.Start(self._timeTick)
+ self.DisableSliderIfNeeded()
+
+ timeTick = property(fget = GetTimeTick, fset = SetTimeTick)
+
+ def OnTimerTick(self, event):
+ for anim in self.animations:
+ anim.Update()
+
+ def StartAnimation(self):
+ # if self.timer.IsRunning():
+ # self.timer.Stop()
+
+ for anim in self.animations:
+ if self.timer.IsRunning():
+ anim.NextFrameIndex()
+ anim.Start()
+ if not self.timer.IsRunning():
+ self.timer.Start(self.timeTick)
+ self.DisableSliderIfNeeded()
+
+ def PauseAnimation(self, paused):
+ if paused:
+ if self.timer.IsRunning():
+ self.timer.Stop()
+ self.DisableSliderIfNeeded()
+ else:
+ if not self.timer.IsRunning():
+ self.timer.Start(self.timeTick)
+ self.DisableSliderIfNeeded()
+
+ for anim in self.animations:
+ anim.Pause(paused)
+
+ def EndAnimation(self):
+ if self.timer.IsRunning():
+ self.timer.Stop()
+ self.DisableSliderIfNeeded()
+
+ for anim in self.animations:
+ anim.Stop()
+
+ def UpdateFrameEnd(self, index, win, provider, dataId):
+ if self.timer.IsRunning():
+ self.timer.Stop()
+ self.DisableSliderIfNeeded()
+
+ self.animationToolbar.Stop()
+
+ self.UpdateFrame(index, win, provider, dataId)
+
+ def UpdateFrame(self, index, win, provider, dataId):
+ bitmap = provider.GetBitmap(dataId)
+ if dataId is None:
+ dataId = ''
+ win.DrawBitmap(bitmap, dataId)
+ # self.frame.SetStatusText(dataId)
+ self.slider.UpdateFrame(index)
+ self.UpdateReloadStatus()
+
+ def SliderChanging(self, index):
+ if self.runAfterReleasingSlider is None:
+ self.runAfterReleasingSlider = self.timer.IsRunning()
+ self.PauseAnimation(True)
+ self.ChangeFrame(index)
+
+ def SliderChanged(self):
+ if self.runAfterReleasingSlider:
+ self.PauseAnimation(False)
+ self.runAfterReleasingSlider = None
+
+ def ChangeFrame(self, index):
+ for anim in self.animations:
+ anim.FrameChangedFromOutside(index)
+
+ def DisableSliderIfNeeded(self):
+ if self.timer.IsRunning() and self._timeTick < 100:
+ self.slider.EnableSlider(False)
+ else:
+ self.slider.EnableSlider(True)
+
+
+ def OrientationChangedInReverseMode(self, mode):
+ if mode == Orientation.FORWARD:
+ self.animationToolbar.PlayForward()
+ elif mode == Orientation.BACKWARD:
+ self.animationToolbar.PlayBack()
+
+ def SetReplayMode(self, mode):
+ for anim in self.animations:
+ anim.replayMode = mode
+
+ def SetOrientation(self, mode):
+ for anim in self.animations:
+ anim.orientation = mode
+
+
+ def SetTemporalMode(self, mode):
+ self._temporalMode = mode
+
+ def GetTemporalMode(self):
+ return self._temporalMode
+
+ temporalMode = property(fget = GetTemporalMode, fset = SetTemporalMode)
+
+ def GetTimeGranularity(self):
+ if self.temporalMode == TemporalMode.TEMPORAL:
+ return self.temporalManager.GetGranularity(original = False)
+
+ return None
+
+ def EditAnimations(self):
+ # running = False
+ # if self.timer.IsRunning():
+ # running = True
+ self.EndAnimation()
+
+ dlg = EditDialog(parent = self.frame, evalFunction = self.EvaluateInput,
+ animationData = self.animationData, maxAnimations = len(self.animations))
+ if dlg.ShowModal() == wx.ID_CANCEL:
+ dlg.Destroy()
+ return
+ self.animationData, self.temporalMode, self.temporalManager = dlg.GetResult()
+ dlg.Destroy()
+
+ self._setAnimations()
+
+ def AddAnimation(self):
+ # check if we can add more animations
+ found = False
+ indices = [anim.windowIndex for anim in self.animationData]
+ for windowIndex in range(len(self.animations)):
+ if windowIndex not in indices:
+ found = True
+ break
+
+ if not found:
+ GMessage(parent = self.frame, message = _("Maximum number of animations is %s.") % len(self.animations))
+ return
+
+ # running = False
+ # if self.timer.IsRunning():
+ # running = True
+ self.EndAnimation()
+ # self.PauseAnimation(True)
+
+ animData = AnimationData()
+ # number of active animations
+ animationIndex = len([anim for anim in self.animations if anim.IsActive()])
+ animData.SetDefaultValues(windowIndex, animationIndex)
+ dlg = InputDialog(parent = self.frame, mode = 'add', animationData = animData)
+ if dlg.ShowModal() == wx.ID_CANCEL:
+ dlg.Destroy()
+ return
+ dlg.Destroy()
+ # check compatibility
+ if animData.windowIndex in indices:
+ GMessage(parent = self.frame, message = _("More animations are using one window."
+ " Please select different window for each animation."))
+ return
+ try:
+ temporalMode, tempManager = self.EvaluateInput(self.animationData + [animData])
+ except GException, e:
+ GError(parent = self.frame, message = e.value, showTraceback = False)
+ return
+ # if ok, set temporal mode
+ self.temporalMode = temporalMode
+ self.temporalManager = tempManager
+ # add data
+ windowIndex = animData.windowIndex
+ self.animationData.append(animData)
+ self._setAnimations()
+
+ def SetAnimations(self, raster = None, strds = None):
+ """!Set animation data directly.
+
+ @param raster list of lists of raster maps or None
+ @param strds list of strds or None
+ """
+ try:
+ animationData = []
+ for i in range(len(self.animations)):
+ if raster is not None and type(raster[i]) == list and raster[i]:
+ anim = AnimationData()
+ anim.SetDefaultValues(i, i)
+ anim.inputMapType = 'rast'
+ anim.inputData = ','.join(raster[i])
+ animationData.append(anim)
+
+ elif strds is not None and strds[i]:
+ anim = AnimationData()
+ anim.SetDefaultValues(i, i)
+ anim.inputMapType = 'strds'
+ anim.inputData = strds[i]
+ animationData.append(anim)
+
+ except (GException, ValueError, IOError) as e:
+ GError(parent = self.frame, message = str(e),
+ showTraceback = False, caption = _("Invalid input"))
+ return
+ try:
+ temporalMode, tempManager = self.EvaluateInput(animationData)
+ except GException, e:
+ GError(parent = self.frame, message = e.value, showTraceback = False)
+ return
+ self.animationData = animationData
+ self.temporalManager = tempManager
+ self.temporalMode = temporalMode
+ self._setAnimations()
+
+ def _setAnimations(self):
+ indices = [anim.windowIndex for anim in self.animationData]
+
+ self._updateWindows(activeIndices = indices)
+
+ if self.temporalMode == TemporalMode.TEMPORAL:
+ timeLabels, mapNamesDict = self.temporalManager.GetLabelsAndMaps()
+ else:
+ timeLabels, mapNamesDict = None, None
+
+ self._updateSlider(timeLabels = timeLabels)
+ self._updateAnimations(activeIndices = indices, mapNamesDict = mapNamesDict)
+ self._updateRegion()
+ self._updateBitmapData()
+ # if running:
+ # self.PauseAnimation(False)
+ # # self.StartAnimation()
+ # else:
+ self.EndAnimation()
+
+ self.UpdateReloadStatus()
+
+ def _updateSlider(self, timeLabels = None):
+ if self.temporalMode == TemporalMode.NONTEMPORAL:
+ self.frame.SetSlider('nontemporal')
+ self.slider = self.sliders['nontemporal']
+ frameCount = len(self.animationData[0].mapData) # should be the same for all
+ self.slider.SetFrames(frameCount)
+ elif self.temporalMode == TemporalMode.TEMPORAL:
+ self.frame.SetSlider('temporal')
+ self.slider = self.sliders['temporal']
+ self.slider.SetTemporalType(self.temporalManager.temporalType)
+ self.slider.SetFrames(timeLabels)
+ else:
+ self.frame.SetSlider(None)
+ self.slider = None
+
+ def _updateAnimations(self, activeIndices, mapNamesDict = None):
+ if self.temporalMode == TemporalMode.NONTEMPORAL:
+ for i in range(len(self.animations)):
+ if i not in activeIndices:
+ self.animations[i].SetActive(False)
+ continue
+ anim = [anim for anim in self.animationData if anim.windowIndex == i][0]
+ self.animations[i].SetFrames(anim.mapData)
+ self.animations[i].SetActive(True)
+ else:
+ for i in range(len(self.animations)):
+ if i not in activeIndices:
+ self.animations[i].SetActive(False)
+ continue
+ anim = [anim for anim in self.animationData if anim.windowIndex == i][0]
+ self.animations[i].SetFrames(mapNamesDict[anim.inputData])
+ self.animations[i].SetActive(True)
+
+ def _updateWindows(self, activeIndices):
+ # add or remove window
+ for windowIndex in range(len(self.animations)):
+ if not self.frame.IsWindowShown(windowIndex) and windowIndex in activeIndices:
+ self.frame.AddWindow(windowIndex)
+ elif self.frame.IsWindowShown(windowIndex) and windowIndex not in activeIndices:
+ self.frame.RemoveWindow(windowIndex)
+
+ def _updateBitmapData(self):
+ # unload data:
+ for prov in self.bitmapProviders:
+ prov.Unload()
+
+ # load data
+ for animData in self.animationData:
+ if animData.viewMode == '2d':
+ self._load2DData(animData)
+ else:
+ self._load3DData(animData)
+
+ # clear bitmapPool
+ usedNames = []
+ for prov in self.bitmapProviders:
+ names = prov.GetDataNames()
+ if names:
+ usedNames.extend(names)
+ self.bitmapPool.Clear(usedNames)
+
+ def _load2DData(self, animationData):
+ prov = self.bitmapProviders[animationData.windowIndex]
+ prov.SetData(datasource = animationData.mapData)
+
+ self.bitmapProviders[animationData.windowIndex].Load()
+
+ def _load3DData(self, animationData):
+ prov = self.bitmapProviders[animationData.windowIndex]
+ prov.SetData(datasource = animationData.GetNvizCommands(),
+ dataNames = animationData.mapData, dataType = 'nviz',
+ suffix = animationData.nvizParameter)
+
+ self.bitmapProviders[animationData.windowIndex].Load()
+
+ def EvaluateInput(self, animationData):
+ stds = 0
+ maps = 0
+ mapCount = set()
+ tempManager = None
+ windowIndex = []
+ for anim in animationData:
+
+ mapCount.add(len(anim.mapData))
+ windowIndex.append(anim.windowIndex)
+
+ if anim.inputMapType in ('rast', 'vect'):
+ maps += 1
+ elif anim.inputMapType in ('strds', 'stvds'):
+ stds += 1
+
+ if maps and stds:
+ temporalMode = TemporalMode.NONTEMPORAL
+ elif maps:
+ temporalMode = TemporalMode.NONTEMPORAL
+ elif stds:
+ temporalMode = TemporalMode.TEMPORAL
+ else:
+ temporalMode = None
+
+ if temporalMode == TemporalMode.NONTEMPORAL:
+ if len(mapCount) > 1:
+ raise GException(_("Inconsistent number of maps, please check input data."))
+ elif temporalMode == TemporalMode.TEMPORAL:
+ tempManager = TemporalManager()
+ # these raise GException:
+ for anim in animationData:
+ if anim.inputMapType not in ('strds', 'stvds'):
+ continue
+ tempManager.AddTimeSeries(anim.inputData, anim.inputMapType)
+ tempManager.EvaluateInputData()
+
+ return temporalMode, tempManager
+
+ def Reload(self):
+ self.EndAnimation()
+
+ self._updateRegion()
+ activeIndices = [anim.windowIndex for anim in self.animationData]
+ for index in activeIndices:
+ self.bitmapProviders[index].Load(force = True)
+
+ self.EndAnimation()
+
+ self.UpdateReloadStatus()
+
+ def UpdateReloadStatus(self):
+ activeIndices = [anim.windowIndex for anim in self.animationData]
+ for i, win in enumerate(self.mapwindows):
+ if i in activeIndices and win.IsRescaled():
+ self.frame.SetStatusText(_("Reload recommended"), 1)
+ return
+ self.frame.SetStatusText('', 1)
+
+ def _updateRegion(self):
+ grassRegion = grass.region()
+ for anim in self.animationData:
+ if anim.viewMode == '2d':
+ self.mapwindows[anim.windowIndex].SetRegion(grassRegion)
+ else:
+ loadSize = self.bitmapProviders[anim.windowIndex].GetLoadSize() or \
+ self.mapwindows[anim.windowIndex].GetClientSize()
+ region = {}
+ region['cols'], region['rows'] = loadSize
+ self.mapwindows[anim.windowIndex].SetRegion(region)
+
+
+
+#def test():
+# import gettext
+# gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+#
+# import grass.script as grass
+# import wx
+# app = wx.PySimpleApp()
+# wx.InitAllImageHandlers()
+# # app.MainLoop()
+#
+# bitmaps = {}
+# rasters = ['elevation.dem']
+# # rasters = ['streams']
+# # rasters = grass.read_command("g.mlist", type = 'rast', fs = ',', quiet = True).strip().split(',')
+#
+# # print nrows, ncols
+# size = (300,100)
+# newSize, scale = ComputeScale(size)
+# # print scale
+# LoadRasters(rasters = rasters, bitmaps = bitmaps, scale = scale, size = newSize)
+#
+# for b in bitmaps.keys():
+# bitmaps[b].SaveFile('/home/anna/testy/ctypes/' + b + '.png', wx.BITMAP_TYPE_PNG)
+#
+#if __name__ == '__main__':
+#
+# test()
Added: grass-addons/grass7/gui/wxpython/wx.animation/animation/dialogs.py
===================================================================
--- grass-addons/grass7/gui/wxpython/wx.animation/animation/dialogs.py (rev 0)
+++ grass-addons/grass7/gui/wxpython/wx.animation/animation/dialogs.py 2012-10-17 12:24:37 UTC (rev 53437)
@@ -0,0 +1,810 @@
+"""!
+ at package animation.dialogs
+
+ at brief Dialogs for animation management, changing speed of animation
+
+Classes:
+ - dialogs::SpeedDialog
+ - dialogs::InputDialog
+ - dialogs::EditDialog
+ - dialogs::AnimationData
+
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+import os
+import sys
+import wx
+import copy
+import datetime
+from wx.lib.newevent import NewEvent
+import wx.lib.filebrowsebutton as filebrowse
+
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.environ['GISBASE'], "etc", "gui", "wxpython"))
+
+import grass.temporal as tgis
+
+from core.gcmd import GMessage, GError, GException
+from core import globalvar
+from gui_core import gselect
+from gui_core.dialogs import MapLayersDialog, EVT_APPLY_MAP_LAYERS
+from core.settings import UserSettings
+
+from utils import TemporalMode, validateTimeseriesName, validateMapNames
+from nviztask import NvizTask
+
+SpeedChangedEvent, EVT_SPEED_CHANGED = NewEvent()
+
+class SpeedDialog(wx.Dialog):
+ def __init__(self, parent, title = _("Adjust speed of animation"),
+ temporalMode = None, minimumDuration = 20, timeGranularity = None,
+ initialSpeed = 200):#, framesCount = None
+ wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title,
+ style = wx.DEFAULT_DIALOG_STYLE)
+
+ self.minimumDuration = minimumDuration
+ # self.framesCount = framesCount
+ self.defaultSpeed = initialSpeed
+ self.lastAppliedValue = self.defaultSpeed
+ self.lastAppliedValueTemp = self.defaultSpeed
+
+ self._layout()
+
+ self.temporalMode = temporalMode
+ self.timeGranularity = timeGranularity
+
+ self._fillUnitChoice(self.choiceUnits)
+ self.InitTimeSpin(self.defaultSpeed)
+
+ def SetTimeGranularity(self, gran):
+ self._timeGranularity = gran
+
+ def GetTimeGranularity(self):
+ return self._timeGranularity
+
+ timeGranularity = property(fset = SetTimeGranularity, fget = GetTimeGranularity)
+
+ def SetTemporalMode(self, mode):
+ self._temporalMode = mode
+ self._setTemporalMode()
+
+ def GetTemporalMode(self):
+ return self._temporalMode
+
+ temporalMode = property(fset = SetTemporalMode, fget = GetTemporalMode)
+
+ def _layout(self):
+ """!Layout window"""
+ mainSizer = wx.BoxSizer(wx.VERTICAL)
+ #
+ # simple mode
+ #
+ self.nontemporalBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+ label = ' %s ' % _("Simple mode"))
+ box = wx.StaticBoxSizer(self.nontemporalBox, wx.VERTICAL)
+ gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+
+ labelDuration = wx.StaticText(self, id = wx.ID_ANY, label = _("Frame duration:"))
+ labelUnits = wx.StaticText(self, id = wx.ID_ANY, label = _("ms"))
+ self.spinDuration = wx.SpinCtrl(self, id = wx.ID_ANY, min = self.minimumDuration,
+ max = 10000, initial = self.defaultSpeed)
+ # TODO total time
+
+ gridSizer.Add(item = labelDuration, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+ gridSizer.Add(item = self.spinDuration, pos = (0, 1), flag = wx.ALIGN_CENTER)
+ gridSizer.Add(item = labelUnits, pos = (0, 2), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+ gridSizer.AddGrowableCol(0)
+
+ box.Add(item = gridSizer, proportion = 1, border = 5, flag = wx.ALL | wx.EXPAND)
+ self.nontemporalSizer = gridSizer
+ mainSizer.Add(item = box, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 5)
+ #
+ # temporal mode
+ #
+ self.temporalBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+ label = ' %s ' % _("Temporal mode"))
+ box = wx.StaticBoxSizer(self.temporalBox, wx.VERTICAL)
+ gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+
+ labelTimeUnit = wx.StaticText(self, id = wx.ID_ANY, label = _("Time unit:"))
+ labelDuration = wx.StaticText(self, id = wx.ID_ANY, label = _("Duration of time unit:"))
+ labelUnits = wx.StaticText(self, id = wx.ID_ANY, label = _("ms"))
+ self.spinDurationTemp = wx.SpinCtrl(self, id = wx.ID_ANY, min = self.minimumDuration,
+ max = 10000, initial = self.defaultSpeed)
+ self.choiceUnits = wx.Choice(self, id = wx.ID_ANY)
+
+ # TODO total time
+
+ gridSizer.Add(item = labelTimeUnit, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+ gridSizer.Add(item = self.choiceUnits, pos = (0, 1), flag = wx.ALIGN_CENTER | wx.EXPAND)
+ gridSizer.Add(item = labelDuration, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+ gridSizer.Add(item = self.spinDurationTemp, pos = (1, 1), flag = wx.ALIGN_CENTER | wx.EXPAND)
+ gridSizer.Add(item = labelUnits, pos = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+ gridSizer.AddGrowableCol(1)
+
+ self.temporalSizer = gridSizer
+ box.Add(item = gridSizer, proportion = 1, border = 5, flag = wx.ALL | wx.EXPAND)
+ mainSizer.Add(item = box, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 5)
+
+ self.btnOk = wx.Button(self, wx.ID_OK)
+ self.btnApply = wx.Button(self, wx.ID_APPLY)
+ self.btnCancel = wx.Button(self, wx.ID_CANCEL)
+ self.btnOk.SetDefault()
+
+ self.btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
+ self.btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+ self.btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+ self.Bind(wx.EVT_CLOSE, self.OnCancel)
+ # button sizer
+ btnStdSizer = wx.StdDialogButtonSizer()
+ btnStdSizer.AddButton(self.btnOk)
+ btnStdSizer.AddButton(self.btnApply)
+ btnStdSizer.AddButton(self.btnCancel)
+ btnStdSizer.Realize()
+
+ mainSizer.Add(item = btnStdSizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border = 5)
+
+ self.SetSizer(mainSizer)
+ mainSizer.Fit(self)
+
+ def _setTemporalMode(self):
+ self.nontemporalBox.Enable(self.temporalMode == TemporalMode.NONTEMPORAL)
+ self.temporalBox.Enable(self.temporalMode == TemporalMode.TEMPORAL)
+ for child in self.temporalSizer.GetChildren():
+ child.GetWindow().Enable(self.temporalMode == TemporalMode.TEMPORAL)
+ for child in self.nontemporalSizer.GetChildren():
+ child.GetWindow().Enable(self.temporalMode == TemporalMode.NONTEMPORAL)
+
+ self.Layout()
+
+ def _fillUnitChoice(self, choiceWidget):
+ timeUnitsChoice = [_("year"), _("month"), _("day"), _("hour"), _("minute"), _("second")]
+ timeUnits = ["years", "months", "days", "hours", "minutes", "seconds"]
+ for item, cdata in zip(timeUnitsChoice, timeUnits):
+ choiceWidget.Append(item, cdata)
+
+ if self.temporalMode == TemporalMode.TEMPORAL:
+ unit = self.timeGranularity.split()[1]
+ try:
+ index = timeUnits.index(unit)
+ except ValueError:
+ index = 0
+ choiceWidget.SetSelection(index)
+ else:
+ choiceWidget.SetSelection(0)
+
+ def OnOk(self, event):
+ self._apply()
+ self.OnCancel(None)
+
+ def OnApply(self, event):
+ self._apply()
+
+ def OnCancel(self, event):
+ self.spinDuration.SetValue(self.lastAppliedValue)
+ self.spinDurationTemp.SetValue(self.lastAppliedValueTemp)
+ self.Hide()
+
+ def InitTimeSpin(self, timeTick):
+ if self.temporalMode == TemporalMode.TEMPORAL:
+ index = self.choiceUnits.GetSelection()
+ unit = self.choiceUnits.GetClientData(index)
+ delta = self._timedelta(unit = unit, number = 1)
+ seconds1 = self._total_seconds(delta)
+
+ number, unit = self.timeGranularity.split()
+ number = float(number)
+ delta = self._timedelta(unit = unit, number = number)
+ seconds2 = self._total_seconds(delta)
+ value = timeTick
+ ms = value * seconds1 / float(seconds2)
+ self.spinDurationTemp.SetValue(ms)
+ else:
+ self.spinDuration.SetValue(timeTick)
+
+ def _apply(self):
+ if self.temporalMode == TemporalMode.NONTEMPORAL:
+ ms = self.spinDuration.GetValue()
+ self.lastAppliedValue = self.spinDuration.GetValue()
+ elif self.temporalMode == TemporalMode.TEMPORAL:
+ index = self.choiceUnits.GetSelection()
+ unit = self.choiceUnits.GetClientData(index)
+ delta = self._timedelta(unit = unit, number = 1)
+ seconds1 = self._total_seconds(delta)
+
+
+ number, unit = self.timeGranularity.split()
+ number = float(number)
+ delta = self._timedelta(unit = unit, number = number)
+ seconds2 = self._total_seconds(delta)
+
+ value = self.spinDurationTemp.GetValue()
+ ms = value * seconds2 / float(seconds1)
+ if ms < self.minimumDuration:
+ GMessage(parent = self, message = _("Animation speed is too high."))
+ return
+ self.lastAppliedValueTemp = self.spinDurationTemp.GetValue()
+ else:
+ return
+
+ event = SpeedChangedEvent(ms = ms)
+ wx.PostEvent(self, event)
+
+ def _timedelta(self, unit, number):
+ if unit == "years":
+ delta = datetime.timedelta(days = 365 * number)
+ elif unit == "months":
+ delta = datetime.timedelta(days = 30 * number)
+ elif unit == "days":
+ delta = datetime.timedelta(days = 1 * number)
+ elif unit == "hours":
+ delta = datetime.timedelta(hours = 1 * number)
+ elif unit == "minutes":
+ delta = datetime.timedelta(minutes = 1 * number)
+ elif unit == "seconds":
+ delta = datetime.timedelta(seconds = 1 * number)
+
+ return delta
+
+ def _total_seconds(self, delta):
+ """!timedelta.total_seconds is new in version 2.7.
+ """
+ return delta.seconds + delta.days * 24 * 3600
+
+
+class InputDialog(wx.Dialog):
+ def __init__(self, parent, mode, animationData):
+ wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY,
+ style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
+ if mode == 'add':
+ self.SetTitle(_("Add new animation"))
+ elif mode == 'edit':
+ self.SetTitle(_("Edit animation"))
+
+ self.animationData = animationData
+
+ self._layout()
+ self.OnViewMode(event = None)
+
+ def _layout(self):
+ mainSizer = wx.BoxSizer(wx.VERTICAL)
+
+ self.windowChoice = wx.Choice(self, id = wx.ID_ANY,
+ choices = [_("top left"), _("top right"),
+ _("bottom left"), _("bottom right")])
+ self.windowChoice.SetSelection(self.animationData.windowIndex)
+
+ self.nameCtrl = wx.TextCtrl(self, id = wx.ID_ANY, value = self.animationData.name)
+
+ self.nDChoice = wx.Choice(self, id = wx.ID_ANY)
+ mode = self.animationData.viewMode
+ index = 0
+ for i, (viewMode, viewModeName) in enumerate(self.animationData.viewModes):
+ self.nDChoice.Append(viewModeName, clientData = viewMode)
+ if mode == viewMode:
+ index = i
+
+ self.nDChoice.SetSelection(index)
+ # TODO
+ self.nDChoice.SetToolTipString(_(""))
+ self.nDChoice.Bind(wx.EVT_CHOICE, self.OnViewMode)
+
+ gridSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
+ gridSizer.Add(item = wx.StaticText(self, id = wx.ID_ANY, label = _("Name:")),
+ flag = wx.ALIGN_CENTER_VERTICAL)
+ gridSizer.Add(item = self.nameCtrl, proportion = 1, flag = wx.EXPAND)
+ gridSizer.Add(item = wx.StaticText(self, id = wx.ID_ANY, label = _("Window position:")),
+ flag = wx.ALIGN_CENTER_VERTICAL)
+ gridSizer.Add(item = self.windowChoice, proportion = 1, flag = wx.ALIGN_RIGHT)
+ gridSizer.Add(item = wx.StaticText(self, id = wx.ID_ANY, label = _("View mode:")),
+ flag = wx.ALIGN_CENTER_VERTICAL)
+ gridSizer.Add(item = self.nDChoice, proportion = 1, flag = wx.ALIGN_RIGHT)
+ gridSizer.AddGrowableCol(0, 1)
+ gridSizer.AddGrowableCol(1, 1)
+ mainSizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+
+ self.dataPanel = self._createDataPanel()
+ self.threeDPanel = self._create3DPanel()
+ mainSizer.Add(item = self.dataPanel, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 3)
+ mainSizer.Add(item = self.threeDPanel, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 3)
+
+ # buttons
+ self.btnOk = wx.Button(self, wx.ID_OK)
+ self.btnCancel = wx.Button(self, wx.ID_CANCEL)
+ self.btnOk.SetDefault()
+ self.btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
+ # button sizer
+ btnStdSizer = wx.StdDialogButtonSizer()
+ btnStdSizer.AddButton(self.btnOk)
+ btnStdSizer.AddButton(self.btnCancel)
+ btnStdSizer.Realize()
+
+ mainSizer.Add(item = btnStdSizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border = 5)
+
+ self.SetSizer(mainSizer)
+ mainSizer.Fit(self)
+
+ def _createDataPanel(self):
+ panel = wx.Panel(self, id = wx.ID_ANY)
+ dataStBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+ label = ' %s ' % _("Data"))
+ dataBoxSizer = wx.StaticBoxSizer(dataStBox, wx.VERTICAL)
+
+ self.dataChoice = wx.Choice(panel, id = wx.ID_ANY)
+ self._setMapTypes()
+ self.dataChoice.Bind(wx.EVT_CHOICE, self.OnDataType)
+
+ self.dataSelect = gselect.Select(parent = panel, id = wx.ID_ANY,
+ size = globalvar.DIALOG_GSELECT_SIZE)
+
+
+ iconTheme = UserSettings.Get(group = 'appearance', key = 'iconTheme', subkey = 'type')
+ bitmapPath = os.path.join(globalvar.ETCICONDIR, iconTheme, 'layer-open.png')
+ if os.path.isfile(bitmapPath) and os.path.getsize(bitmapPath):
+ bitmap = wx.Bitmap(name = bitmapPath)
+ else:
+ bitmap = wx.ArtProvider.GetBitmap(id = wx.ART_MISSING_IMAGE, client = wx.ART_TOOLBAR)
+ self.addManyMapsButton = wx.BitmapButton(panel, id = wx.ID_ANY, bitmap = bitmap)
+ self.addManyMapsButton.Bind(wx.EVT_BUTTON, self.OnAddMaps)
+
+ self.OnDataType(None)
+ if self.animationData.inputData is None:
+ self.dataSelect.SetValue('')
+ else:
+ self.dataSelect.SetValue(self.animationData.inputData)
+
+ hbox = wx.BoxSizer(wx.HORIZONTAL)
+ hbox.Add(item = wx.StaticText(panel, wx.ID_ANY, label = _("Input data type:")),
+ proportion = 1, flag = wx.ALIGN_CENTER_VERTICAL)
+ hbox.Add(item = self.dataChoice, proportion = 1, flag = wx.EXPAND)
+ dataBoxSizer.Add(item = hbox, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 3)
+
+ hbox = wx.BoxSizer(wx.HORIZONTAL)
+ # hbox.Add(item = wx.StaticText(panel, wx.ID_ANY, label = _("Input data:")),
+ # proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+ hbox.Add(item = self.dataSelect, proportion = 1, flag = wx.ALIGN_CENTER)
+ hbox.Add(item = self.addManyMapsButton, proportion = 0, flag = wx.LEFT, border = 5)
+ dataBoxSizer.Add(item = hbox, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 3)
+
+ panel.SetSizerAndFit(dataBoxSizer)
+ panel.SetAutoLayout(True)
+
+ return panel
+
+ def _create3DPanel(self):
+ panel = wx.Panel(self, id = wx.ID_ANY)
+ dataStBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+ label = ' %s ' % _("3D view parameters"))
+ dataBoxSizer = wx.StaticBoxSizer(dataStBox, wx.VERTICAL)
+
+ # workspace file
+ self.fileSelector = filebrowse.FileBrowseButton(parent = panel, id = wx.ID_ANY,
+ size = globalvar.DIALOG_GSELECT_SIZE,
+ labelText = _("Workspace file:"),
+ dialogTitle = _("Choose workspace file to import 3D view parameters"),
+ buttonText = _('Browse'),
+ startDirectory = os.getcwd(), fileMode = 0,
+ fileMask = "GRASS Workspace File (*.gxw)|*.gxw")
+ if self.animationData.workspaceFile:
+ self.fileSelector.SetValue(self.animationData.workspaceFile)
+ self.paramLabel = wx.StaticText(panel, wx.ID_ANY, label = _("Parameter for animation:"))
+ self.paramChoice = wx.Choice(panel, id = wx.ID_ANY, choices = self.animationData.nvizParameters)
+ self.paramChoice.SetStringSelection(self.animationData.nvizParameter)
+
+ hbox = wx.BoxSizer(wx.HORIZONTAL)
+ hbox.Add(item = self.fileSelector, proportion = 1, flag = wx.EXPAND | wx.ALIGN_CENTER)
+ dataBoxSizer.Add(item = hbox, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 3)
+
+ hbox = wx.BoxSizer(wx.HORIZONTAL)
+ hbox.Add(item = self.paramLabel, proportion = 1, flag = wx.ALIGN_CENTER_VERTICAL)
+ hbox.Add(item = self.paramChoice, proportion = 1, flag = wx.EXPAND)
+ dataBoxSizer.Add(item = hbox, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 3)
+
+ panel.SetSizerAndFit(dataBoxSizer)
+ panel.SetAutoLayout(True)
+
+ return panel
+
+ def _setMapTypes(self, view2d = True):
+ index = 0
+ if view2d:
+ inputTypes = self.animationData.inputMapTypes[::2]
+ else:
+ inputTypes = self.animationData.inputMapTypes
+ self.dataChoice.Clear()
+ for i, (itype, itypeName) in enumerate(inputTypes):
+ self.dataChoice.Append(itypeName, clientData = itype)
+ if itype == self.animationData.inputMapType:
+ index = i
+ self.dataChoice.SetSelection(index)
+
+ def OnViewMode(self, event):
+ mode = self.nDChoice.GetSelection()
+ self.Freeze()
+ sizer = self.threeDPanel.GetContainingSizer()
+ sizer.Show(self.threeDPanel, mode != 0, True)
+ sizer.Layout()
+ self._setMapTypes(mode == 0)
+ self.Layout()
+ self.Fit()
+ self.Thaw()
+
+ def OnDataType(self, event):
+ etype = self.dataChoice.GetClientData(self.dataChoice.GetSelection())
+ if etype in ('rast', 'vect'):
+ self.dataSelect.SetType(etype = etype, multiple = True)
+ self.addManyMapsButton.Enable(True)
+ else:
+ self.dataSelect.SetType(etype = etype, multiple = False)
+ self.addManyMapsButton.Enable(False)
+
+ self.dataSelect.SetValue('')
+
+ def OnAddMaps(self, event):
+ # TODO: fix dialog
+ etype = self.dataChoice.GetClientData(self.dataChoice.GetSelection())
+ index = 0
+ if etype == 'vect':
+ index = 2
+
+ dlg = MapLayersDialog(self, title = _("Select raster maps for animation"))
+ dlg.Bind(EVT_APPLY_MAP_LAYERS, self.OnApplyMapLayers)
+ dlg.layerType.SetSelection(index)
+ dlg.LoadMapLayers(dlg.GetLayerType(cmd = True),
+ dlg.mapset.GetStringSelection())
+ if dlg.ShowModal() == wx.ID_OK:
+ self.dataSelect.SetValue(','.join(dlg.GetMapLayers()))
+
+ dlg.Destroy()
+
+ def OnApplyMapLayers(self, event):
+ self.dataSelect.SetValue(','.join(event.mapLayers))
+
+ def _update(self):
+ self.animationData.name = self.nameCtrl.GetValue()
+ self.animationData.windowIndex = self.windowChoice.GetSelection()
+
+ sel = self.dataChoice.GetSelection()
+ self.animationData.inputMapType = self.dataChoice.GetClientData(sel)
+ self.animationData.inputData = self.dataSelect.GetValue()
+ sel = self.nDChoice.GetSelection()
+ self.animationData.viewMode = self.nDChoice.GetClientData(sel)
+
+ if self.threeDPanel.IsShown():
+ self.animationData.workspaceFile = self.fileSelector.GetValue()
+ if self.threeDPanel.IsShown():
+ self.animationData.nvizParameter = self.paramChoice.GetStringSelection()
+
+ def OnOk(self, event):
+ try:
+ self._update()
+ self.EndModal(wx.ID_OK)
+ except (GException, ValueError, IOError) as e:
+ GError(message = str(e), showTraceback = False, caption = _("Invalid input"))
+
+
+class EditDialog(wx.Dialog):
+ def __init__(self, parent, evalFunction, animationData, maxAnimations):
+ wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY,
+ style = wx.DEFAULT_DIALOG_STYLE)
+ self.animationData = copy.deepcopy(animationData)
+ self.eval = evalFunction
+ self.SetTitle(_("Add, edit or remove animations"))
+ self._layout()
+ self.SetSize((300, -1))
+ self.maxAnimations = maxAnimations
+ self.result = None
+
+ def _layout(self):
+ mainSizer = wx.BoxSizer(wx.VERTICAL)
+ box = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("List of animations"))
+ sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+ gridBagSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+ gridBagSizer.AddGrowableCol(0)
+ # gridBagSizer.AddGrowableCol(1,1)
+
+ self.listbox = wx.ListBox(self, id = wx.ID_ANY, choices = [], style = wx.LB_SINGLE|wx.LB_NEEDED_SB)
+ self.listbox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnEdit)
+
+ self.addButton = wx.Button(self, id = wx.ID_ANY, label = _("Add"))
+ self.addButton.Bind(wx.EVT_BUTTON, self.OnAdd)
+ self.editButton = wx.Button(self, id = wx.ID_ANY, label = _("Edit"))
+ self.editButton.Bind(wx.EVT_BUTTON, self.OnEdit)
+ self.removeButton = wx.Button(self, id = wx.ID_ANY, label = _("Remove"))
+ self.removeButton.Bind(wx.EVT_BUTTON, self.OnRemove)
+
+ self._updateListBox()
+
+ gridBagSizer.Add(self.listbox, pos = (0,0), span = (3, 1), flag = wx.ALIGN_CENTER_VERTICAL| wx.EXPAND, border = 0)
+ gridBagSizer.Add(self.addButton, pos = (0,1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+ gridBagSizer.Add(self.editButton, pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+ gridBagSizer.Add(self.removeButton, pos = (2,1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+ sizer.Add(gridBagSizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+ mainSizer.Add(item = sizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL, border = 5)
+
+ # buttons
+ self.btnOk = wx.Button(self, wx.ID_OK)
+ self.btnCancel = wx.Button(self, wx.ID_CANCEL)
+ self.btnOk.SetDefault()
+ self.btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
+ # button sizer
+ btnStdSizer = wx.StdDialogButtonSizer()
+ btnStdSizer.AddButton(self.btnOk)
+ btnStdSizer.AddButton(self.btnCancel)
+ btnStdSizer.Realize()
+
+ mainSizer.Add(item = btnStdSizer, proportion = 0,
+ flag = wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border = 5)
+
+ self.SetSizer(mainSizer)
+ mainSizer.Fit(self)
+
+ def _updateListBox(self):
+ self.listbox.Clear()
+ for anim in self.animationData:
+ self.listbox.Append(anim.name, clientData = anim)
+ self.listbox.SetSelection(0)
+
+ def _getNextIndex(self):
+ indices = [anim.windowIndex for anim in self.animationData]
+ for i in range(self.maxAnimations):
+ if i not in indices:
+ return i
+ return None
+
+ def OnAdd(self, event):
+ windowIndex = self._getNextIndex()
+ if windowIndex is None:
+ GMessage(self, message = _("Maximum number of animations is %d.") % self.maxAnimations)
+ return
+ animData = AnimationData()
+ # number of active animations
+ animationIndex = len(self.animationData)
+ animData.SetDefaultValues(windowIndex, animationIndex)
+ dlg = InputDialog(parent = self, mode = 'add', animationData = animData)
+ if dlg.ShowModal() == wx.ID_CANCEL:
+ dlg.Destroy()
+ return
+ dlg.Destroy()
+ self.animationData.append(animData)
+
+ self._updateListBox()
+
+
+ def OnEdit(self, event):
+ index = self.listbox.GetSelection()
+ if index == wx.NOT_FOUND:
+ return
+
+ animData = self.listbox.GetClientData(index)
+ dlg = InputDialog(parent = self, mode = 'edit', animationData = animData)
+ if dlg.ShowModal() == wx.ID_CANCEL:
+ dlg.Destroy()
+ return
+ dlg.Destroy()
+
+ self._updateListBox()
+
+ def OnRemove(self, event):
+ index = self.listbox.GetSelection()
+ if index == wx.NOT_FOUND:
+ return
+
+ animData = self.listbox.GetClientData(index)
+ self.animationData.remove(animData)
+
+ self._updateListBox()
+
+ def GetResult(self):
+ return self.result
+
+ def OnOk(self, event):
+ indices = set([anim.windowIndex for anim in self.animationData])
+ if len(indices) != len(self.animationData):
+ GError(parent = self, message = _("More animations are using one window."
+ " Please select different window for each animation."))
+ return
+ try:
+ temporalMode, tempManager = self.eval(self.animationData)
+ except GException, e:
+ GError(parent = self, message = e.value, showTraceback = False)
+ return
+ self.result = (self.animationData, temporalMode, tempManager)
+
+ self.EndModal(wx.ID_OK)
+
+
+class AnimationData(object):
+ def __init__(self):
+ self._inputMapTypes = [('rast', _("Multiple raster maps")),
+ ('vect', _("Multiple vector maps")),
+ ('strds', _("Space time raster dataset")),
+ ('stvds', _("Space time vector dataset"))]
+ self._inputMapType = 'rast'
+ self.inputData = None
+ self.mapData = None
+ self._viewModes = [('2d', _("2D view")),
+ ('3d', _("3D view"))]
+ self.viewMode = '2d'
+
+ self.nvizTask = NvizTask()
+ self._nvizParameters = self.nvizTask.ListMapParameters()
+ self.nvizParameter = self._nvizParameters[0]
+
+ self.workspaceFile = None
+
+ def GetName(self):
+ return self._name
+
+ def SetName(self, name):
+ if name == '':
+ raise ValueError(_("No animation name selected."))
+ self._name = name
+
+ name = property(fget = GetName, fset = SetName)
+
+ def GetWindowIndex(self):
+ return self._windowIndex
+
+ def SetWindowIndex(self, windowIndex):
+ self._windowIndex = windowIndex
+
+ windowIndex = property(fget = GetWindowIndex, fset = SetWindowIndex)
+
+ def GetInputMapTypes(self):
+ return self._inputMapTypes
+
+ inputMapTypes = property(fget = GetInputMapTypes)
+
+ def GetInputMapType(self):
+ return self._inputMapType
+
+ def SetInputMapType(self, itype):
+ if itype in [each[0] for each in self.inputMapTypes]:
+ self._inputMapType = itype
+ else:
+ raise ValueError("Bad input type.")
+
+ inputMapType = property(fget = GetInputMapType, fset = SetInputMapType)
+
+ def GetInputData(self):
+ return self._inputData
+
+ def SetInputData(self, data):
+ if data == '':
+ raise ValueError(_("No data selected."))
+ if data is None:
+ self._inputData = data
+ return
+
+ if self.inputMapType in ('rast', 'vect'):
+ maps = data.split(',')
+ newNames = validateMapNames(maps, self.inputMapType)
+ self._inputData = ','.join(newNames)
+ self.mapData = newNames
+
+ elif self.inputMapType in ('strds', 'stvds'):
+ timeseries = validateTimeseriesName(data, etype = self.inputMapType)
+ if self.inputMapType == 'strds':
+ sp = tgis.SpaceTimeRasterDataset(ident = timeseries)
+ elif self.inputMapType == 'stvds':
+ sp = tgis.SpaceTimeRasterDataset(ident = timeseries)
+ # else ?
+ sp.select()
+ rows = sp.get_registered_maps(columns = "id", where = None, order = "start_time", dbif = None)
+ timeseriesMaps = []
+ if rows:
+ for row in rows:
+ timeseriesMaps.append(row["id"])
+ self._inputData = timeseries
+ self.mapData = timeseriesMaps
+ else:
+ self._inputData = data
+
+ inputData = property(fget = GetInputData, fset = SetInputData)
+
+ def SetMapData(self, data):
+ self._mapData = data
+
+ def GetMapData(self):
+ return self._mapData
+
+ mapData = property(fget = GetMapData, fset = SetMapData)
+
+ def GetWorkspaceFile(self):
+ return self._workspaceFile
+
+ def SetWorkspaceFile(self, fileName):
+ if fileName is None:
+ self._workspaceFile = None
+ return
+
+ if fileName == '':
+ raise ValueError(_("No workspace file selected."))
+
+ if not os.path.exists(fileName):
+ raise IOError(_("File %s not found") % fileName)
+ self._workspaceFile = fileName
+
+ self.nvizTask.Load(self.workspaceFile)
+
+ workspaceFile = property(fget = GetWorkspaceFile, fset = SetWorkspaceFile)
+
+ def SetDefaultValues(self, windowIndex, animationIndex):
+ self.windowIndex = windowIndex
+ self.name = _("Animation %d") % (animationIndex + 1)
+
+ def GetNvizParameters(self):
+ return self._nvizParameters
+
+ nvizParameters = property(fget = GetNvizParameters)
+
+ def GetNvizParameter(self):
+ return self._nvizParameter
+
+ def SetNvizParameter(self, param):
+ self._nvizParameter = param
+
+ nvizParameter = property(fget = GetNvizParameter, fset = SetNvizParameter)
+
+ def GetViewMode(self):
+ return self._viewMode
+
+ def SetViewMode(self, mode):
+ self._viewMode = mode
+
+ viewMode = property(fget = GetViewMode, fset = SetViewMode)
+
+ def GetViewModes(self):
+ return self._viewModes
+
+ viewModes = property(fget = GetViewModes)
+
+ def GetNvizCommands(self):
+ if not self.workspaceFile or not self.mapData:
+ return []
+
+ cmds = self.nvizTask.GetCommandSeries(series = self.mapData, paramName = self.nvizParameter)
+
+ return cmds
+
+ def __repr__(self):
+ return "%s(%r)" % (self.__class__, self.__dict__)
+
+
+def test():
+ import wx.lib.inspection
+ import gettext
+ gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+
+ import grass.script as grass
+
+ app = wx.PySimpleApp()
+ anim = AnimationData()
+ anim.SetDefaultValues(animationIndex = 0, windowIndex = 0)
+
+ dlg = InputDialog(parent = None, mode = 'add', animationData = anim)
+ # dlg = EditDialog(parent = None, animationData = [anim])
+ wx.lib.inspection.InspectionTool().Show()
+
+ dlg.Show()
+ # if val == wx.ID_OK:
+ # dlg.Update()
+ # # print anim
+
+ # dlg.Destroy()
+ print anim
+ app.MainLoop()
+
+if __name__ == '__main__':
+
+ test()
\ No newline at end of file
Added: grass-addons/grass7/gui/wxpython/wx.animation/animation/frame.py
===================================================================
--- grass-addons/grass7/gui/wxpython/wx.animation/animation/frame.py (rev 0)
+++ grass-addons/grass7/gui/wxpython/wx.animation/animation/frame.py 2012-10-17 12:24:37 UTC (rev 53437)
@@ -0,0 +1,518 @@
+"""!
+ at package animation.frame
+
+ at brief Animation frame and different types of sliders
+
+Classes:
+ - frame::SwipeMapDialog
+ - frame::AnimationFrame
+ - frame::AnimationsPanel
+ - frame::AnimationSliderBase
+ - frame::SimpleAnimationSlider
+ - frame::TimeAnimationSlider
+
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+import os
+import sys
+import wx
+import wx.aui
+
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.environ['GISBASE'], "etc", "gui", "wxpython"))
+from core import globalvar
+from gui_core.widgets import IntegerValidator
+from core.gcmd import RunCommand
+
+from mapwindow import AnimationWindow, BitmapProvider, BitmapPool
+from controller import AnimationController
+from animation import Animation
+from toolbars import MainToolbar, AnimationToolbar, MiscToolbar
+from dialogs import SpeedDialog, EVT_SPEED_CHANGED
+from utils import Orientation, ReplayMode, TemporalType
+
+
+MAX_COUNT = 4
+
+class AnimationFrame(wx.Frame):
+ def __init__(self, parent = None, id = wx.ID_ANY, title = _("Animation tool"), rasters = None, timeseries = None):
+ wx.Frame.__init__(self, parent, id, title = title, style = wx.DEFAULT_FRAME_STYLE, size = (800, 600))
+
+ self.SetClientSize(self.GetSize())
+ self.iconsize = (16, 16)
+
+ self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
+
+ self.animations = [Animation() for i in range(MAX_COUNT)]
+ self.windows = []
+ self.animationPanel = AnimationsPanel(self, self.windows, initialCount = MAX_COUNT)
+ bitmapPool = BitmapPool()
+ self.providers = [BitmapProvider(frame = self, bitmapPool = bitmapPool) for i in range(MAX_COUNT)]
+ self.animationSliders = {}
+ self.animationSliders['nontemporal'] = SimpleAnimationSlider(self)
+ self.animationSliders['temporal'] = TimeAnimationSlider(self)
+ self.controller = AnimationController(frame = self,
+ sliders = self.animationSliders,
+ animations = self.animations,
+ mapwindows = self.windows,
+ providers = self.providers,
+ bitmapPool = bitmapPool)
+ for win, provider in zip(self.windows, self.providers):
+ win.Bind(wx.EVT_SIZE, lambda event, prov = provider: prov.WindowSizeChanged(event))
+
+ self.InitStatusbar()
+ self._mgr = wx.aui.AuiManager(self)
+
+ # toolbars
+ self.toolbars = {}
+ tb = ['miscToolbar', 'animationToolbar', 'mainToolbar']
+ if sys.platform == 'win32':
+ tb.reverse()
+ for toolb in tb:
+ self._addToolbar(toolb)
+
+ self._addPanes()
+ self._mgr.Update()
+
+ self.dialogs = dict()
+ self.dialogs['speed'] = None
+
+ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+
+ def InitStatusbar(self):
+ """!Init statusbar."""
+ statusbar = self.CreateStatusBar(number = 2, style = 0)
+ statusbar.SetStatusWidths([-3, -2])
+
+ def _addPanes(self):
+ self._mgr.AddPane(self.animationPanel, wx.aui.AuiPaneInfo().CentrePane().
+ Name('animPanel').CentrePane().CaptionVisible(False).PaneBorder(False).
+ Floatable(False).BestSize((-1,-1)).
+ CloseButton(False).DestroyOnClose(True).Layer(0))
+ for name, slider in self.animationSliders.iteritems():
+ self._mgr.AddPane(slider, wx.aui.AuiPaneInfo().PaneBorder(False).Name('slider_' + name).
+ Layer(1).CaptionVisible(False).BestSize(slider.GetBestSize()).
+ DestroyOnClose(True).CloseButton(False).Bottom())
+ self._mgr.GetPane('slider_' + name).Hide()
+
+ def _addToolbar(self, name):
+ """!Add defined toolbar to the window
+
+ Currently known toolbars are:
+ - 'mainToolbar' - data management
+ - 'animationToolbar' - animation controls
+ - 'miscToolbar' - help, close
+ """
+ if name == "mainToolbar":
+ self.toolbars[name] = MainToolbar(self)
+ self._mgr.AddPane(self.toolbars[name],
+ wx.aui.AuiPaneInfo().
+ Name('mainToolbar').Caption(_("Main Toolbar")).
+ ToolbarPane().Top().
+ LeftDockable(False).RightDockable(False).
+ BottomDockable(True).TopDockable(True).
+ CloseButton(False).Layer(2).Row(1).
+ BestSize((self.toolbars['mainToolbar'].GetBestSize())))
+ elif name == 'animationToolbar':
+ self.toolbars[name] = AnimationToolbar(self)
+ self._mgr.AddPane(self.toolbars[name],
+ wx.aui.AuiPaneInfo().
+ Name('animationToolbar').Caption(_("Animation Toolbar")).
+ ToolbarPane().Top().
+ LeftDockable(False).RightDockable(False).
+ BottomDockable(True).TopDockable(True).
+ CloseButton(False).Layer(2).Row(1).
+ BestSize((self.toolbars['animationToolbar'].GetBestSize())))
+ self.controller.SetAnimationToolbar(self.toolbars['animationToolbar'])
+ elif name == 'miscToolbar':
+ self.toolbars[name] = MiscToolbar(self)
+ self._mgr.AddPane(self.toolbars[name],
+ wx.aui.AuiPaneInfo().
+ Name('miscToolbar').Caption(_("Misc Toolbar")).
+ ToolbarPane().Top().
+ LeftDockable(False).RightDockable(False).
+ BottomDockable(True).TopDockable(True).
+ CloseButton(False).Layer(2).Row(1).
+ BestSize((self.toolbars['miscToolbar'].GetBestSize())))
+ self.controller.SetAnimationToolbar(self.toolbars['miscToolbar'])
+
+ def SetAnimations(self, raster = None, strds = None):
+ self.controller.SetAnimations(raster, strds)
+
+ def OnAddAnimation(self, event):
+ self.controller.AddAnimation()
+
+ def AddWindow(self, index):
+ self.animationPanel.AddWindow(index)
+
+ def RemoveWindow(self, index):
+ self.animationPanel.RemoveWindow(index)
+
+ def IsWindowShown(self, index):
+ return self.animationPanel.IsWindowShown(index)
+
+ def OnEditAnimation(self, event):
+ self.controller.EditAnimations()
+
+ def SetSlider(self, name):
+ if name == 'nontemporal':
+ self._mgr.GetPane('slider_nontemporal').Show()
+ self._mgr.GetPane('slider_temporal').Hide()
+
+ elif name == 'temporal':
+ self._mgr.GetPane('slider_temporal').Show()
+ self._mgr.GetPane('slider_nontemporal').Hide()
+ else:
+ self._mgr.GetPane('slider_temporal').Hide()
+ self._mgr.GetPane('slider_nontemporal').Hide()
+ self._mgr.Update()
+
+ def OnPlayForward(self, event):
+ self.controller.SetOrientation(Orientation.FORWARD)
+ self.controller.StartAnimation()
+
+ def OnPlayBack(self, event):
+ self.controller.SetOrientation(Orientation.BACKWARD)
+ self.controller.StartAnimation()
+
+ def OnPause(self, event):
+ self.controller.PauseAnimation(paused = event.IsChecked())
+
+ def OnStop(self, event):
+ self.controller.EndAnimation()
+
+ def OnOneDirectionReplay(self, event):
+ if event.IsChecked():
+ mode = ReplayMode.REPEAT
+ else:
+ mode = ReplayMode.ONESHOT
+ self.controller.SetReplayMode(mode)
+
+ def OnBothDirectionReplay(self, event):
+ if event.IsChecked():
+ mode = ReplayMode.REVERSE
+ else:
+ mode = ReplayMode.ONESHOT
+ self.controller.SetReplayMode(mode)
+
+ def OnAdjustSpeed(self, event):
+ win = self.dialogs['speed']
+ if win:
+ win.SetTemporalMode(self.controller.GetTemporalMode())
+ win.SetTimeGranularity(self.controller.GetTimeGranularity())
+ win.InitTimeSpin(self.controller.GetTimeTick())
+ if win.IsShown():
+ win.SetFocus()
+ else:
+ win.Show()
+ else: # start
+ win = SpeedDialog(self, temporalMode = self.controller.GetTemporalMode(),
+ timeGranularity = self.controller.GetTimeGranularity(),
+ initialSpeed = self.controller.timeTick)
+ self.dialogs['speed'] = win
+ win.Bind(EVT_SPEED_CHANGED, self.OnChangeSpeed)
+ win.Show()
+
+ def OnChangeSpeed(self, event):
+ self.ChangeSpeed(ms = event.ms)
+
+ def ChangeSpeed(self, ms):
+ self.controller.timeTick = ms
+
+ def Reload(self, event):
+ self.controller.Reload()
+
+ def OnHelp(self, event):
+ RunCommand('g.manual',
+ quiet = True,
+ entry = 'wxGUI.Animation')
+
+ def OnCloseWindow(self, event):
+ self.Destroy()
+
+ def __del__(self):
+ if hasattr(self, 'controller') and hasattr(self.controller, 'timer'):
+ if self.controller.timer.IsRunning():
+ self.controller.timer.Stop()
+
+
+class AnimationsPanel(wx.Panel):
+ def __init__(self, parent, windows, initialCount = 4):
+ wx.Panel.__init__(self, parent, id = wx.ID_ANY, style = wx.NO_BORDER)
+ self.shown = []
+ self.count = initialCount
+ self.mainSizer = wx.FlexGridSizer(rows = 2, hgap = 0, vgap = 0)
+ for i in range(initialCount):
+ w = AnimationWindow(self)
+ windows.append(w)
+ self.mainSizer.Add(item = w, proportion = 1, flag = wx.EXPAND)
+
+ self.mainSizer.AddGrowableCol(0)
+ self.mainSizer.AddGrowableCol(1)
+ self.mainSizer.AddGrowableRow(0)
+ self.mainSizer.AddGrowableRow(1)
+ self.windows = windows
+ self.SetSizerAndFit(self.mainSizer)
+
+ for i in range(initialCount):
+ self.mainSizer.Hide(windows[i])
+ self.Layout()
+
+
+ def AddWindow(self, index):
+ if len(self.shown) == self.count:
+ return
+ self.mainSizer.Show(self.windows[index])
+ self.shown.append(index)
+ self.Layout()
+
+ def RemoveWindow(self, index):
+ if len(self.shown) == 0:
+ return
+ self.mainSizer.Hide(self.windows[index])
+ self.shown.remove(index)
+ self.Layout()
+
+ def IsWindowShown(self, index):
+ return self.mainSizer.IsShown(self.windows[index])
+
+
+class AnimationSliderBase(wx.Panel):
+ def __init__(self, parent):
+ wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
+ self.label1 = wx.StaticText(self, id = wx.ID_ANY)
+ self.slider = wx.Slider(self, id = wx.ID_ANY, style = wx.SL_HORIZONTAL)
+ self.indexField = wx.TextCtrl(self, id = wx.ID_ANY, size = (40, -1),
+ style = wx.TE_PROCESS_ENTER | wx.TE_RIGHT,
+ validator = IntegerValidator())
+
+ self.callbackSliderChanging = None
+ self.callbackSliderChanged = None
+ self.callbackFrameIndexChanged = None
+
+ self.framesCount = 0
+
+ self.enable = True
+
+ self.slider.Bind(wx.EVT_SPIN, self.OnSliderChanging)
+ self.slider.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnSliderChanged)
+ self.indexField.Bind(wx.EVT_TEXT_ENTER, self.OnFrameIndexChanged)
+
+ def UpdateFrame(self, index):
+ if not self.enable:
+ return
+
+ self.slider.SetValue(index)
+ self._updateFrameIndex(index)
+
+ def _updateFrameIndex(self, index):
+ raise NotImplementedError
+
+ def OnFrameIndexChanged(self, event):
+ self._onFrameIndexChanged(event)
+
+ def SetFrames(self, frames):
+ self._setFrames(frames)
+
+ def _setFrames(self, frames):
+ raise NotImplementedError
+
+ def SetCallbackSliderChanging(self, callback):
+ self.callbackSliderChanging = callback
+
+ def SetCallbackSliderChanged(self, callback):
+ self.callbackSliderChanged = callback
+
+ def SetCallbackFrameIndexChanged(self, callback):
+ self.callbackFrameIndexChanged = callback
+
+ def EnableSlider(self, enable = True):
+ self.enable = enable
+ self.slider.Enable(enable)
+ self.indexField.Enable(enable)
+
+ def OnSliderChanging(self, event):
+ self.callbackSliderChanging(event.GetInt())
+
+ def OnSliderChanged(self, event):
+ self.callbackSliderChanged()
+
+ def _onFrameIndexChanged(self, event):
+ index = self.indexField.GetValue()
+ index = self._validate(index)
+ if index is not None:
+ self.slider.SetValue(index)
+ self.callbackFrameIndexChanged(index)
+
+ def _validate(self, index):
+ try:
+ index = int(index)
+ except ValueError:
+ index = self.slider.GetValue()
+ self.indexField.SetValue(str(index + 1))
+ return None
+ start, end = self.slider.GetRange()
+ index -= 1
+ if index > end:
+ index = end
+ self.indexField.SetValue(str(end + 1))
+ elif index < start:
+ index = start
+ self.indexField.SetValue(str(start + 1))
+
+ return index
+
+
+class SimpleAnimationSlider(AnimationSliderBase):
+ def __init__(self, parent):
+ AnimationSliderBase.__init__(self, parent)
+
+ self._setLabel()
+ self._doLayout()
+
+ def _doLayout(self):
+ hbox = wx.BoxSizer(wx.HORIZONTAL)
+ hbox.Add(item = self.indexField, proportion = 0,
+ flag = wx.ALIGN_CENTER | wx.LEFT, border = 5)
+ hbox.Add(item = self.label1, proportion = 0,
+ flag = wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT, border = 5)
+ hbox.Add(item = self.slider, proportion = 1, flag = wx.ALIGN_CENTER| wx.EXPAND, border = 0)
+ self.SetSizerAndFit(hbox)
+
+ def _setFrames(self, count):
+ self.framesCount = count
+ self.slider.SetRange(0, self.framesCount - 1)
+ self._setLabel()
+
+ def _setLabel(self):
+ label = "/ %(framesCount)s" % {'framesCount': self.framesCount}
+ self.label1.SetLabel(label)
+ self.Layout()
+
+ def _updateFrameIndex(self, index):
+ self.indexField.SetValue(str(index + 1))
+
+
+class TimeAnimationSlider(AnimationSliderBase):
+ def __init__(self, parent):
+ AnimationSliderBase.__init__(self, parent)
+ self.timeLabels = []
+ self.label2 = wx.StaticText(self, id = wx.ID_ANY)
+ self.label3 = wx.StaticText(self, id = wx.ID_ANY)
+ self.label2Length = 0
+ self.temporalType = TemporalType.RELATIVE
+
+ self._setLabel()
+ self._doLayout()
+
+ def _doLayout(self):
+ vbox = wx.BoxSizer(wx.VERTICAL)
+ hbox = wx.BoxSizer(wx.HORIZONTAL)
+ hbox.Add(item = self.label1, proportion = 0,
+ flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+ hbox.AddStretchSpacer()
+ hbox.Add(item = self.indexField, proportion = 0,
+ flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+ hbox.Add(item = self.label2, proportion = 0,
+ flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border = 3)
+ hbox.AddStretchSpacer()
+ hbox.Add(item = self.label3, proportion = 0,
+ flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+ vbox.Add(item = hbox, proportion = 0, flag = wx.EXPAND, border = 0)
+
+ hbox = wx.BoxSizer(wx.HORIZONTAL)
+ hbox.Add(item = self.slider, proportion = 1, flag = wx.ALIGN_CENTER | wx.EXPAND, border = 0)
+ vbox.Add(item = hbox, proportion = 0, flag = wx.EXPAND, border = 0)
+
+ self._setTemporalType()
+ self.SetSizerAndFit(vbox)
+
+ def _setTemporalType(self):
+ sizer = self.indexField.GetContainingSizer()
+ # sizer.Show(self.indexField, False) # TODO: what to do?
+ sizer.Show(self.indexField, self.temporalType == TemporalType.RELATIVE)
+ self.Layout()
+
+ def SetTemporalType(self, mode):
+ self.temporalType = mode
+ self._setTemporalType()
+
+ def _setFrames(self, timeLabels):
+ self.timeLabels = timeLabels
+ self.framesCount = len(timeLabels)
+ self.slider.SetRange(0, self.framesCount - 1)
+ self._setLabel()
+
+ def _setLabel(self):
+ if self.timeLabels:
+ if self.temporalType == TemporalType.ABSOLUTE:
+ start = self.timeLabels[0][0]
+ self.label1.SetLabel(start)
+ if self.timeLabels[-1][1]:
+ end = self.timeLabels[-1][1]
+ else:
+ end = self.timeLabels[-1][0]
+ self.label3.SetLabel(end)
+ else:
+ unit = self.timeLabels[0][2]
+ start = self.timeLabels[0][0]
+ self.label1.SetLabel(start)
+ if self.timeLabels[-1][1]:
+ end = self.timeLabels[-1][1]
+ else:
+ end = self.timeLabels[-1][0]
+ end = "%(end)s %(unit)s" % {'end': end, 'unit': unit}
+ self.label3.SetLabel(end)
+
+ self.label2Length = len(start)
+ self._updateFrameIndex(0)
+
+ else:
+ self.label1.SetLabel("")
+ self.label2.SetLabel("")
+ self.label3.SetLabel("")
+ self.Layout()
+
+ def _updateFrameIndex(self, index):
+ start = self.timeLabels[index][0]
+ if self.timeLabels[index][1]: # interval
+ if self.temporalType == TemporalType.ABSOLUTE:
+ label = _("%(from)s %(dash)s %(to)s") % {'from': start, 'dash': u"\u2013", 'to': self.timeLabels[index][1]}
+ else:
+ label = _("to %(to)s") % {'to': self.timeLabels[index][1]}
+ else:
+ if self.temporalType == TemporalType.ABSOLUTE:
+ label = start
+ else:
+ label = ''
+ self.label2.SetLabel(label)
+ if self.temporalType == TemporalType.RELATIVE:
+ self.indexField.SetValue(start)
+ if len(label) != self.label2Length:
+ self.label2Length = len(label)
+ self.Layout()
+
+def test():
+ import gettext
+ gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+
+ import grass.script as grass
+
+ app = wx.PySimpleApp()
+ wx.InitAllImageHandlers()
+
+ frame = AnimationFrame(parent = None)
+ frame.SetAnimations(raster = None, strds = None)
+
+ frame.Show()
+ app.MainLoop()
+
+if __name__ == '__main__':
+
+ test()
\ No newline at end of file
Added: grass-addons/grass7/gui/wxpython/wx.animation/animation/mapwindow.py
===================================================================
--- grass-addons/grass7/gui/wxpython/wx.animation/animation/mapwindow.py (rev 0)
+++ grass-addons/grass7/gui/wxpython/wx.animation/animation/mapwindow.py 2012-10-17 12:24:37 UTC (rev 53437)
@@ -0,0 +1,457 @@
+"""!
+ at package animation.mapwindow
+
+ at brief Animation window and bitmaps management
+
+Classes:
+ - mapwindow::BufferedWindow
+ - mapwindow::AnimationWindow
+ - mapwindow::BitmapProvider
+ - mapwindow::BitmapPool
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+import os
+import wx
+
+import grass.script as grass
+from core.gcmd import RunCommand
+
+class BufferedWindow(wx.Window):
+ """
+ A Buffered window class (http://wiki.wxpython.org/DoubleBufferedDrawing).
+
+ To use it, subclass it and define a Draw(DC) method that takes a DC
+ to draw to. In that method, put the code needed to draw the picture
+ you want. The window will automatically be double buffered, and the
+ screen will be automatically updated when a Paint event is received.
+
+ When the drawing needs to change, you app needs to call the
+ UpdateDrawing() method. Since the drawing is stored in a bitmap, you
+ can also save the drawing to file by calling the
+ SaveToFile(self, file_name, file_type) method.
+
+ """
+ def __init__(self, *args, **kwargs):
+ # make sure the NO_FULL_REPAINT_ON_RESIZE style flag is set.
+ kwargs['style'] = kwargs.setdefault('style', wx.NO_FULL_REPAINT_ON_RESIZE) | wx.NO_FULL_REPAINT_ON_RESIZE
+ wx.Window.__init__(self, *args, **kwargs)
+
+ wx.EVT_PAINT(self, self.OnPaint)
+ wx.EVT_SIZE(self, self.OnSize)
+ self._Buffer = wx.EmptyBitmap(0, 0)
+ # OnSize called to make sure the buffer is initialized.
+ # This might result in OnSize getting called twice on some
+ # platforms at initialization, but little harm done.
+ self.OnSize(None)
+
+ def Draw(self, dc):
+ ## just here as a place holder.
+ ## This method should be over-ridden when subclassed
+ pass
+
+ def OnPaint(self, event):
+ # All that is needed here is to draw the buffer to screen
+ dc = wx.BufferedPaintDC(self, self._Buffer)
+
+ def OnSize(self, event):
+ # The Buffer init is done here, to make sure the buffer is always
+ # the same size as the Window
+ #Size = self.GetClientSizeTuple()
+ size = self.ClientSize
+
+ # Make new offscreen bitmap: this bitmap will always have the
+ # current drawing in it, so it can be used to save the image to
+ # a file, or whatever.
+ self._Buffer = wx.EmptyBitmap(*size)
+ self.UpdateDrawing()
+ # event.Skip()
+
+ def SaveToFile(self, FileName, FileType=wx.BITMAP_TYPE_PNG):
+ ## This will save the contents of the buffer
+ ## to the specified file. See the wxWindows docs for
+ ## wx.Bitmap::SaveFile for the details
+ self._Buffer.SaveFile(FileName, FileType)
+
+ def UpdateDrawing(self):
+ """
+ This would get called if the drawing needed to change, for whatever reason.
+
+ The idea here is that the drawing is based on some data generated
+ elsewhere in the system. If that data changes, the drawing needs to
+ be updated.
+
+ This code re-draws the buffer, then calls Update, which forces a paint event.
+ """
+ dc = wx.MemoryDC()
+ dc.SelectObject(self._Buffer)
+ self.Draw(dc)
+ del dc # need to get rid of the MemoryDC before Update() is called.
+ self.Refresh()
+ self.Update()
+
+
+class AnimationWindow(BufferedWindow):
+ def __init__(self, parent, id = wx.ID_ANY,
+ style = wx.DEFAULT_FRAME_STYLE | wx.FULL_REPAINT_ON_RESIZE | wx.BORDER_RAISED):
+ self.bitmap = wx.EmptyBitmap(0, 0)
+ self.x = self.y = 0
+ self.text = ''
+ self.size = wx.Size()
+ self.rescaleNeeded = False
+ self.region = None
+ self.parent = parent
+
+ BufferedWindow.__init__(self, parent = parent, id = id, style = style)
+ self.SetBackgroundColour(wx.BLACK)
+ self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
+ self.Bind(wx.EVT_SIZE, self.OnSize)
+
+ def Draw(self, dc):
+ """!Draws bitmap."""
+ dc.Clear() # make sure you clear the bitmap!
+ dc.DrawBitmap(self.bitmap, x = self.x, y = self.y)
+ dc.DrawText(self.text, 0, 0)
+
+ def OnSize(self, event):
+ self._computeBitmapCoordinates()
+
+ self.DrawBitmap(self.bitmap, self.text)
+
+ BufferedWindow.OnSize(self, event)
+ if event:
+ event.Skip()
+
+ def IsRescaled(self):
+ return self.rescaleNeeded
+
+ def _rescaleIfNeeded(self, bitmap):
+ """!If the bitmap has different size than the window, rescale it."""
+ bW, bH = bitmap.GetSize()
+ wW, wH = self.size
+ if abs(bW - wW) > 5 and abs(bH - wH) > 5:
+ self.rescaleNeeded = True
+ im = wx.ImageFromBitmap(bitmap)
+ im.Rescale(*self.size)
+ bitmap = wx.BitmapFromImage(im)
+ else:
+ self.rescaleNeeded = False
+ return bitmap
+
+ def DrawBitmap(self, bitmap, text):
+ """!Draws bitmap.
+ Does not draw the bitmap if it is the same one as last time.
+ """
+ bmp = self._rescaleIfNeeded(bitmap)
+ if self.bitmap == bmp:
+ return
+
+ self.bitmap = bmp
+ self.text = text
+ self.UpdateDrawing()
+
+ def _computeBitmapCoordinates(self):
+ """!Computes where to place the bitmap
+ to be in the center of the window."""
+ if not self.region:
+ return
+
+ cols = self.region['cols']
+ rows = self.region['rows']
+
+ ratioB = cols / float(rows)
+ ww, wh = self.GetClientSize()
+ try:
+ ratioW = ww / float(wh)
+ except ZeroDivisionError:
+ self.x = self.y = 0
+ self.size = (0, 0)
+ return
+ if ratioW > ratioB:
+ self.size = (wh * ratioB, wh)
+ self.y = 0
+ self.x = (ww - wh * ratioB) / 2
+ else:
+ self.size = (ww, ww / ratioB)
+ self.x = 0
+ self.y = (wh - ww / ratioB) / 2
+
+ def SetRegion(self, region):
+ """!Sets region for size computations.
+ Region is set from outside to avoid calling g.region multiple times.
+ """
+ self.region = region
+ self._computeBitmapCoordinates()
+
+class BitmapProvider(object):
+ """!Class responsible for loading data and providing bitmaps"""
+ def __init__(self, frame, bitmapPool):
+
+ self.datasource = None
+ self.dataNames = None
+ self.dataType = None
+ self.bitmapPool = bitmapPool
+ self.frame = frame
+ self.size = wx.Size()
+ self.loadSize = wx.Size()
+
+ self.suffix = ''
+
+ def GetDataNames(self):
+ return self.dataNames
+
+ def SetData(self, datasource, dataNames = None, dataType = 'rast', suffix = ''):
+ """!Sets data.
+
+ @param datasource data to load (raster maps, m.nviz.image commands)
+ @param dataNames data labels (keys)
+ @param dataType 'rast', 'nviz'
+ """
+ self.datasource = datasource
+ self.dataType = dataType
+ self.suffix = suffix
+ # add postfix _nviz, _image if needed
+ if dataNames:
+ self.dataNames = dataNames
+ else:
+ self.dataNames = datasource
+
+ self.dataNames = [name + self.suffix for name in self.dataNames]
+
+ def GetBitmap(self, dataId):
+ """!Returns bitmap with given key
+ or 'no data' bitmap if no such key exists.
+
+ @param dataId name of bitmap
+ """
+ dataId += self.suffix
+ try:
+ bitmap = self.bitmapPool[dataId]
+ except KeyError:
+ bitmap = self.bitmapPool[None]
+ return bitmap
+
+ def GetLoadSize(self):
+ return self.loadSize
+
+ def WindowSizeChanged(self, event):
+ """!Sets size when size of related window changes."""
+ self.size = event.GetSize()
+ event.Skip()
+
+ def _createNoDataBitmap(self, ncols, nrows):
+ """!Creates 'no data' bitmap.
+
+ Used when requested bitmap is not available (loading data was not successful) or
+ we want to show 'no data' bitmap.
+ """
+ bitmap = wx.EmptyBitmap(ncols, nrows)
+ dc = wx.MemoryDC()
+ dc.SelectObject(bitmap)
+ dc.Clear()
+ text = _("No data")
+ dc.SetFont(wx.Font(pointSize = 40, family = wx.FONTFAMILY_SCRIPT,
+ style = wx.FONTSTYLE_NORMAL, weight = wx.FONTWEIGHT_BOLD))
+ tw, th = dc.GetTextExtent(text)
+ dc.DrawText(text, (ncols-tw)/2, (nrows-th)/2)
+ dc.SelectObject(wx.NullBitmap)
+ return bitmap
+
+ def Load(self, force = False):
+ """!Loads data.
+
+ Shows progress dialog.
+
+ @param force if True reload all data, otherwise only missing data
+ """
+ count, maxLength = self._dryLoad(rasters = self.datasource,
+ names = self.dataNames, force = force)
+ progress = None
+ if self.dataType == 'rast' and count > 5 or \
+ self.dataType == 'nviz':
+ progress = wx.ProgressDialog(title = "Loading data",
+ message = " " * (maxLength + 20), # ?
+ maximum = count,
+ parent = self.frame,
+ style = wx.PD_CAN_ABORT | wx.PD_APP_MODAL |
+ wx.PD_AUTO_HIDE | wx.PD_SMOOTH)
+ updateFunction = progress.Update
+ else:
+ updateFunction = None
+
+ if self.dataType == 'rast':
+ size, scale = self._computeScale()
+ # loading ...
+ self._loadRasters(rasters = self.datasource, names = self.dataNames,
+ size = size, scale = scale, force = force, updateFunction = updateFunction)
+ elif self.dataType == 'nviz':
+ self._load3D(commands = self.datasource, names = self.dataNames,
+ force = force, updateFunction = updateFunction)
+ if progress:
+ progress.Destroy()
+
+ def Unload(self):
+ self.datasource = None
+ self.dataNames = None
+ self.dataType = None
+
+ def _computeScale(self):
+ """!Computes parameters for creating bitmaps."""
+ region = grass.region()
+ ncols, nrows = region['cols'], region['rows']
+ if nrows > ncols:
+ longdim = nrows
+ size = self.size[1]
+ else:
+ longdim = ncols
+ size = self.size[0]
+ scale = 1.0
+
+ if longdim > size:
+ scale = float(size) / longdim
+ elif longdim < size:
+ scale = float(size) / longdim
+ size = (int(ncols * scale), int(nrows * scale))
+
+ return (size, scale)
+
+ def _dryLoad(self, rasters, names, force):
+ """!Tries how many bitmaps will be loaded.
+ Used for progress dialog.
+
+ @param rasters raster maps to be loaded
+ @param names names used as keys for bitmaps
+ @param force load everything even though it is already there
+ """
+ count = 0
+ maxLength = 0
+ for raster, name in zip(rasters, names):
+ if not(name in self.bitmapPool and force is False):
+ count += 1
+ if len(raster) > maxLength:
+ maxLength = len(raster)
+
+ return count, maxLength
+
+ def _loadRasters(self, rasters, names, size, scale, force, updateFunction):
+ """!Loads rasters (also rasters from temporal dataset).
+
+ Uses r.out.ppm.
+
+ @param rasters raster maps to be loaded
+ @param names names used as keys for bitmaps
+ @param size size of new bitmaps
+ @param scale used for adjustment of region resolution for r.out.ppm
+ @param force load everything even though it is already there
+ @param updateFunction function called for updating progress dialog
+ """
+ region = grass.region()
+ for key in ('rows', 'cols', 'cells'):
+ region.pop(key)
+ region['nsres'] /= scale
+ region['ewres'] /= scale
+ os.environ['GRASS_REGION'] = grass.region_env(**region)
+ ncols, nrows = size
+ self.loadSize = size
+ count = 0
+ # create no data bitmap
+ if None not in self.bitmapPool or force:
+ self.bitmapPool[None] = self._createNoDataBitmap(ncols, nrows)
+ for raster, name in zip(rasters, names):
+ if name in self.bitmapPool and force is False:
+ continue
+ count += 1
+ # RunCommand has problem with DecodeString
+ returncode, stdout, messages = read2_command('r.out.ppm', input = raster,
+ flags = 'h', output = '-', quiet = True)
+ if returncode != 0:
+ self.bitmapPool[name] = wx.EmptyBitmap(ncols, nrows)
+ continue
+
+ bitmap = wx.BitmapFromBuffer(ncols, nrows, stdout)
+ self.bitmapPool[name] = bitmap
+
+ if updateFunction:
+ keepGoing, skip = updateFunction(count, raster)
+ if not keepGoing:
+ break
+
+ os.environ.pop('GRASS_REGION')
+
+ def _load3D(self, commands, names, force, updateFunction):
+ """!Load 3D view images using m.nviz.image.
+
+ @param commands
+ @param names names used as keys for bitmaps
+ @param force load everything even though it is already there
+ @param updateFunction function called for updating progress dialog
+ """
+ ncols, nrows = self.size
+ self.loadSize = ncols, nrows
+ count = 0
+ format = 'ppm'
+ tempFile = grass.tempfile(False)
+ tempFileFormat = tempFile + '.' + format
+ # create no data bitmap
+ if None not in self.bitmapPool or force:
+ self.bitmapPool[None] = self._createNoDataBitmap(ncols, nrows)
+ for command, name in zip(commands, names):
+ if name in self.bitmapPool and force is False:
+ continue
+ count += 1
+ # set temporary file
+ command[1]['output'] = tempFile
+ # set size
+ command[1]['size'] = '%d,%d' % (ncols, nrows)
+ # set format
+ command[1]['format'] = format
+
+ returncode, messages = RunCommand(getErrorMsg = True, prog = command[0], **command[1])
+ if returncode != 0:
+ self.bitmapPool[name] = wx.EmptyBitmap(ncols, nrows)
+ continue
+
+
+ self.bitmapPool[name] = wx.Bitmap(tempFileFormat)
+
+ if updateFunction:
+ keepGoing, skip = updateFunction(count, name)
+ if not keepGoing:
+ break
+ grass.try_remove(tempFileFormat)
+
+class BitmapPool():
+ """!Class storing bitmaps (emulates dictionary)"""
+ def __init__(self):
+ self.bitmaps = {}
+
+ def __getitem__(self, key):
+ return self.bitmaps[key]
+
+ def __setitem__(self, key, bitmap):
+ self.bitmaps[key] = bitmap
+
+ def __contains__(self, key):
+ return key in self.bitmaps
+
+ def Clear(self, usedKeys):
+ """!Removes all bitmaps which are currentlu not used.
+
+ @param usedKeys keys which are currently used
+ """
+ for key in self.bitmaps.keys():
+ if key not in usedKeys and key is not None:
+ del self.bitmaps[key]
+
+
+def read2_command(*args, **kwargs):
+ kwargs['stdout'] = grass.PIPE
+ kwargs['stderr'] = grass.PIPE
+ ps = grass.start_command(*args, **kwargs)
+ stdout, stderr = ps.communicate()
+ return ps.returncode, stdout, stderr
\ No newline at end of file
Added: grass-addons/grass7/gui/wxpython/wx.animation/animation/nviztask.py
===================================================================
--- grass-addons/grass7/gui/wxpython/wx.animation/animation/nviztask.py (rev 0)
+++ grass-addons/grass7/gui/wxpython/wx.animation/animation/nviztask.py 2012-10-17 12:24:37 UTC (rev 53437)
@@ -0,0 +1,209 @@
+"""!
+ at package animation.nviztask
+
+ at brief Conversion from workspace file to m.nviz.image command
+
+Classes:
+ - nviztask::NvizTask
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import os
+import sys
+try:
+ import xml.etree.ElementTree as etree
+except ImportError:
+ import elementtree.ElementTree as etree # Python <= 2.4
+from pprint import pprint
+
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.environ['GISBASE'], "etc", "gui", "wxpython"))
+
+from core.workspace import ProcessWorkspaceFile
+from core.gcmd import RunCommand, GException
+from core.utils import GetLayerNameFromCmd, CmdToTuple
+from grass.script import task as gtask
+
+class NvizTask:
+ def __init__(self):
+ self.task = None
+ self.filename = None
+
+ def Load(self, filename):
+ self.task = gtask.grassTask("m.nviz.image")
+ self.filename = filename
+ try:
+ gxwXml = ProcessWorkspaceFile(etree.parse(self.filename))
+ except Exception, e:
+ raise GException(_("Reading workspace file <%s> failed.\n"
+ "Invalid file, unable to parse XML document.") % filename)
+ # for display in gxwXml.displays:
+ # pprint(display)
+ # for layer in gxwXml.layers:
+ # pprint(layer)
+ # pprint(gxwXml.nviz_state)
+
+ if not gxwXml.nviz_state:
+ raise GException(_("No 3d view information in workspace file <%s>.") % self.filename)
+
+ # self.task.set_param('output', "/home/anna/testy/nviz/out.ppm")
+
+ self._processState(gxwXml.nviz_state)
+ self._processLayers(gxwXml.layers)
+
+ def _processLayers(self, layers):
+ for layer in layers:
+ if not layer['checked']:
+ continue
+
+ if not layer['nviz']:
+ continue
+ layerName, found = GetLayerNameFromCmd(layer['cmd'], fullyQualified = False,
+ param = 'map')
+ if not found:
+ continue
+
+ if 'surface' in layer['nviz']:
+ self._processSurface(layer['nviz']['surface'], mapName = layerName)
+
+
+ def _processSurface(self, surface, mapName):
+ self._setMultiTaskParam('elevation_map', mapName)
+
+ # attributes like color, shine, transparency
+ attributes = ('color', 'shine', 'transp') # mask missing
+ parameters = (('color_map', 'color'),
+ ('shininess_map', 'shininess_value'),
+ ('transparency_map', 'transparency_value'))
+ for attr, params in zip(attributes, parameters):
+ mapname = None
+ const = None
+ if attr in surface['attribute']:
+ if surface['attribute'][attr]['map']:
+ mapname = surface['attribute'][attr]['value']
+ else:
+ const = surface['attribute'][attr]['value']
+ else:
+ if attr == 'transp':
+ const = 0
+ elif attr == 'color':
+ mapname = mapName
+
+ if mapname:
+ self._setMultiTaskParam(params[0], mapname)
+ else:
+ self._setMultiTaskParam(params[1], const)
+
+ # draw mode
+ for mode in ('mode', 'shading', 'style'):
+ value = surface['draw']['mode']['desc'][mode]
+ self._setMultiTaskParam(mode, value)
+ # wire color
+ value = surface['draw']['wire-color']['value']
+ self._setMultiTaskParam('wire_color', value)
+ # resolution
+ for mode1, mode2 in zip(('coarse', 'fine'), ('resolution_coarse', 'resolution_fine')):
+ value = surface['draw']['resolution'][mode1]
+ self._setMultiTaskParam(mode2, value)
+
+ # position
+ pos = []
+ for coor in ('x', 'y', 'z'):
+ pos.append(str(surface['position'][coor]))
+ value = ','.join(pos)
+ self._setMultiTaskParam('surface_position', value)
+
+ def _processState(self, state):
+ color = state['view']['background']['color']
+ self.task.set_param('bgcolor', self._join(color, delim = ':'))
+ self.task.set_param('position', self._join((state['view']['position']['x'],
+ state['view']['position']['y'])))
+ self.task.set_param('height', state['iview']['height']['value'])
+ self.task.set_param('perspective', state['view']['persp']['value'])
+ self.task.set_param('twist', state['view']['twist']['value'])
+ # TODO: fix zexag
+ self.task.set_param('zexag', state['view']['z-exag']['value'])
+ self.task.set_param('focus', self._join((state['iview']['focus']['x'],
+ state['iview']['focus']['y'],
+ state['iview']['focus']['z'])))
+ self.task.set_param('light_position', self._join((state['light']['position']['x'],
+ state['light']['position']['y'],
+ state['light']['position']['z']/100.)))
+ color = state['light']['color'][:3]
+ self.task.set_param('light_color', self._join(color, delim = ':'))
+ self.task.set_param('light_brightness', int(state['light']['bright']))
+ self.task.set_param('light_ambient', state['light']['ambient'])
+
+
+
+
+ def _setMultiTaskParam(self, param, value):
+ last = self.task.get_param(param)['value']
+ self.task.set_param(param, self._join((last, value)))
+
+ def _join(self, toJoin, delim = ','):
+ toJoin = filter(self._ignore, toJoin)
+ return delim.join(map(str, toJoin))
+
+ def _ignore(self, value):
+ if value == '' or value is None:
+ return False
+ else:
+ return True
+
+ def ListMapParameters(self):
+ # params = self.task.get_list_params()
+ # parameter with 'map' name
+ # params = filter(lambda x: 'map' in x, params)
+ return ('elevation_map', 'color_map', 'vline','vpoint')
+
+ def GetCommandSeries(self, series, paramName):
+ commands = []
+ if not self.task:
+ return commands
+
+ for value in series:
+ self.task.set_param(paramName, value)
+ self.task.set_flag('overwrite', True)
+ self.task.set_param('output', 'tobechanged')
+ cmd = self.task.get_cmd(ignoreErrors = False, ignoreRequired = False, ignoreDefault = True)
+ commands.append(CmdToTuple(cmd))
+
+ return commands
+
+ def GetCommand(self):
+ if not self.task:
+ return None
+ self.task.set_flag('overwrite', True)
+ self.task.set_param('output', 'tobechanged')
+ cmd = self.task.get_cmd(ignoreErrors = False, ignoreRequired = False, ignoreDefault = True)
+ return CmdToTuple(cmd)
+
+
+
+def test():
+ import gettext
+ gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+
+ nviz = NvizTask('/home/anna/testy/nviz/t12.gxw')
+ # nviz = NvizState('/home/anna/testy/nviz/t3.gxw')
+
+ # cmd = nviz.GetCommand()
+ cmds = nviz.GetCommandSeries(['aspect','elevation'], 'color_map')
+ for cmd in cmds:
+ print cmd
+ returncode, message = RunCommand(getErrorMsg = True, prog = cmd[0], **cmd[1])
+ print returncode, message
+
+
+if __name__ == '__main__':
+
+ test()
+
+
Added: grass-addons/grass7/gui/wxpython/wx.animation/animation/temporal_manager.py
===================================================================
--- grass-addons/grass7/gui/wxpython/wx.animation/animation/temporal_manager.py (rev 0)
+++ grass-addons/grass7/gui/wxpython/wx.animation/animation/temporal_manager.py 2012-10-17 12:24:37 UTC (rev 53437)
@@ -0,0 +1,514 @@
+"""!
+ at package animation.temporal_manager
+
+ at brief Management of temporal datasets used in animation
+
+Classes:
+ - temporal_manager::DataMode
+ - temporal_manager::TemporalMapTime
+ - temporal_manager::TemporalManager
+
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import os
+import sys
+
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.environ['GISBASE'], "etc", "gui", "wxpython"))
+
+import grass.script as grass
+import grass.temporal as tgis
+from core.gcmd import GException
+from utils import validateTimeseriesName, TemporalType
+
+
+class DataMode:
+ SIMPLE = 1
+ MULTIPLE = 2
+
+class TemporalMapTime:
+ POINT = 1
+ INTERVAL = 2
+
+
+class TemporalManager(object):
+ """!Class for temporal data processing."""
+ def __init__(self):
+ self.timeseriesList = []
+ self.timeseriesInfo = {}
+
+ self.dataMode = None
+ self.temporalType = None
+ self.temporalMapTime = None
+
+ # Make sure the temporal database exists
+ tgis.create_temporal_database()
+
+ def GetTemporalType(self):
+ """!Get temporal type (TemporalType.ABSOLUTE, TemporalType.RELATIVE)"""
+ return self._temporalType
+
+ def SetTemporalType(self, ttype):
+ self._temporalType = ttype
+
+ temporalType = property(fget = GetTemporalType, fset = SetTemporalType)
+
+ def AddTimeSeries(self, timeseries, etype):
+ """!Add space time dataset
+ and collect basic information about it.
+
+ Raises GException (e.g. with invalid topology).
+
+ @param timeseries name of timeseries (with or without mapset)
+ @param etype element type (strds, stvds)
+ """
+ self._gatherInformation(timeseries, etype, self.timeseriesList, self.timeseriesInfo)
+
+
+ def EvaluateInputData(self):
+ """!Checks if all timeseries are compatible (raises GException).
+
+ Sets internal variables.
+ """
+ timeseriesCount = len(self.timeseriesList)
+
+ if timeseriesCount == 1:
+ self.dataMode = DataMode.SIMPLE
+ elif timeseriesCount > 1:
+ self.dataMode = DataMode.MULTIPLE
+ else:
+ self.dataMode = None
+
+ ret, message = self._setTemporalState()
+ if not ret:
+ raise GException(message)
+
+
+ def _setTemporalState(self):
+ # check for absolute x relative
+ absolute, relative = 0, 0
+ for infoDict in self.timeseriesInfo.values():
+ if infoDict['temporal_type'] == 'absolute':
+ absolute += 1
+ else:
+ relative += 1
+ if bool(absolute) == bool(relative):
+ message = _("It is not allowed to display data with different temporal types (absolute and relative).")
+ return False, message
+ if absolute:
+ self.temporalType = TemporalType.ABSOLUTE
+ else:
+ self.temporalType = TemporalType.RELATIVE
+
+ # check for units for relative type
+ if relative:
+ units = set()
+ for infoDict in self.timeseriesInfo.values():
+ units.add(infoDict['unit'])
+ if len(units) > 1:
+ message = _("It is not allowed to display data with different units (%s).") % ','.join(units)
+ return False, message
+
+ # check for interval x point
+ interval, point = 0, 0
+ for infoDict in self.timeseriesInfo.values():
+ if infoDict['map_time'] == 'interval':
+ interval += 1
+ else:
+ point += 1
+ if bool(interval) == bool(point):
+ message = _("It is not allowed to display data with different temporal types of maps (interval and point).")
+ return False, message
+ if interval:
+ self.temporalMapTime = TemporalMapTime.INTERVAL
+ else:
+ self.temporalMapTime = TemporalMapTime.POINT
+
+ return True, None
+
+ def GetGranularity(self, original = True):
+ """!Returns temporal granularity of currently loaded timeseries."""
+ if self.dataMode == DataMode.SIMPLE:
+ gran = self.timeseriesInfo[self.timeseriesList[0]]['granularity']
+ if not original and 'unit' in self.timeseriesInfo[self.timeseriesList[0]]:
+ gran = "%(gran)s %(unit)s" % {'gran': gran,
+ 'unit': self.timeseriesInfo[self.timeseriesList[0]]['unit']}
+ return gran
+
+ if self.dataMode == DataMode.MULTIPLE:
+ return self._getCommonGranularity(original)
+
+ def _getCommonGranularity(self, original = True):
+ allMaps = []
+ for dataset in self.timeseriesList:
+ maps = self.timeseriesInfo[dataset]['maps']
+ allMaps.extend(maps)
+
+ if self.temporalType == TemporalType.ABSOLUTE:
+ return tgis.compute_absolute_time_granularity(allMaps)
+ if self.temporalType == TemporalType.RELATIVE:
+ gran = tgis.compute_relative_time_granularity(allMaps)
+ if not original:
+ gran = "%(gran)s %(unit)s" % {'gran': gran,
+ 'unit': self.timeseriesInfo[self.timeseriesList[0]]['unit']}
+ return gran
+
+
+ def GetLabelsAndMaps(self):
+ """!Returns time labels and map names.
+ """
+ mapLists = []
+ labelLists = []
+ for dataset in self.timeseriesList:
+ if self.temporalMapTime == TemporalMapTime.INTERVAL:
+ grassLabels, listOfMaps = self._getLabelsAndMapsInterval(dataset)
+ else:
+ grassLabels, listOfMaps = self._getLabelsAndMapsPoint(dataset)
+ mapLists.append(listOfMaps)
+ labelLists.append(grassLabels)
+
+ # choose longest time interval and fill missing maps with None
+ timestamps = max(labelLists, key = len)
+ newMapLists = []
+ for mapList, labelList in zip(mapLists, labelLists):
+ newMapList = [None] * len(timestamps)
+ i = 0
+ while timestamps[i] != labelList[0]:
+ i += 1
+ newMapList[i:i + len(mapList)] = mapList
+ newMapLists.append(newMapList)
+
+ mapDict = {}
+ for i, dataset in enumerate(self.timeseriesList):
+ mapDict[dataset] = newMapLists[i]
+
+ return timestamps, mapDict
+
+ def _getLabelsAndMapsInterval(self, timeseries):
+ """!Returns time labels and map names (done by sampling)
+ for interval data.
+ """
+ sp = tgis.dataset_factory(self.timeseriesInfo[timeseries]['etype'], timeseries)
+ # sp = tgis.SpaceTimeRasterDataset(ident = timeseries)
+ if sp.is_in_db() == False:
+ raise GException(_("Space time dataset <%s> not found.") % timeseries)
+ sp.select()
+
+ listOfMaps = []
+ timeLabels = []
+ gran = self.GetGranularity()
+ if self.temporalType == TemporalType.ABSOLUTE:
+ gran, unit = gran.split()
+ gran = '%(one)d %(unit)s' % {'one': 1, 'unit': unit}
+
+ unit = None
+ if self.temporalType == TemporalType.RELATIVE:
+ unit = self.timeseriesInfo[timeseries]['unit']
+ maps = sp.get_registered_maps_as_objects_by_granularity(gran = gran)
+ if maps is not None:
+ for map in maps:
+ if len(map) > 0:
+ timeseries = map[0].get_id()
+ start, end = map[0].get_valid_time()
+ listOfMaps.append(timeseries)
+ if end:
+ end = str(end)
+ timeLabels.append((str(start), end, unit))
+ else:
+ continue
+
+ if self.temporalType == TemporalType.ABSOLUTE:
+ timeLabels = self._pretifyTimeLabels(timeLabels)
+
+ return timeLabels, listOfMaps
+
+ def _getLabelsAndMapsPoint(self, timeseries):
+ """!Returns time labels and map names (done by sampling)
+ for point data.
+
+ Simplified sampling is done manually because we cannot sample point data.
+ """
+ sp = tgis.dataset_factory(self.timeseriesInfo[timeseries]['etype'], timeseries)
+ # sp = tgis.SpaceTimeRasterDataset(ident = timeseries)
+ if sp.is_in_db() == False:
+ raise GException(_("Space time dataset <%s> not found.") % timeseries)
+
+ sp.select()
+
+ listOfMaps = []
+ timeLabels = []
+ gran = self.GetGranularity()
+ if self.temporalType == TemporalType.ABSOLUTE:
+ gran, unit = gran.split()
+ gran = '%(one)d %(unit)s' % {'one': 1, 'unit': unit}
+
+ unit = None
+ if self.temporalType == TemporalType.RELATIVE:
+ unit = self.timeseriesInfo[timeseries]['unit']
+
+ start, end = sp.get_valid_time()
+
+ rows = sp.get_registered_maps(columns = "id,start_time", where = None, order = "start_time", dbif = None)
+ if not rows:
+ return timeLabels, listOfMaps
+
+ nextTime = start
+ while nextTime <= end:
+ timeLabels.append((str(nextTime), None, unit))
+ found = False
+ for row in rows:
+ timeseries, start_time = row
+ if start_time == nextTime:
+ listOfMaps.append(timeseries)
+ found = True
+ break
+ if not found:
+ listOfMaps.append(timeseries) # here repeat last used map
+ # listOfMaps.append(None)
+
+ if sp.is_time_absolute():
+ nextTime = tgis.increment_datetime_by_string(nextTime, gran)
+ else:
+ nextTime = nextTime + gran
+
+ if self.temporalType == TemporalType.ABSOLUTE:
+ timeLabels = self._pretifyTimeLabels(timeLabels)
+
+ return timeLabels, listOfMaps
+
+ def _pretifyTimeLabels(self, labels):
+ """!Convert absolute time labels to grass time and
+ leave only datum when time is 0.
+ """
+ grassLabels = []
+ isTime = False
+ for start, end, unit in labels:
+ start = tgis.string_to_datetime(start)
+ start = tgis.datetime_to_grass_datetime_string(start)
+ if end is not None:
+ end = tgis.string_to_datetime(end)
+ end = tgis.datetime_to_grass_datetime_string(end)
+ grassLabels.append((start, end, unit))
+ if '00:00:00' not in start or (end is not None and '00:00:00' not in end):
+ isTime = True
+ if not isTime:
+ for i, (start, end, unit) in enumerate(grassLabels):
+ start = start.replace('00:00:00', '').strip()
+ if end is not None:
+ end = end.replace('00:00:00', '').strip()
+ grassLabels[i] = (start, end, unit)
+ return grassLabels
+
+ def _gatherInformation(self, timeseries, etype, timeseriesList, infoDict):
+ """!Get info about timeseries and check topology (raises GException)"""
+ id = validateTimeseriesName(timeseries, etype)
+ sp = tgis.dataset_factory(etype, id)
+ # sp = tgis.SpaceTimeRasterDataset(ident = id)
+
+ # Insert content from db
+ sp.select()
+ # Get ordered map list
+ maps = sp.get_registered_maps_as_objects()
+ # check topology
+ check = sp.check_temporal_topology(maps)
+ if not check:
+ raise GException(_("Topology of Space time dataset %s is invalid." % id))
+
+ timeseriesList.append(id)
+ infoDict[id] = {}
+ infoDict[id]['etype'] = etype
+ # compute granularity
+ infoDict[id]['temporal_type'] = sp.get_temporal_type()
+ if infoDict[id]['temporal_type'] == 'absolute':
+ gran = tgis.compute_absolute_time_granularity(maps)
+ else:
+ start, end, infoDict[id]['unit'] = sp.get_relative_time()
+ gran = tgis.compute_relative_time_granularity(maps)
+
+ infoDict[id]['granularity'] = gran
+
+ infoDict[id]['map_time'] = sp.get_map_time()
+ infoDict[id]['maps'] = maps
+
+def test():
+ import gettext
+ from pprint import pprint
+ gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+
+
+
+ temp = TemporalManager()
+
+ timeseries1 = 'testRelInt1'
+ timeseries2 = 'testRelInt2'
+ createRelativeInterval(timeseries1, timeseries2)
+
+ # timeseries = 'testAbsPoint'
+ # createAbsolutePoint(timeseries)
+
+
+ temp.AddTimeSeries(timeseries1, 'strds')
+ temp.AddTimeSeries(timeseries2, 'strds')
+ # try:
+ # temp._gatherInformation(timeseries, info)
+ # except GException, e:
+ # print e
+ # pprint(info)
+ try:
+ temp.EvaluateInputData()
+ except GException, e:
+ print e
+ return
+
+ print '///////////////////////////'
+ gran = temp.GetGranularity()
+ print "granularity: " + str(gran)
+ pprint (temp.GetLabelsAndMaps())
+ # maps = []
+ # labels = []
+ # try:
+ # maps, labels = temp.GetLabelsAndMaps(timeseries)
+ # except GException, e:
+ # print e
+ # print maps
+ # print labels
+ # pprint(temp.timeseriesInfo['timeseries'])
+
+
+
+def createAbsoluteInterval(name1, name2):
+ grass.run_command('g.region', s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10, flags = 'p3', quiet = True)
+
+ grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite = True)
+ grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite = True)
+ grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite = True)
+ grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite = True)
+ grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite = True)
+ grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite = True)
+
+ grass.mapcalc(exp="temp_1 = rand(0, 550)", overwrite = True)
+ grass.mapcalc(exp="temp_2 = rand(0, 450)", overwrite = True)
+ grass.mapcalc(exp="temp_3 = rand(0, 320)", overwrite = True)
+ grass.mapcalc(exp="temp_4 = rand(0, 510)", overwrite = True)
+ grass.mapcalc(exp="temp_5 = rand(0, 300)", overwrite = True)
+ grass.mapcalc(exp="temp_6 = rand(0, 650)", overwrite = True)
+
+ n1 = grass.read_command("g.tempfile", pid = 1, flags = 'd').strip()
+ fd = open(n1, 'w')
+ fd.write(
+ "prec_1|2001-01-01|2001-02-01\n"
+ "prec_2|2001-04-01|2001-05-01\n"
+ "prec_3|2001-05-01|2001-09-01\n"
+ "prec_4|2001-09-01|2002-01-01\n"
+ "prec_5|2002-01-01|2002-05-01\n"
+ "prec_6|2002-05-01|2002-07-01\n"
+ )
+ fd.close()
+
+ n2 = grass.read_command("g.tempfile", pid = 2, flags = 'd').strip()
+ fd = open(n2, 'w')
+ fd.write(
+ "temp_1|2000-10-01|2001-01-01\n"
+ "temp_2|2001-04-01|2001-05-01\n"
+ "temp_3|2001-05-01|2001-09-01\n"
+ "temp_4|2001-09-01|2002-01-01\n"
+ "temp_5|2002-01-01|2002-05-01\n"
+ "temp_6|2002-05-01|2002-07-01\n"
+ )
+ fd.close()
+ grass.run_command('t.unregister', type = 'rast',
+ maps='prec_1,prec_2,prec_3,prec_4,prec_5,prec_6,temp_1,temp_2,temp_3,temp_4,temp_5,temp_6')
+ for name, fname in zip((name1, name2), (n1, n2)):
+ grass.run_command('t.create', overwrite = True, type='strds',
+ temporaltype='absolute', output=name,
+ title="A test with input files", descr="A test with input files")
+ grass.run_command('t.register', flags = 'i', input=name, file=fname, overwrite = True)
+
+def createRelativeInterval(name1, name2):
+ grass.run_command('g.region', s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10, flags = 'p3', quiet = True)
+
+ grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite = True)
+ grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite = True)
+ grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite = True)
+ grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite = True)
+ grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite = True)
+ grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite = True)
+
+ grass.mapcalc(exp="temp_1 = rand(0, 550)", overwrite = True)
+ grass.mapcalc(exp="temp_2 = rand(0, 450)", overwrite = True)
+ grass.mapcalc(exp="temp_3 = rand(0, 320)", overwrite = True)
+ grass.mapcalc(exp="temp_4 = rand(0, 510)", overwrite = True)
+ grass.mapcalc(exp="temp_5 = rand(0, 300)", overwrite = True)
+ grass.mapcalc(exp="temp_6 = rand(0, 650)", overwrite = True)
+
+ n1 = grass.read_command("g.tempfile", pid = 1, flags = 'd').strip()
+ fd = open(n1, 'w')
+ fd.write(
+ "prec_1|1|4\n"
+ "prec_2|6|7\n"
+ "prec_3|7|10\n"
+ "prec_4|10|11\n"
+ "prec_5|11|14\n"
+ "prec_6|14|17\n"
+ )
+ fd.close()
+
+ n2 = grass.read_command("g.tempfile", pid = 2, flags = 'd').strip()
+ fd = open(n2, 'w')
+ fd.write(
+ "temp_1|1|4\n"
+ "temp_2|4|7\n"
+ "temp_3|7|10\n"
+ "temp_4|10|11\n"
+ "temp_5|11|14\n"
+ "temp_6|14|17\n"
+ )
+ fd.close()
+ grass.run_command('t.unregister', type = 'rast',
+ maps='prec_1,prec_2,prec_3,prec_4,prec_5,prec_6,temp_1,temp_2,temp_3,temp_4,temp_5,temp_6')
+ for name, fname in zip((name1, name2), (n1, n2)):
+ grass.run_command('t.create', overwrite = True, type='strds',
+ temporaltype='relative', output=name,
+ title="A test with input files", descr="A test with input files")
+ grass.run_command('t.register', flags = 'i', input = name, file = fname, unit = "years", overwrite = True)
+
+def createAbsolutePoint(name):
+ grass.run_command('g.region', s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10, flags = 'p3', quiet = True)
+
+ grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite = True)
+ grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite = True)
+ grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite = True)
+ grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite = True)
+ grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite = True)
+ grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite = True)
+
+ n1 = grass.read_command("g.tempfile", pid = 1, flags = 'd').strip()
+ fd = open(n1, 'w')
+ fd.write(
+ "prec_1|2001-01-01\n"
+ "prec_2|2001-02-10\n"
+ "prec_3|2001-07-01\n"
+ "prec_4|2001-10-01\n"
+ "prec_5|2002-01-01\n"
+ "prec_6|2002-04-01\n"
+ )
+ fd.close()
+ grass.run_command('t.create', overwrite = True, type='strds',
+ temporaltype='absolute', output=name,
+ title="A test with input files", descr="A test with input files")
+
+ grass.run_command('t.register', flags = 'i', input=name, file=n1, overwrite = True)
+
+if __name__ == '__main__':
+
+ test()
+
+
+
Added: grass-addons/grass7/gui/wxpython/wx.animation/animation/toolbars.py
===================================================================
--- grass-addons/grass7/gui/wxpython/wx.animation/animation/toolbars.py (rev 0)
+++ grass-addons/grass7/gui/wxpython/wx.animation/animation/toolbars.py 2012-10-17 12:24:37 UTC (rev 53437)
@@ -0,0 +1,197 @@
+"""!
+ at package animation.toolbars
+
+ at brief Animation toolbars
+
+Classes:
+ - toolbars::MainToolbar(BaseToolbar):
+ - toolbars::AnimationToolbar(BaseToolbar):
+ - toolbars::MiscToolbar(BaseToolbar):
+
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+import wx
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from icons.icon import MetaIcon
+
+from animation import ReplayMode
+
+ganimIcons = {
+ 'speed': MetaIcon(img = 'settings', label = _("Change animation speed")),
+ 'playForward': MetaIcon(img = 'execute', label = _("Play forward")),
+ 'playBack': MetaIcon(img = 'player-back', label = _("Play back")),
+ 'stop': MetaIcon(img = 'player-stop', label = _("Stop")),
+ 'pause': MetaIcon(img = 'player-pause', label = _("Pause")),
+ 'oneDirectionReplay': MetaIcon(img = 'redraw', label = _("Repeat")),
+ 'bothDirectionReplay': MetaIcon(img = 'player-repeat-back-forward',
+ label = _("Play back and forward")),
+ 'addAnimation': MetaIcon(img = 'layer-add', label = _("Add new animation"),
+ desc = _("Add new animation")),
+ 'editAnimation': MetaIcon(img = 'layer-more', label = _("Add, edit or remove animation"),
+ desc = _("Add, edit or remove animation")),
+ }
+
+class MainToolbar(BaseToolbar):
+ """!Main toolbar (data management)
+ """
+ def __init__(self, parent):
+ """!Main toolbar constructor
+ """
+ BaseToolbar.__init__(self, parent)
+
+ self.InitToolbar(self._toolbarData())
+
+ # realize the toolbar
+ self.Realize()
+
+ def _toolbarData(self):
+ """!Returns toolbar data (name, icon, handler)"""
+ # BaseIcons are a set of often used icons. It is possible
+ # to reuse icons in ./trunk/gui/icons/grass or add new ones there.
+ icons = ganimIcons
+ return self._getToolbarData((("addAnimation", icons["addAnimation"],
+ self.parent.OnAddAnimation),
+ ("editAnimation", icons["editAnimation"],
+ self.parent.OnEditAnimation),
+ ("reload", BaseIcons["render"],
+ self.parent.Reload)
+ ))
+class AnimationToolbar(BaseToolbar):
+ """!Animation toolbar (to control animation)
+ """
+ def __init__(self, parent):
+ """!Animation toolbar constructor
+ """
+ BaseToolbar.__init__(self, parent)
+
+ self.InitToolbar(self._toolbarData())
+
+ # realize the toolbar
+ self.Realize()
+
+ self.isPlayingForward = True
+
+
+ def _toolbarData(self):
+ """!Returns toolbar data (name, icon, handler)"""
+ # BaseIcons are a set of often used icons. It is possible
+ # to reuse icons in ./trunk/gui/icons/grass or add new ones there.
+ icons = ganimIcons
+ return self._getToolbarData((("playBack", icons["playBack"],
+ self.OnPlayBack),
+ ("playForward", icons["playForward"],
+ self.OnPlayForward),
+ ("pause", icons["pause"],
+ self.OnPause,
+ wx.ITEM_CHECK),
+ ("stop", icons["stop"],
+ self.OnStop),
+ (None, ),
+ ("oneDirectionReplay", icons["oneDirectionReplay"],
+ self.OnOneDirectionReplay,
+ wx.ITEM_CHECK),
+ ("bothDirectionReplay", icons["bothDirectionReplay"],
+ self.OnBothDirectionReplay,
+ wx.ITEM_CHECK),
+ (None, ),
+ ("adjustSpeed", icons['speed'],
+ self.parent.OnAdjustSpeed)
+ ))
+ def OnPlayForward(self, event):
+ self.PlayForward()
+ self.parent.OnPlayForward(event)
+
+ def PlayForward(self):
+ self.EnableTool(self.playForward, False)
+ self.EnableTool(self.playBack, True)
+ self.EnableTool(self.pause, True)
+ self.EnableTool(self.stop, True)
+ self.ToggleTool(self.pause, False)
+ self.isPlayingForward = True
+
+ def OnPlayBack(self, event):
+ self.PlayBack()
+ self.parent.OnPlayBack(event)
+
+ def PlayBack(self):
+ self.EnableTool(self.playForward, True)
+ self.EnableTool(self.playBack, False)
+ self.EnableTool(self.pause, True)
+ self.EnableTool(self.stop, True)
+ self.ToggleTool(self.pause, False)
+ self.isPlayingForward = False
+
+ def OnPause(self, event):
+ self.Pause()
+ self.parent.OnPause(event)
+
+ def Pause(self):
+ if self.GetToolState(self.pause):
+ self.EnableTool(self.playForward, True)
+ self.EnableTool(self.playBack, True)
+ else:
+ self.EnableTool(self.playForward, not self.isPlayingForward)
+ self.EnableTool(self.playBack, self.isPlayingForward)
+
+
+
+ def OnStop(self, event):
+ self.Stop()
+ self.parent.OnStop(event)
+
+ def Stop(self):
+ self.EnableTool(self.playForward, True)
+ self.EnableTool(self.playBack, True)
+ self.EnableTool(self.pause, False)
+ self.EnableTool(self.stop, False)
+ self.ToggleTool(self.pause, False)
+
+ # if not self.GetToolState(self.oneDirectionReplay) and \
+ # not self.GetToolState(self.bothDirectionReplay):
+ # self.EnableTool(self.playBack, False) # assuming that stop rewinds to the beginning
+
+ def OnOneDirectionReplay(self, event):
+ if event.IsChecked():
+ self.ToggleTool(self.bothDirectionReplay, False)
+ self.parent.OnOneDirectionReplay(event)
+
+ def OnBothDirectionReplay(self, event):
+ if event.IsChecked():
+ self.ToggleTool(self.oneDirectionReplay, False)
+ self.parent.OnBothDirectionReplay(event)
+
+ def SetReplayMode(self, mode):
+ one, both = False, False
+ if mode == ReplayMode.REPEAT:
+ one = True
+ elif mode == ReplayMode.REVERSE:
+ both = True
+
+ self.ToggleTool(self.oneDirectionReplay, one)
+ self.ToggleTool(self.bothDirectionReplay, both)
+
+class MiscToolbar(BaseToolbar):
+ """!Toolbar with miscellaneous tools related to app
+ """
+ def __init__(self, parent):
+ """!Toolbar constructor
+ """
+ BaseToolbar.__init__(self, parent)
+
+ self.InitToolbar(self._toolbarData())
+ # realize the toolbar
+ self.Realize()
+
+ def _toolbarData(self):
+ """!Toolbar data"""
+ return self._getToolbarData((("help", BaseIcons['help'],
+ self.parent.OnHelp),
+ ("quit", BaseIcons['quit'],
+ self.parent.OnCloseWindow),
+ ))
\ No newline at end of file
Added: grass-addons/grass7/gui/wxpython/wx.animation/animation/utils.py
===================================================================
--- grass-addons/grass7/gui/wxpython/wx.animation/animation/utils.py (rev 0)
+++ grass-addons/grass7/gui/wxpython/wx.animation/animation/utils.py 2012-10-17 12:24:37 UTC (rev 53437)
@@ -0,0 +1,86 @@
+"""!
+ at package animation.utils
+
+ at brief Miscellaneous functions and enum classes
+
+Classes:
+ - utils::TemporalMode
+ - utils::TemporalType
+ - utils::Orientation
+ - utils::ReplayMode
+
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+import grass.temporal as tgis
+import grass.script as grass
+
+from core.gcmd import GException
+
+class TemporalMode:
+ TEMPORAL = 1
+ NONTEMPORAL = 2
+
+class TemporalType:
+ ABSOLUTE = 1
+ RELATIVE = 2
+
+class Orientation:
+ FORWARD = 1
+ BACKWARD = 2
+
+class ReplayMode:
+ ONESHOT = 1
+ REVERSE = 2
+ REPEAT = 3
+
+def validateTimeseriesName(timeseries, etype = 'strds'):
+ """!Checks if space time dataset exists and completes missing mapset.
+
+ Raises GException if dataset doesn't exist.
+ """
+ trastDict = tgis.tlist_grouped(etype)
+ if timeseries.find("@") >= 0:
+ nameShort, mapset = timeseries.split('@', 1)
+ if nameShort in trastDict[mapset]:
+ return timeseries
+ else:
+ raise GException(_("Space time dataset <%s> not found.") % timeseries)
+
+
+ for mapset, names in trastDict.iteritems():
+ if timeseries in names:
+ return timeseries + "@" + mapset
+
+ raise GException(_("Space time dataset <%s> not found.") % timeseries)
+
+def validateMapNames(names, etype):
+ """!Checks if maps exist and completes missing mapset.
+
+ Input is list of map names.
+ Raises GException if map doesn't exist.
+ """
+ mapDict = grass.list_grouped(etype)
+
+ newNames = []
+ for name in names:
+ if name.find("@") >= 0:
+ nameShort, mapset = name.split('@', 1)
+ if nameShort in mapDict[mapset]:
+ newNames.append(name)
+ else:
+ raise GException(_("Map <%s> not found.") % name)
+ else:
+ found = False
+ for mapset, mapNames in mapDict.iteritems():
+ if name in mapNames:
+ found = True
+ newNames.append(name + "@" + mapset)
+ if not found:
+ raise GException(_("Map <%s> not found.") % name)
+ return newNames
Added: grass-addons/grass7/gui/wxpython/wx.animation/wxGUI.Animation.html
===================================================================
--- grass-addons/grass7/gui/wxpython/wx.animation/wxGUI.Animation.html (rev 0)
+++ grass-addons/grass7/gui/wxpython/wx.animation/wxGUI.Animation.html 2012-10-17 12:24:37 UTC (rev 53437)
@@ -0,0 +1,60 @@
+<!-- meta page description: wxGUI Animation tool -->
+<!-- meta page index: wxGUI -->
+<h2>DESCRIPTION</h2>
+
+The <b>Animation Tool</b> is a <em><a href="wxGUI.html">wxGUI</a></em> component
+for animating a series of GRASS raster maps or a space time raster dataset (created by t.* modules).
+
+<p>
+Animation Tool allows to:
+
+<ul>
+ <li>display up to 4 synchronized animations</li>
+ <li>control the animation speed</li>
+ <li>interactively change active frame using a slider</li>
+ <li>visualize space time datasets with unequally spaced intervals</li>
+ <li>animate 3d view (partially implemented)</li>
+</ul>
+
+<p>
+3D view animation enables to animate raster (as an elevation map or a color map)
+or vector map (points, lines). Internally, module m.nviz.image is used.
+To display 3D view animation follow these steps:
+<ul>
+ <li>open GRASS GUI, load maps and start 3D view</li>
+ <li>set view, light and other parameters as you like</li>
+ <li>save workspace file</li>
+ <li>add new animation in Animation Tool, choose 3D view mode</li>
+ <li>choose data (series of maps or space time dataset) used for animation</li>
+ <li>set workspace file</li>
+ <li>choose parameter (parameter of m.nviz.image) to animate (e.g. color_map)</li>
+
+</ul>
+
+<center>
+<br>
+<img src="wxGUI_animation_tool.jpg" border="1" alt="Animation Tool screenshot">
+<br>
+<br>
+</center>
+
+<h2>TODO</h2>
+<ul>
+ <li>export animation</li>
+ <li>...</li>
+</ul>
+
+<h2>SEE ALSO</h2>
+
+<em>
+ <a href="wxGUI.html">wxGUI</a><br>
+ <a href="wxGUI.Components.html">wxGUI components</a>
+</em>
+
+<h2>AUTHOR</h2>
+
+Anna Kratochvilova,
+<a href="http://www.cvut.cz">Czech Technical University in Prague</a>, Czech Republic
+
+<p>
+<i>$Date: 2012-03-07 13:21:57 +0100 (Wed, 07 Mar 2012) $</i>
Added: grass-addons/grass7/gui/wxpython/wx.animation/wxGUI_animation_tool.jpg
===================================================================
(Binary files differ)
Property changes on: grass-addons/grass7/gui/wxpython/wx.animation/wxGUI_animation_tool.jpg
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
More information about the grass-commit
mailing list