[GRASS-SVN] r49274 - grass/trunk/gui/wxpython/gui_modules
svn_grass at osgeo.org
svn_grass at osgeo.org
Wed Nov 16 11:02:14 EST 2011
Author: annakrat
Date: 2011-11-16 08:02:14 -0800 (Wed, 16 Nov 2011)
New Revision: 49274
Added:
grass/trunk/gui/wxpython/gui_modules/nviz_animation.py
Modified:
grass/trunk/gui/wxpython/gui_modules/mapdisp.py
grass/trunk/gui/wxpython/gui_modules/nviz_mapdisp.py
grass/trunk/gui/wxpython/gui_modules/nviz_tools.py
grass/trunk/gui/wxpython/gui_modules/preferences.py
grass/trunk/gui/wxpython/gui_modules/workspace.py
grass/trunk/gui/wxpython/gui_modules/wxnviz.py
Log:
wxNviz: simple animation implemented
Modified: grass/trunk/gui/wxpython/gui_modules/mapdisp.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/mapdisp.py 2011-11-16 15:58:34 UTC (rev 49273)
+++ grass/trunk/gui/wxpython/gui_modules/mapdisp.py 2011-11-16 16:02:14 UTC (rev 49274)
@@ -649,7 +649,7 @@
# add Nviz notebookpage
self._layerManager.AddNvizTools()
self.MapWindow3D.ResetViewHistory()
- for page in ('view', 'light', 'fringe', 'constant', 'cplane'):
+ for page in ('view', 'light', 'fringe', 'constant', 'cplane', 'animation'):
self._layerManager.nviz.UpdatePage(page)
self.MapWindow3D.overlays = self.MapWindow2D.overlays
Added: grass/trunk/gui/wxpython/gui_modules/nviz_animation.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/nviz_animation.py (rev 0)
+++ grass/trunk/gui/wxpython/gui_modules/nviz_animation.py 2011-11-16 16:02:14 UTC (rev 49274)
@@ -0,0 +1,208 @@
+"""!
+ at package nviz_animation.py
+
+ at brief Nviz (3D view) animation
+
+Classes:
+ - Animation
+
+(C) 2008-2011 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 copy
+
+import wx
+from wx.lib.newevent import NewEvent
+
+wxAnimationFinished, EVT_ANIM_FIN = NewEvent()
+wxAnimationUpdateIndex, EVT_ANIM_UPDATE_IDX = NewEvent()
+
+
+class Animation:
+ """!Class represents animation as a sequence of states (views).
+ It enables to record, replay the sequence and finally generate
+ all image files. Recording and replaying is based on timer events.
+ There is no frame interpolation like in the Tcl/Tk based Nviz.
+ """
+ def __init__(self, mapWindow, timer):
+ """!Animation constructor
+
+ @param mapWindow glWindow where rendering takes place
+ @param timer timer for recording and replaying
+ """
+
+ self.animationList = [] # view states
+ self.timer = timer
+ self.mapWindow = mapWindow
+ self.actions = {'record': self.Record,
+ 'play': self.Play}
+ self.formats = ['ppm', 'tif'] # currently supported formats
+ self.mode = 'record' # current mode (record, play, save)
+ self.paused = False # recording/replaying paused
+ self.currentFrame = 0 # index of current frame
+ self.fps = 24 # user settings # Frames per second
+
+ self.stopSaving = False # stop during saving images
+ self.animationSaved = False # current animation saved or not
+
+ def Start(self):
+ """!Start recording/playing"""
+ self.timer.Start(self.GetInterval())
+
+ def Pause(self):
+ """!Pause recording/playing"""
+ self.timer.Stop()
+
+ def Stop(self):
+ """!Stop recording/playing"""
+ self.timer.Stop()
+ self.PostFinishedEvent()
+
+ def Update(self):
+ """!Record/play next view state (on timer event)"""
+ self.actions[self.mode]()
+
+ def Record(self):
+ """!Record new view state"""
+ self.animationList.append({'view' : copy.deepcopy(self.mapWindow.view),
+ 'iview': copy.deepcopy(self.mapWindow.iview)})
+ self.currentFrame += 1
+ self.PostUpdateIndexEvent(index = self.currentFrame)
+ self.animationSaved = False
+
+ def Play(self):
+ """!Render next frame"""
+ if not self.animationList:
+ self.Stop()
+ return
+ try:
+ self.IterAnimation()
+ except IndexError:
+ # no more frames
+ self.Stop()
+
+ def IterAnimation(self):
+ params = self.animationList[self.currentFrame]
+ self.UpdateView(params)
+ self.currentFrame += 1
+
+ self.PostUpdateIndexEvent(index = self.currentFrame)
+
+ def UpdateView(self, params):
+ """!Update view data in map window and render"""
+ toolWin = self.mapWindow.GetToolWin()
+ toolWin.UpdateState(view = params['view'], iview = params['iview'])
+
+ self.mapWindow.UpdateView()
+
+ self.mapWindow.render['quick'] = True
+ self.mapWindow.Refresh(False)
+
+ def IsRunning(self):
+ """!Test if timer is running"""
+ return self.timer.IsRunning()
+
+ def SetMode(self, mode):
+ """!Start animation mode
+
+ @param mode animation mode (record, play, save)
+ """
+ self.mode = mode
+
+ def GetMode(self):
+ """!Get animation mode (record, play, save)"""
+ return self.mode
+
+ def IsPaused(self):
+ """!Test if animation is paused"""
+ return self.paused
+
+ def SetPause(self, pause):
+ self.paused = pause
+
+ def Exists(self):
+ """!Returns if an animation has been recorded"""
+ return bool(self.animationList)
+
+ def GetFrameCount(self):
+ """!Return number of recorded frames"""
+ return len(self.animationList)
+
+ def Clear(self):
+ """!Clear all records"""
+ self.animationList = []
+ self.currentFrame = 0
+
+ def GoToFrame(self, index):
+ """!Render frame of given index"""
+ if index >= len(self.animationList):
+ return
+
+ self.currentFrame = index
+ params = self.animationList[self.currentFrame]
+ self.UpdateView(params)
+
+ def PostFinishedEvent(self):
+ """!Animation ends"""
+ toolWin = self.mapWindow.GetToolWin()
+ event = wxAnimationFinished(mode = self.mode)
+ wx.PostEvent(toolWin, event)
+
+ def PostUpdateIndexEvent(self, index):
+ """!Frame index changed, update tool window"""
+ toolWin = self.mapWindow.GetToolWin()
+ event = wxAnimationUpdateIndex(index = index, mode = self.mode)
+ wx.PostEvent(toolWin, event)
+
+ def StopSaving(self):
+ """!Abort image files generation"""
+ self.stopSaving = True
+
+ def IsSaved(self):
+ """"!Test if animation has been saved (to images)"""
+ return self.animationSaved
+
+ def SaveAnimationFile(self, path, prefix, format):
+ """!Generate image files
+
+ @param path path to direcory
+ @param prefix file prefix
+ @param format index of image file format
+ """
+ w, h = self.mapWindow.GetClientSizeTuple()
+ toolWin = self.mapWindow.GetToolWin()
+
+ self.currentFrame = 0
+ self.mode = 'save'
+ for params in self.animationList:
+ if not self.stopSaving:
+ self.UpdateView(params)
+ filename = prefix + "_" + str(self.currentFrame) + '.' + self.formats[format]
+ filepath = os.path.join(path, filename)
+ self.mapWindow.SaveToFile(FileName = filepath, FileType = self.formats[format],
+ width = w, height = h)
+ self.currentFrame += 1
+
+ wx.Yield()
+ toolWin.UpdateFrameIndex(index = self.currentFrame, goToFrame = False)
+ else:
+ self.stopSaving = False
+ break
+ self.animationSaved = True
+ self.PostFinishedEvent()
+
+ def SetFPS(self, fps):
+ """!Set Frames Per Second value
+ @param fps frames per second
+ """
+ self.fps = fps
+
+ def GetInterval(self):
+ """!Return timer interval in ms"""
+ return 1000. / self.fps
Modified: grass/trunk/gui/wxpython/gui_modules/nviz_mapdisp.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/nviz_mapdisp.py 2011-11-16 15:58:34 UTC (rev 49273)
+++ grass/trunk/gui/wxpython/gui_modules/nviz_mapdisp.py 2011-11-16 16:02:14 UTC (rev 49274)
@@ -42,6 +42,7 @@
from goutput import wxCmdOutput
from preferences import globalSettings as UserSettings
from workspace import Nviz as NvizDefault
+from nviz_animation import Animation
import wxnviz
@@ -119,6 +120,8 @@
self.textdict = {}
self.dragid = -1
self.hitradius = 5
+ # layer manager toolwindow
+ self.toolWin = None
if self.lmgr:
self.log = self.lmgr.goutput
@@ -158,6 +161,9 @@
# timer for flythrough
self.timerFly = wx.Timer(self, id = wx.NewId())
+ # timer for animations
+ self.timerAnim = wx.Timer(self, id = wx.NewId())
+ self.animation = Animation(mapWindow = self, timer = self.timerAnim)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_SIZE, self.OnSize)
@@ -165,10 +171,11 @@
self._bindMouseEvents()
self.Bind(EVT_UPDATE_PROP, self.UpdateMapObjProperties)
- self.Bind(EVT_UPDATE_VIEW, self.UpdateView)
+ self.Bind(EVT_UPDATE_VIEW, self.OnUpdateView)
self.Bind(EVT_UPDATE_LIGHT, self.UpdateLight)
self.Bind(EVT_UPDATE_CPLANE, self.UpdateCPlane)
+ self.Bind(wx.EVT_TIMER, self.OnTimerAnim, self.timerAnim)
self.Bind(wx.EVT_TIMER, self.OnTimerFly, self.timerFly)
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
@@ -207,6 +214,7 @@
self.ComputeFlyValues(mx = mx, my = my)
self._display.FlyThrough(flyInfo = self.fly['value'], mode = self.fly['mode'],
exagInfo = self.fly['exag'])
+ self.ChangeInnerView()
self.render['quick'] = True
self.Refresh(False)
@@ -262,6 +270,7 @@
def __del__(self):
"""!Stop timers if running, unload data"""
+ self.StopTimer(self.timerAnim)
self.StopTimer(self.timerFly)
self.UnloadDataLayers(force = True)
@@ -280,8 +289,17 @@
cplane = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'cplane'))
cplane['on'] = False
self.cplanes.append(cplane)
+
+ def SetToolWin(self, toolWin):
+ """!Sets reference to nviz toolwindow in layer manager"""
+ self.toolWin = toolWin
+
+ def GetToolWin(self):
+ """!Returns reference to nviz toolwindow in layer manager"""
+ return self.toolWin
def OnClose(self, event):
+ self.StopTimer(self.timerAnim)
self.StopTimer(self.timerFly)
# cleanup when window actually closes (on quit) and not just is hidden
self.UnloadDataLayers(force = True)
@@ -298,14 +316,15 @@
self.SetCurrent()
self._display.ResizeWindow(size.width,
size.height)
+
+ # reposition checkbox in statusbar
+ self.parent.StatusbarReposition()
+
+ # update statusbar
+ self.parent.StatusbarUpdate()
+
self.size = size
- # reposition checkbox in statusbar
- self.parent.StatusbarReposition()
-
- # update statusbar
- self.parent.StatusbarUpdate()
-
event.Skip()
def OnPaint(self, event):
@@ -334,6 +353,7 @@
self.lmgr.nviz.UpdatePage('light')
self.lmgr.nviz.UpdatePage('cplane')
self.lmgr.nviz.UpdatePage('decoration')
+ self.lmgr.nviz.UpdatePage('animation')
layer = self.GetSelectedLayer()
if layer:
if layer.type == 'raster':
@@ -492,6 +512,12 @@
return texture.id
return -1
+ def OnTimerAnim(self, event):
+ self.animation.Update()
+
+ def GetAnimation(self):
+ return self.animation
+
def OnKeyDown(self, event):
"""!Key was pressed.
@@ -752,6 +778,12 @@
if self.fly['mouseControl']:
self.StopTimer(self.timerFly)
self.fly['mouseControl'] = None
+ #for key in self.iview['dir'].keys():
+ #self.iview[''][key] = -1
+ # this causes sudden change, but it should be there
+ #if hasattr(self.lmgr, "nviz"):
+ #self.lmgr.nviz.UpdateSettings()
+
self.render['quick'] = False
self.Refresh(False)
@@ -788,8 +820,7 @@
focus['z'] -= dz
#update properties
- evt = wxUpdateView(zExag = False)
- wx.PostEvent(self, evt)
+ self.PostViewEvent()
self.mouse['tmp'] = event.GetPositionTuple()
self.render['quick'] = True
@@ -928,8 +959,7 @@
focus['x'], focus['y'] = e, n
self.saveHistory = True
#update properties
- evt = wxUpdateView(zExag = False)
- wx.PostEvent(self, evt)
+ self.PostViewEvent()
self.render['quick'] = False
self.Refresh(False)
@@ -982,37 +1012,62 @@
self.log.WriteLog(_("No point on surface"))
self.log.WriteCmdLog('-' * 80)
+ def PostViewEvent(self, zExag = False):
+ """!Change view settings"""
+ event = wxUpdateView(zExag = zExag)
+ wx.PostEvent(self, event)
+
def OnQueryVector(self, event):
"""!Query vector on given position"""
self.parent.QueryVector(*event.GetPosition())
+
+ def ChangeInnerView(self):
+ """!Get current viewdir and viewpoint and set view"""
+ view = self.view
+ iview = self.iview
+ (view['position']['x'], view['position']['y'],
+ iview['height']['value']) = self._display.GetViewpointPosition()
+ for key, val in zip(('x', 'y', 'z'), self._display.GetViewdir()):
+ iview['dir'][key] = val
- def UpdateView(self, event):
+ iview['dir']['use'] = True
+
+ def OnUpdateView(self, event):
"""!Change view settings"""
- data = self.view
-
- ## multiple z-exag value from slider by original value computed in ogsf so it scales
- ## correctly with maps of different height ranges
- if event and event.zExag and 'value' in data['z-exag']:
- self._display.SetZExag(self.iview['z-exag']['original'] * data['z-exag']['value'])
+ if event:
+ self.UpdateView(zexag = event.zExag)
+
+ self.saveHistory = True
+ if event:
+ event.Skip()
- self._display.SetView(data['position']['x'], data['position']['y'],
- self.iview['height']['value'],
- data['persp']['value'],
- data['twist']['value'])
+
+ def UpdateView(self, zexag = False):
+ """!Change view settings"""
+ view = self.view
+ iview = self.iview
+ if zexag and 'value' in view['z-exag']:
+ self._display.SetZExag(self.iview['z-exag']['original'] * view['z-exag']['value'])
- if self.iview['focus']['x'] != -1:
+
+ self._display.SetView(view['position']['x'], view['position']['y'],
+ iview['height']['value'],
+ view['persp']['value'],
+ view['twist']['value'])
+
+ if iview['dir']['use']:
+ self._display.SetViewdir(iview['dir']['x'], iview['dir']['y'], iview['dir']['z'])
+
+ elif iview['focus']['x'] != -1:
self._display.SetFocus(self.iview['focus']['x'], self.iview['focus']['y'],
self.iview['focus']['z'])
- if 'rotation' in self.iview:
- if self.iview['rotation']:
- self._display.SetRotationMatrix(self.iview['rotation'])
+
+ if 'rotation' in iview:
+ if iview['rotation']:
+ self._display.SetRotationMatrix(iview['rotation'])
else:
self._display.ResetRotation()
- self.saveHistory = True
- if event:
- event.Skip()
-
def UpdateLight(self, event):
"""!Change light settings"""
data = self.light
@@ -1694,8 +1749,7 @@
focus = self.iview['focus']
focus['x'], focus['y'], focus['z'] = self._display.GetFocus()
- event = wxUpdateView(zExag = False)
- wx.PostEvent(self, event)
+ self.PostViewEvent()
def UpdateMapObjProperties(self, event):
"""!Generic method to update data layer properties"""
Modified: grass/trunk/gui/wxpython/gui_modules/nviz_tools.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/nviz_tools.py 2011-11-16 15:58:34 UTC (rev 49273)
+++ grass/trunk/gui/wxpython/gui_modules/nviz_tools.py 2011-11-16 16:02:14 UTC (rev 49274)
@@ -4,6 +4,11 @@
@brief Nviz (3D view) tools window
Classes:
+ - ScrolledPanel
+ - NTCValidator
+ - NumTextCtrl
+ - FloatSlider
+ - SymbolButton
- NvizToolWindow
- PositionWindow
- ViewPositionWindow
@@ -27,9 +32,16 @@
import string
import wx
-import wx.lib.colourselect as csel
+import wx.lib.colourselect as csel
import wx.lib.scrolledpanel as SP
+import wx.lib.filebrowsebutton as filebrowse
+
try:
+ from wx.lib.buttons import ThemedGenBitmapTextButton as BitmapTextButton
+except ImportError: # not sure about TGBTButton version
+ from wx.lib.buttons import GenBitmapTextButton as BitmapTextButton
+
+try:
import wx.lib.agw.flatnotebook as FN
except ImportError:
import wx.lib.flatnotebook as FN
@@ -50,12 +62,14 @@
from preferences import globalSettings as UserSettings
from gselect import VectorDBInfo
+from nviz_animation import EVT_ANIM_FIN, EVT_ANIM_UPDATE_IDX
try:
from nviz_mapdisp import wxUpdateView, wxUpdateLight, wxUpdateProperties,\
wxUpdateCPlane
import wxnviz
except ImportError:
pass
+
from debug import Debug
@@ -154,11 +168,67 @@
Debug.msg(4, "FloatSlider.GetValue(): value = %f" % (val/self.coef))
return val/self.coef
+
+class SymbolButton(BitmapTextButton):
+ """!Button with symbol and label."""
+ def __init__(self, parent, usage, label, **kwargs):
+ """!Constructor
+
+ @param parent parent (usually wx.Panel)
+ @param usage determines usage and picture
+ @param label displayed label
+ """
+ BitmapTextButton.__init__(self, parent = parent, label = " " + label, **kwargs)
+
+ size = (15, 15)
+ buffer = wx.EmptyBitmap(*size)
+ dc = wx.MemoryDC()
+ dc.SelectObject(buffer)
+ maskColor = wx.Color(255, 255, 255)
+ dc.SetBrush(wx.Brush(maskColor))
+ dc.Clear()
+
+ if usage == 'record':
+ self.DrawRecord(dc, size)
+ elif usage == 'stop':
+ self.DrawStop(dc, size)
+ elif usage == 'play':
+ self.DrawPlay(dc, size)
+ elif usage == 'pause':
+ self.DrawPause(dc, size)
+
+ buffer.SetMaskColour(maskColor)
+ self.SetBitmapLabel(buffer)
+ dc.SelectObject(wx.NullBitmap)
+
+ def DrawRecord(self, dc, size):
+ """!Draw record symbol"""
+ dc.SetBrush(wx.Brush(wx.Color(255, 0, 0)))
+ dc.DrawCircle(size[0]/2, size[1] / 2, size[0] / 2)
+
+ def DrawStop(self, dc, size):
+ """!Draw stop symbol"""
+ dc.SetBrush(wx.Brush(wx.Color(50, 50, 50)))
+ dc.DrawRectangle(0, 0, size[0], size[1])
+
+ def DrawPlay(self, dc, size):
+ """!Draw play symbol"""
+ dc.SetBrush(wx.Brush(wx.Color(0, 255, 0)))
+ points = (wx.Point(0, 0), wx.Point(0, size[1]), wx.Point(size[0], size[1] / 2))
+ dc.DrawPolygon(points)
+
+ def DrawPause(self, dc, size):
+ """!Draw pause symbol"""
+ dc.SetBrush(wx.Brush(wx.Color(50, 50, 50)))
+ dc.DrawRectangle(0, 0, 2 * size[0] / 5, size[1])
+ dc.DrawRectangle(3 * size[0] / 5, 0, 2 * size[0] / 5, size[1])
+
+
class NvizToolWindow(FN.FlatNotebook):
"""!Nviz (3D view) tools panel
"""
def __init__(self, parent, display, id = wx.ID_ANY,
- style = globalvar.FNPageStyle|FN.FNB_NO_X_BUTTON|FN.FNB_NO_NAV_BUTTONS,
+ style = globalvar.FNPageStyle|FN.FNB_NO_X_BUTTON,
**kwargs):
Debug.msg(5, "NvizToolWindow.__init__()")
self.parent = parent # GMFrame
@@ -191,8 +261,14 @@
# analysis page
self.AddPage(page = self._createAnalysisPage(),
text = " %s " % _("Analysis"))
+ # view page
+ self.AddPage(page = self._createAnimationPage(),
+ text = " %s " % _("Animation"))
self.UpdateSettings()
+
+ self.mapWindow.SetToolWin(self)
+
self.pageChanging = False
self.vetoGSelectEvt = False #when setting map, event is invoked
self.mapWindow.render['quick'] = False
@@ -202,6 +278,9 @@
self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
self.Bind(wx.EVT_SIZE, self.OnSize)
+ self.Bind(EVT_ANIM_FIN, self.OnAnimationFinished)
+ self.Bind(EVT_ANIM_UPDATE_IDX, self.OnAnimationUpdateIndex)
+
Debug.msg(3, "NvizToolWindow.__init__()")
self.Update()
@@ -444,7 +523,154 @@
panel.SetSizer(pageSizer)
return panel
-
+
+ def _createAnimationPage(self):
+ """!Create view settings page"""
+ panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
+ panel.SetupScrolling(scroll_x = False)
+ self.page['animation'] = { 'id' : 0,
+ 'notebook' : self.GetId()}
+
+ pageSizer = wx.BoxSizer(wx.VERTICAL)
+ box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+ label = " %s " % (_("Animation")))
+ boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+ hSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ self.win['anim'] = {}
+ # animation help text
+ help = wx.StaticText(parent = panel, id = wx.ID_ANY,
+ label = _("Press 'Record' button and start changing the view. "
+ "It is recommended to use fly-through mode "
+ "(Map Display toolbar) to achieve smooth motion."))
+ self.win['anim']['help'] = help.GetId()
+ hSizer.Add(item = help, proportion = 0)
+ boxSizer.Add(item = hSizer, proportion = 1,
+ flag = wx.ALL | wx.EXPAND, border = 5)
+
+ # animation controls
+ hSizer = wx.BoxSizer(wx.HORIZONTAL)
+ record = SymbolButton(parent = panel, id = wx.ID_ANY,
+ usage = "record", label = _("Record"))
+ play = SymbolButton(parent = panel, id = wx.ID_ANY,
+ usage = "play", label = _("Play"))
+ pause = SymbolButton(parent = panel, id = wx.ID_ANY,
+ usage = "pause", label = _("Pause"))
+ stop = SymbolButton(parent = panel, id = wx.ID_ANY,
+ usage = "stop", label = _("Stop"))
+
+ self.win['anim']['record'] = record.GetId()
+ self.win['anim']['play'] = play.GetId()
+ self.win['anim']['pause'] = pause.GetId()
+ self.win['anim']['stop'] = stop.GetId()
+
+ self._createControl(panel, data = self.win['anim'], name = 'frameIndex',
+ range = (0, 1), floatSlider = False,
+ bind = (self.OnFrameIndex, None, self.OnFrameIndexText))
+ frameSlider = self.FindWindowById(self.win['anim']['frameIndex']['slider'])
+ frameText = self.FindWindowById(self.win['anim']['frameIndex']['text'])
+ infoLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Total number of frames :"))
+ info = wx.StaticText(parent = panel, id = wx.ID_ANY)
+ self.win['anim']['info'] = info.GetId()
+
+ fpsLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Frame rate (FPS):"))
+ fps = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+ initial = UserSettings.Get(group = 'nviz', key = 'animation', subkey = 'fps'),
+ min = 1,
+ max = 50)
+ self.win['anim']['fps'] = fps.GetId()
+ fps.SetToolTipString(_("Frames are recorded with given frequency (FPS). "))
+
+ record.Bind(wx.EVT_BUTTON, self.OnRecord)
+ play.Bind(wx.EVT_BUTTON, self.OnPlay)
+ stop.Bind(wx.EVT_BUTTON, self.OnStop)
+ pause.Bind(wx.EVT_BUTTON, self.OnPause)
+ fps.Bind(wx.EVT_SPINCTRL, self.OnFPS)
+
+ hSizer.Add(item = record, proportion = 0)
+ hSizer.Add(item = play, proportion = 0)
+ hSizer.Add(item = pause, proportion = 0)
+ hSizer.Add(item = stop, proportion = 0)
+ boxSizer.Add(item = hSizer, proportion = 0,
+ flag = wx.ALL | wx.EXPAND, border = 3)
+
+ sliderBox = wx.BoxSizer(wx.HORIZONTAL)
+ sliderBox.Add(item = frameSlider, proportion = 1, border = 5, flag = wx.EXPAND | wx.RIGHT)
+ sliderBox.Add(item = frameText, proportion = 0, border = 5, flag = wx.EXPAND| wx.RIGHT | wx.LEFT)
+ boxSizer.Add(item = sliderBox, proportion = 0, flag = wx.EXPAND)
+
+ # total number of frames
+ hSizer = wx.BoxSizer(wx.HORIZONTAL)
+ hSizer.Add(item = infoLabel, proportion = 0, flag = wx.RIGHT, border = 5)
+ hSizer.Add(item = info, proportion = 0, flag = wx.LEFT, border = 5)
+
+ boxSizer.Add(item = hSizer, proportion = 0,
+ flag = wx.ALL | wx.EXPAND, border = 5)
+
+ # frames per second
+ hSizer = wx.BoxSizer(wx.HORIZONTAL)
+ hSizer.Add(item = fpsLabel, proportion = 0, flag = wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, border = 5)
+ hSizer.Add(item = fps, proportion = 0, flag = wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border = 5)
+
+ boxSizer.Add(item = hSizer, proportion = 0,
+ flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+ pageSizer.Add(item = boxSizer, proportion = 0,
+ flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+ border = 3)
+
+ # save animation
+ self.win['anim']['save'] = {}
+ self.win['anim']['save']['image'] = {}
+ box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+ label = " %s " % (_("Save image sequence")))
+ boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+ vSizer = wx.BoxSizer(wx.VERTICAL)
+ gridSizer = wx.GridBagSizer(vgap = 5, hgap = 10)
+
+ pwd = os.getcwd()
+ dir = filebrowse.DirBrowseButton(parent = panel, id = wx.ID_ANY,
+ labelText = _("Choose a directory:"),
+ dialogTitle = _("Choose a directory for images"),
+ buttonText = _('Browse'),
+ startDirectory = pwd)
+ dir.SetValue(pwd)
+ prefixLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("File prefix:"))
+ prefixCtrl = wx.TextCtrl(parent = panel, id = wx.ID_ANY, size = (100, -1),
+ value = UserSettings.Get(group = 'nviz',
+ key = 'animation', subkey = 'prefix'))
+ prefixCtrl.SetToolTipString(_("Generated files names will look like this: prefix_1.ppm, prefix_2.ppm, ..."))
+ fileTypeLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("File format:"))
+ fileTypeCtrl = wx.Choice(parent = panel, id = wx.ID_ANY, choices = ["PPM", "TIF"])
+
+ save = wx.Button(parent = panel, id = wx.ID_ANY,
+ label = "Save")
+
+ self.win['anim']['save']['image']['dir'] = dir.GetId()
+ self.win['anim']['save']['image']['prefix'] = prefixCtrl.GetId()
+ self.win['anim']['save']['image']['format'] = fileTypeCtrl.GetId()
+ self.win['anim']['save']['image']['confirm'] = save.GetId()
+
+ boxSizer.Add(item = dir, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 3)
+
+ gridSizer.Add(item = prefixLabel, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+ gridSizer.Add(item = prefixCtrl, pos = (0, 1), flag = wx.EXPAND )
+ gridSizer.Add(item = fileTypeLabel, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+ gridSizer.Add(item = fileTypeCtrl, pos = (1, 1), flag = wx.EXPAND )
+
+ boxSizer.Add(item = gridSizer, proportion = 1,
+ flag = wx.ALL | wx.EXPAND, border = 5)
+ boxSizer.Add(item = save, proportion = 0, flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
+
+ save.Bind(wx.EVT_BUTTON, self.OnSaveAnimation)
+
+ pageSizer.Add(item = boxSizer, proportion = 0,
+ flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+ border = 3)
+
+ panel.SetSizer(pageSizer)
+
+ return panel
+
def _createDataPage(self):
"""!Create data (surface, vector, volume) settings page"""
@@ -1944,7 +2170,195 @@
return self.mapWindow.GetLayerByName(name, mapType = '3d-raster', dataType = 'nviz')
return None
-
+
+ def OnRecord(self, event):
+ """!Animation: start recording"""
+ anim = self.mapWindow.GetAnimation()
+ if not anim.IsPaused():
+ if anim.Exists() and not anim.IsSaved():
+ msg = _("Do you want to record new animation without saving the previous one?")
+ dlg = wx.MessageDialog(parent = self,
+ message = msg,
+ caption =_("Animation already axists"),
+ style = wx.YES_NO | wx.CENTRE)
+ if dlg.ShowModal() == wx.ID_NO:
+ dlg.Destroy()
+ return
+
+
+ anim.Clear()
+ self.UpdateFrameIndex(0)
+ self.UpdateFrameCount()
+
+ anim.SetPause(False)
+ anim.SetMode(mode = 'record')
+ anim.Start()
+
+ self.FindWindowById(self.win['anim']['play']).Disable()
+ self.FindWindowById(self.win['anim']['record']).Disable()
+ self.FindWindowById(self.win['anim']['pause']).Enable()
+ self.FindWindowById(self.win['anim']['stop']).Enable()
+ self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+ self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+
+ def OnPlay(self, event):
+ """!Animation: replay"""
+ anim = self.mapWindow.GetAnimation()
+ anim.SetPause(False)
+ anim.SetMode(mode = 'play')
+ anim.Start()
+
+ self.FindWindowById(self.win['anim']['play']).Disable()
+ self.FindWindowById(self.win['anim']['record']).Disable()
+ self.FindWindowById(self.win['anim']['pause']).Enable()
+ self.FindWindowById(self.win['anim']['stop']).Enable()
+ self.FindWindowById(self.win['anim']['frameIndex']['slider']).Enable()
+ self.FindWindowById(self.win['anim']['frameIndex']['text']).Enable()
+
+ def OnStop(self, event):
+ """!Animation: stop recording/replaying"""
+ anim = self.mapWindow.GetAnimation()
+ anim.SetPause(False)
+ if anim.GetMode() == 'save':
+ anim.StopSaving()
+ if anim.IsRunning():
+ anim.Stop()
+
+ self.UpdateFrameIndex(0)
+
+ self.FindWindowById(self.win['anim']['play']).Enable()
+ self.FindWindowById(self.win['anim']['record']).Enable()
+ self.FindWindowById(self.win['anim']['pause']).Disable()
+ self.FindWindowById(self.win['anim']['stop']).Disable()
+ self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+ self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+
+ def OnPause(self, event):
+ """!Pause animation"""
+ anim = self.mapWindow.GetAnimation()
+
+ anim.SetPause(True)
+ mode = anim.GetMode()
+ if anim.IsRunning():
+ anim.Pause()
+
+ if mode == "record":
+ self.FindWindowById(self.win['anim']['play']).Disable()
+ self.FindWindowById(self.win['anim']['record']).Enable()
+ self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+ self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+ elif mode == 'play':
+ self.FindWindowById(self.win['anim']['record']).Disable()
+ self.FindWindowById(self.win['anim']['play']).Enable()
+ self.FindWindowById(self.win['anim']['frameIndex']['slider']).Enable()
+ self.FindWindowById(self.win['anim']['frameIndex']['text']).Enable()
+
+ self.FindWindowById(self.win['anim']['pause']).Disable()
+ self.FindWindowById(self.win['anim']['stop']).Enable()
+
+
+ def OnFrameIndex(self, event):
+ """!Frame index changed (by slider)"""
+ index = event.GetInt()
+ self.UpdateFrameIndex(index = index, sliderWidget = False)
+
+ def OnFrameIndexText(self, event):
+ """!Frame index changed by (textCtrl)"""
+ index = event.GetValue()
+ self.UpdateFrameIndex(index = index, textWidget = False)
+
+ def OnFPS(self, event):
+ """!Frames per second changed"""
+ anim = self.mapWindow.GetAnimation()
+ anim.SetFPS(event.GetInt())
+
+ def UpdateFrameIndex(self, index, sliderWidget = True, textWidget = True, goToFrame = True):
+ """!Update frame index"""
+ anim = self.mapWindow.GetAnimation()
+
+ # check index
+ frameCount = anim.GetFrameCount()
+ if index >= frameCount:
+ index = frameCount - 1
+ if index < 0:
+ index = 0
+
+ if sliderWidget:
+ slider = self.FindWindowById(self.win['anim']['frameIndex']['slider'])
+ slider.SetValue(index)
+ if textWidget:
+ text = self.FindWindowById(self.win['anim']['frameIndex']['text'])
+ text.SetValue(int(index))
+
+ # if called from tool window, update frame
+ if goToFrame:
+ anim.GoToFrame(int(index))
+
+ def UpdateFrameCount(self):
+ """!Update frame count label"""
+ anim = self.mapWindow.GetAnimation()
+ count = anim.GetFrameCount()
+ self.FindWindowById(self.win['anim']['info']).SetLabel(str(count))
+
+ def OnAnimationFinished(self, event):
+ """!Animation finished"""
+ anim = self.mapWindow.GetAnimation()
+ self.UpdateFrameIndex(index = 0)
+
+ slider = self.FindWindowById(self.win['anim']['frameIndex']['slider'])
+ text = self.FindWindowById(self.win['anim']['frameIndex']['text'])
+
+ if event.mode == 'record':
+ count = anim.GetFrameCount()
+ slider.SetMax(count)
+ self.UpdateFrameCount()
+
+ self.FindWindowById(self.win['anim']['pause']).Disable()
+ self.FindWindowById(self.win['anim']['stop']).Disable()
+ self.FindWindowById(self.win['anim']['record']).Enable()
+ self.FindWindowById(self.win['anim']['play']).Enable()
+ self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+ self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+ self.FindWindowById(self.win['anim']['save']['image']['confirm']).Enable()
+
+ self.mapWindow.render['quick'] = False
+ self.mapWindow.Refresh(False)
+
+ def OnAnimationUpdateIndex(self, event):
+ """!Animation: frame index changed"""
+ if event.mode == 'record':
+ self.UpdateFrameCount()
+ elif event.mode == 'play':
+ self.UpdateFrameIndex(index = event.index, goToFrame = False)
+
+ def OnSaveAnimation(self, event):
+ """!Save animation as a sequence of images"""
+ anim = self.mapWindow.GetAnimation()
+
+ prefix = self.FindWindowById(self.win['anim']['save']['image']['prefix']).GetValue()
+ format = self.FindWindowById(self.win['anim']['save']['image']['format']).GetSelection()
+ dir = self.FindWindowById(self.win['anim']['save']['image']['dir']).GetValue()
+
+ if not prefix:
+ gcmd.GMessage(parent = self,
+ message = _("No file prefix given."))
+ return
+ elif not os.path.exists(dir):
+ gcmd.GMessage(parent = self,
+ message = _("Directory %s does not exist.") % dir)
+ return
+
+ self.FindWindowById(self.win['anim']['pause']).Disable()
+ self.FindWindowById(self.win['anim']['stop']).Enable()
+ self.FindWindowById(self.win['anim']['record']).Disable()
+ self.FindWindowById(self.win['anim']['play']).Disable()
+ self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+ self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+
+ self.FindWindowById(self.win['anim']['save']['image']['confirm']).Disable()
+
+ anim.SaveAnimationFile(path = dir, prefix = prefix, format = format)
+
def OnNewConstant(self, event):
"""!Create new surface with constant value"""
#TODO settings
@@ -2336,6 +2750,8 @@
sizer.Add(item = sw, pos = (2, 0), flag = wx.ALIGN_CENTER)
sizer.Add(item = w, pos = (1, 0), flag = wx.ALIGN_CENTER)
+
+
def __GetWindowName(self, data, id):
for name in data.iterkeys():
if type(data[name]) is type({}):
@@ -2513,7 +2929,7 @@
for win in self.win['view'][winName].itervalues():
self.FindWindowById(win).SetValue(value)
-
+ self.mapWindow.iview['dir']['use'] = False
self.mapWindow.render['quick'] = True
if self.mapDisplay.IsAutoRendered():
self.mapWindow.Refresh(False)
@@ -4191,10 +4607,33 @@
self.FindWindowById(self.win['cplane']['position']['z']['text']).SetValue(zRange[0])
self.OnCPlaneSelection(None)
+ elif pageId == 'animation':
+ self.UpdateAnimationPage()
self.Update()
self.pageChanging = False
+ def UpdateAnimationPage(self):
+ """!Update animation page"""
+ # wrap help text according to tool window
+ help = self.FindWindowById(self.win['anim']['help'])
+ width = help.GetGrandParent().GetSizeTuple()[0]
+ help.Wrap(width - 15)
+ anim = self.mapWindow.GetAnimation()
+ if anim.Exists():
+ self.FindWindowById(self.win['anim']['play']).Enable()
+ else:
+ self.UpdateFrameIndex(index = 0)
+
+ self.UpdateFrameCount()
+
+ self.FindWindowById(self.win['anim']['play']).Disable()
+ self.FindWindowById(self.win['anim']['record']).Enable()
+ self.FindWindowById(self.win['anim']['pause']).Disable()
+ self.FindWindowById(self.win['anim']['stop']).Disable()
+ self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+ self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+
def UpdateCPlanePage(self, index):
"""!Update widgets according to selected clip plane"""
if index == -1:
@@ -4209,14 +4648,7 @@
def UpdateSurfacePage(self, layer, data, updateName = True):
"""!Update surface page"""
- ret = gcmd.RunCommand('r.info',
- read = True,
- flags = 'm',
- map = layer.name)
- if ret:
- desc = ret.split('=')[1].rstrip('\n')
- else:
- desc = None
+ desc = grass.raster_info(layer.name)['title']
if updateName:
self.FindWindowById(self.win['surface']['map']).SetValue(layer.name)
self.FindWindowById(self.win['surface']['desc']).SetLabel(desc)
@@ -4730,6 +5162,7 @@
return x, y
def OnMouse(self, event):
+ self.mapWindow.iview['dir']['use'] = False # use focus instead of viewdir
PositionWindow.OnMouse(self, event)
if event.LeftIsDown():
self.mapWindow.render['quick'] = self.quick
Modified: grass/trunk/gui/wxpython/gui_modules/preferences.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/preferences.py 2011-11-16 15:58:34 UTC (rev 49273)
+++ grass/trunk/gui/wxpython/gui_modules/preferences.py 2011-11-16 16:02:14 UTC (rev 49274)
@@ -549,6 +549,10 @@
'turn' : 1,
}
},
+ 'animation' : {
+ 'fps' : 24,
+ 'prefix' : _("animation")
+ },
'surface' : {
'shine': {
'map' : False,
@@ -781,6 +785,12 @@
self.internalSettings['nviz']['view']['focus']['x'] = -1
self.internalSettings['nviz']['view']['focus']['y'] = -1
self.internalSettings['nviz']['view']['focus']['z'] = -1
+ self.internalSettings['nviz']['view']['dir'] = {}
+ self.internalSettings['nviz']['view']['dir']['x'] = -1
+ self.internalSettings['nviz']['view']['dir']['y'] = -1
+ self.internalSettings['nviz']['view']['dir']['z'] = -1
+ self.internalSettings['nviz']['view']['dir']['use'] = False
+
for decor in ('arrow', 'scalebar'):
self.internalSettings['nviz'][decor] = {}
self.internalSettings['nviz'][decor]['position'] = {}
Modified: grass/trunk/gui/wxpython/gui_modules/workspace.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/workspace.py 2011-11-16 15:58:34 UTC (rev 49273)
+++ grass/trunk/gui/wxpython/gui_modules/workspace.py 2011-11-16 16:02:14 UTC (rev 49274)
@@ -514,6 +514,19 @@
iview['focus']['x'] = self.__processLayerNvizNode(node_focus, 'x', int)
iview['focus']['y'] = self.__processLayerNvizNode(node_focus, 'y', int)
iview['focus']['z'] = self.__processLayerNvizNode(node_focus, 'z', int)
+ node_dir = node_view.find('dir')
+ if node_dir:
+ iview['dir'] = {}
+ iview['dir']['x'] = self.__processLayerNvizNode(node_dir, 'x', int)
+ iview['dir']['y'] = self.__processLayerNvizNode(node_dir, 'y', int)
+ iview['dir']['z'] = self.__processLayerNvizNode(node_dir, 'z', int)
+ iview['dir']['use'] = True
+ else:
+ iview['dir'] = {}
+ iview['dir']['x'] = -1
+ iview['dir']['y'] = -1
+ iview['dir']['z'] = -1
+ iview['dir']['use'] = False
view['background'] = {}
color = self.__processLayerNvizNode(node_view, 'background_color', str)
Modified: grass/trunk/gui/wxpython/gui_modules/wxnviz.py
===================================================================
--- grass/trunk/gui/wxpython/gui_modules/wxnviz.py 2011-11-16 15:58:34 UTC (rev 49273)
+++ grass/trunk/gui/wxpython/gui_modules/wxnviz.py 2011-11-16 16:02:14 UTC (rev 49274)
@@ -170,6 +170,15 @@
Debug.msg(3, "Nviz::SetView(): x=%f, y=%f, height=%f, persp=%f, twist=%f",
x, y, height, persp, twist)
+ def GetViewpointPosition(self):
+ x = c_double()
+ y = c_double()
+ h = c_double()
+ Nviz_get_viewpoint_height(byref(h))
+ Nviz_get_viewpoint_position(byref(x), byref(y))
+
+ return (x.value, y.value, h.value)
+
def LookHere(self, x, y):
"""!Look here feature
@param x,y screen coordinates
@@ -200,6 +209,22 @@
Debug.msg(3, "Nviz::SetFocus()")
Nviz_set_focus(self.data, x, y, z)
+ def GetViewdir(self):
+ """!Get viewdir"""
+ Debug.msg(3, "Nviz::GetViewdir()")
+ dir = (c_float * 3)()
+ GS_get_viewdir(byref(dir))
+
+ return dir[0], dir[1], dir[2]
+
+ def SetViewdir(self, x, y, z):
+ """!Set viewdir"""
+ Debug.msg(3, "Nviz::SetViewdir(): x=%f, y=%f, z=%f" % (x, y, z))
+ dir = (c_float * 3)()
+ for i, coord in enumerate((x, y, z)):
+ dir[i] = coord
+ GS_set_viewdir(byref(dir))
+
def SetZExag(self, z_exag):
"""!Set z-exag value
More information about the grass-commit
mailing list