"""! @package animation.dialogs @brief Dialogs for animation management, changing speed of animation Classes: - dialogs::SpeedDialog - dialogs::InputDialog - dialogs::EditDialog - dialogs::ExportDialog - dialogs::AnimSimpleLayerManager - dialogs::AddTemporalLayerDialog (C) 2013 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. @author Anna Petrasova """ import os import wx import copy import datetime import wx.lib.filebrowsebutton as filebrowse import wx.lib.scrolledpanel as SP import wx.lib.colourselect as csel from core.gcmd import GMessage, GError, GException from core import globalvar from gui_core.dialogs import MapLayersDialog, GetImageHandlers from gui_core.preferences import PreferencesBaseDialog from gui_core.forms import GUI from core.settings import UserSettings from core.utils import _ from gui_core.gselect import Select from gui_core.widgets import FloatValidator from animation.utils import TemporalMode, getRegisteredMaps from animation.data import AnimationData, AnimLayer from animation.toolbars import AnimSimpleLmgrToolbar, SIMPLE_LMGR_STDS from gui_core.simplelmgr import SimpleLayerManager, \ SIMPLE_LMGR_RASTER, SIMPLE_LMGR_VECTOR, SIMPLE_LMGR_TB_TOP from grass.pydispatch.signal import Signal import grass.script.core as gcore class SpeedDialog(wx.Dialog): def __init__(self, parent, title=_("Adjust speed of animation"), temporalMode=None, minimumDuration=20, timeGranularity=None, initialSpeed=200): wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=title, style=wx.DEFAULT_DIALOG_STYLE) # signal emitted when speed has changed; has attribute 'ms' self.speedChanged = Signal('SpeedDialog.speedChanged') 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[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 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 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 self.speedChanged.emit(ms=ms) def _timedelta(self, unit, number): if unit in "years": delta = datetime.timedelta(days=365.25 * number) elif unit in "months": delta = datetime.timedelta(days=30.4375 * number) # 365.25/12 elif unit in "days": delta = datetime.timedelta(days=1 * number) elif unit in "hours": delta = datetime.timedelta(hours=1 * number) elif unit in "minutes": delta = datetime.timedelta(minutes=1 * number) elif unit in "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._tmpLegendCmd = None self._layout() self.OnViewMode(event=None) def _layout(self): self.notebook = wx.Notebook(parent=self, style=wx.BK_DEFAULT) sizer = wx.BoxSizer(wx.VERTICAL) self.notebook.AddPage(self._createGeneralPage(self.notebook), _("General")) self.notebook.AddPage(self._createAdvancedPage(self.notebook), _("Advanced")) sizer.Add(self.notebook, proportion=1, flag=wx.ALL | wx.EXPAND, 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() sizer.Add(item=btnStdSizer, proportion=0, flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5) self.SetSizer(sizer) sizer.Fit(self) def _createGeneralPage(self, parent): panel = wx.Panel(parent=parent) mainSizer = wx.BoxSizer(wx.VERTICAL) self.windowChoice = wx.Choice(panel, id=wx.ID_ANY, choices=[_("top left"), _("top right"), _("bottom left"), _("bottom right")]) self.windowChoice.SetSelection(self.animationData.windowIndex) self.nameCtrl = wx.TextCtrl(panel, id=wx.ID_ANY, value=self.animationData.name) self.nDChoice = wx.Choice(panel, 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) self.nDChoice.SetToolTipString(_("Select 2D or 3D view")) self.nDChoice.Bind(wx.EVT_CHOICE, self.OnViewMode) gridSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5) gridSizer.Add(item=wx.StaticText(panel, 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(panel, 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(panel, 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=0, flag=wx.ALL | wx.EXPAND, border=5) label = _("For 3D animation, please select only one space-time dataset\n" "or one series of map layers.") self.warning3DLayers = wx.StaticText(panel, label=label) self.warning3DLayers.SetForegroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_GRAYTEXT)) mainSizer.Add(item=self.warning3DLayers, proportion=0, flag=wx.EXPAND | wx.LEFT, border=5) self.dataPanel = self._createDataPanel(panel) self.threeDPanel = self._create3DPanel(panel) mainSizer.Add(item=self.dataPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=3) mainSizer.Add(item=self.threeDPanel, proportion=0, flag=wx.EXPAND | wx.ALL, border=3) panel.SetSizer(mainSizer) mainSizer.Fit(panel) return panel def _createDataPanel(self, parent): panel = wx.Panel(parent) slmgrSizer = wx.BoxSizer(wx.VERTICAL) self._layerList = copy.deepcopy(self.animationData.layerList) self.simpleLmgr = AnimSimpleLayerManager(parent=panel, layerList=self._layerList, modal=True) self.simpleLmgr.SetMinSize((globalvar.DIALOG_GSELECT_SIZE[0], 80)) slmgrSizer.Add(self.simpleLmgr, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) self.legend = wx.CheckBox(panel, label=_("Show raster legend")) self.legend.SetValue(bool(self.animationData.legendCmd)) self.legendBtn = wx.Button(panel, label=_("Set options")) self.legend.Bind(wx.EVT_CHECKBOX, self.OnLegend) self.legendBtn.Bind(wx.EVT_BUTTON, self.OnLegendProperties) hbox = wx.BoxSizer(wx.HORIZONTAL) hbox.Add(item=self.legend, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL) hbox.Add(item=self.legendBtn, proportion=0, flag=wx.LEFT, border=5) slmgrSizer.Add(item=hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=3) panel.SetSizerAndFit(slmgrSizer) panel.SetAutoLayout(True) return panel def _create3DPanel(self, parent): panel = wx.Panel(parent, 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 _createAdvancedPage(self, parent): panel = wx.Panel(parent=parent) mainSizer = wx.BoxSizer(wx.VERTICAL) box = wx.StaticBox(parent=panel, label=" %s " % _("Animate region change (2D view only)")) sizer = wx.StaticBoxSizer(box, wx.VERTICAL) gridSizer = wx.GridBagSizer(hgap=3, vgap=3) gridSizer.Add(wx.StaticText(panel, label=_("Start region:")), pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL) self.stRegion = Select(parent=panel, type='region', size=(200, -1)) if self.animationData.startRegion: self.stRegion.SetValue(self.animationData.startRegion) gridSizer.Add(self.stRegion, pos=(0, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) self.endRegRadio = wx.RadioButton(panel, label=_("End region:"), style=wx.RB_GROUP) gridSizer.Add(self.endRegRadio, pos=(1, 0), flag=wx.EXPAND) self.endRegion = Select(parent=panel, type='region', size=(200, -1)) gridSizer.Add(self.endRegion, pos=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) self.zoomRadio = wx.RadioButton(panel, label=_("Zoom value:")) self.zoomRadio.SetToolTipString(_("N-S/E-W distances in map units used to " "gradually reduce region.")) gridSizer.Add(self.zoomRadio, pos=(2, 0), flag=wx.EXPAND) zoomSizer = wx.BoxSizer(wx.HORIZONTAL) self.zoomNS = wx.TextCtrl(panel, validator=FloatValidator()) self.zoomEW = wx.TextCtrl(panel, validator=FloatValidator()) zoomSizer.Add(wx.StaticText(panel, label=_("N-S:")), proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=3) zoomSizer.Add(self.zoomNS, proportion=1, flag=wx.LEFT, border=3) zoomSizer.Add(wx.StaticText(panel, label=_("E-W:")), proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=3) zoomSizer.Add(self.zoomEW, proportion=1, flag=wx.LEFT, border=3) gridSizer.Add(zoomSizer, pos=(2, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) if self.animationData.endRegion: self.endRegRadio.SetValue(True) self.zoomRadio.SetValue(False) self.endRegion.SetValue(self.animationData.endRegion) if self.animationData.zoomRegionValue: self.endRegRadio.SetValue(False) self.zoomRadio.SetValue(True) zoom = self.animationData.zoomRegionValue self.zoomNS.SetValue(str(zoom[0])) self.zoomEW.SetValue(str(zoom[1])) self.endRegRadio.Bind(wx.EVT_RADIOBUTTON, lambda evt: self._enableRegionWidgets()) self.zoomRadio.Bind(wx.EVT_RADIOBUTTON, lambda evt: self._enableRegionWidgets()) self._enableRegionWidgets() gridSizer.AddGrowableCol(1) sizer.Add(gridSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=3) mainSizer.Add(sizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=3) panel.SetSizer(mainSizer) mainSizer.Fit(panel) return panel def _enableRegionWidgets(self): """!Enables/disables region widgets according to which radiobutton is active.""" endReg = self.endRegRadio.GetValue() self.endRegion.Enable(endReg) self.zoomNS.Enable(not endReg) self.zoomEW.Enable(not endReg) def OnViewMode(self, event): mode = self.nDChoice.GetSelection() self.Freeze() self.simpleLmgr.Activate3D(mode == 1) self.warning3DLayers.Show(mode == 1) # disable region widgets for 3d regSizer = self.stRegion.GetContainingSizer() for child in regSizer.GetChildren(): if child.IsSizer(): for child_ in child.GetSizer().GetChildren(): child_.GetWindow().Enable(mode != 1) elif child.IsWindow(): child.GetWindow().Enable(mode != 1) self._enableRegionWidgets() # update layout sizer = self.threeDPanel.GetContainingSizer() sizer.Show(self.threeDPanel, mode == 1, True) sizer.Layout() self.Thaw() def OnLegend(self, event): if not self.legend.IsChecked(): return if self._tmpLegendCmd or self.animationData.legendCmd: return cmd = ['d.legend', 'at=5,50,2,5'] GUI(parent=self, modal=True).ParseCommand(cmd=cmd, completed=(self.GetOptData, '', '')) def OnLegendProperties(self, event): """!Set options for legend""" if self._tmpLegendCmd: cmd = self._tmpLegendCmd elif self.animationData.legendCmd: cmd = self.animationData.legendCmd else: cmd = ['d.legend', 'at=5,50,2,5'] GUI(parent=self, modal=True).ParseCommand(cmd=cmd, completed=(self.GetOptData, '', '')) def GetOptData(self, dcmd, layer, params, propwin): """!Process decoration layer data""" if dcmd: self._tmpLegendCmd = dcmd if not self.legend.IsChecked(): self.legend.SetValue(True) else: if not self._tmpLegendCmd and not self.animationData.legendCmd: self.legend.SetValue(False) def _update(self): if self.nDChoice.GetSelection() == 1 and len(self._layerList) > 1: raise GException(_("Only one series or space-time " "dataset is accepted for 3D mode.")) hasSeries = False for layer in self._layerList: if layer.active and hasattr(layer, 'maps'): hasSeries = True break if not hasSeries: raise GException(_("No map series or space-time dataset added.")) self.animationData.layerList = self._layerList self.animationData.name = self.nameCtrl.GetValue() self.animationData.windowIndex = self.windowChoice.GetSelection() sel = self.nDChoice.GetSelection() self.animationData.viewMode = self.nDChoice.GetClientData(sel) self.animationData.legendCmd = None if self._tmpLegendCmd: if self.legend.IsChecked(): self.animationData.legendCmd = self._tmpLegendCmd if self.threeDPanel.IsShown(): self.animationData.workspaceFile = self.fileSelector.GetValue() if self.threeDPanel.IsShown(): self.animationData.nvizParameter = self.paramChoice.GetStringSelection() # region (2d only) if self.animationData.viewMode == '3d': self.animationData.startRegion = None self.animationData.endRegion = None self.animationData.zoomRegionValue = None return isEnd = self.endRegRadio.GetValue() and self.endRegion.GetValue() isZoom = self.zoomRadio.GetValue() and self.zoomNS.GetValue() and self.zoomEW.GetValue() isStart = self.stRegion.GetValue() condition = bool(isStart) + bool(isZoom) + bool(isEnd) if condition == 1: raise GException(_("Region information is not complete")) elif condition == 2: self.animationData.startRegion = isStart if isEnd: self.animationData.endRegion = self.endRegion.GetValue() self.animationData.zoomRegionValue = None else: self.animationData.zoomRegionValue = (float(self.zoomNS.GetValue()), float(self.zoomEW.GetValue())) self.animationData.endRegion = None else: self.animationData.startRegion = None self.animationData.endRegion = None self.animationData.zoomRegionValue = None 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) if self.animationData: 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) dlg.CenterOnParent() 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) dlg.CenterOnParent() 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 ExportDialog(wx.Dialog): def __init__(self, parent, temporal, timeTick): wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=_("Export animation"), style=wx.DEFAULT_DIALOG_STYLE) self.decorations = [] self.temporal = temporal self.timeTick = timeTick self._layout() # export animation self.doExport = Signal('ExportDialog::doExport') wx.CallAfter(self._hideAll) def _layout(self): notebook = wx.Notebook(self, id=wx.ID_ANY) mainSizer = wx.BoxSizer(wx.VERTICAL) notebook.AddPage(page=self._createExportFormatPanel(notebook), text=_("Format")) notebook.AddPage(page=self._createDecorationsPanel(notebook), text=_("Decorations")) mainSizer.Add(item=notebook, proportion=0, flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5) self.btnExport = wx.Button(self, wx.ID_OK) self.btnExport.SetLabel(_("Export")) self.btnCancel = wx.Button(self, wx.ID_CANCEL) self.btnExport.SetDefault() self.btnExport.Bind(wx.EVT_BUTTON, self.OnExport) # button sizer btnStdSizer = wx.StdDialogButtonSizer() btnStdSizer.AddButton(self.btnExport) 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) # set the longest option to fit self.hidevbox.Show(self.fontBox, True) self.hidevbox.Show(self.imageBox, False) self.hidevbox.Show(self.textBox, True) self.hidevbox.Show(self.posBox, True) self.hidevbox.Show(self.informBox, False) mainSizer.Fit(self) def _createDecorationsPanel(self, notebook): panel = wx.Panel(notebook, id=wx.ID_ANY) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self._createDecorationsList(panel), proportion=0, flag=wx.ALL | wx.EXPAND, border=10) sizer.Add(self._createDecorationsProperties(panel), proportion=0, flag=wx.ALL | wx.EXPAND, border=10) panel.SetSizer(sizer) sizer.Fit(panel) return panel def _createDecorationsList(self, panel): gridBagSizer = wx.GridBagSizer(hgap=5, vgap=5) gridBagSizer.AddGrowableCol(0) self.listbox = wx.ListBox(panel, id=wx.ID_ANY, choices=[], style=wx.LB_SINGLE | wx.LB_NEEDED_SB) self.listbox.Bind(wx.EVT_LISTBOX, self.OnSelectionChanged) gridBagSizer.Add(self.listbox, pos=(0, 0), span=(4, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0) buttonNames = ['time', 'image', 'text'] buttonLabels = [_("Add time stamp"), _("Add image"), _("Add text")] i = 0 for buttonName, buttonLabel in zip(buttonNames, buttonLabels): if buttonName == 'time' and self.temporal == TemporalMode.NONTEMPORAL: continue btn = wx.Button(panel, id=wx.ID_ANY, name=buttonName, label=buttonLabel) btn.Bind(wx.EVT_BUTTON, lambda evt, temp=buttonName: self.OnAddDecoration(evt, temp)) gridBagSizer.Add(btn, pos=(i, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0) i += 1 removeButton = wx.Button(panel, id=wx.ID_ANY, label=_("Remove")) removeButton.Bind(wx.EVT_BUTTON, self.OnRemove) gridBagSizer.Add(removeButton, pos=(i, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0) return gridBagSizer def _createDecorationsProperties(self, panel): self.hidevbox = wx.BoxSizer(wx.VERTICAL) # inform label self.informBox = wx.BoxSizer(wx.HORIZONTAL) if self.temporal == TemporalMode.TEMPORAL: label = _("Add time stamp, image or text decoration by one of the buttons above.") else: label = _("Add image or text decoration by one of the buttons above.") label = wx.StaticText(panel, id=wx.ID_ANY, label=label) label.Wrap(400) self.informBox.Add(label, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border=5) self.hidevbox.Add(self.informBox, proportion=0, flag=wx.EXPAND | wx.BOTTOM, border=5) # font self.fontBox = wx.BoxSizer(wx.HORIZONTAL) self.fontBox.Add(wx.StaticText(panel, id=wx.ID_ANY, label=_("Font settings:")), proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border=5) self.sampleLabel = wx.StaticText(panel, id=wx.ID_ANY, label=_("Sample text")) self.fontBox.Add(self.sampleLabel, proportion=1, flag=wx.ALIGN_CENTER | wx.RIGHT | wx.LEFT, border=5) fontButton = wx.Button(panel, id=wx.ID_ANY, label=_("Set font")) fontButton.Bind(wx.EVT_BUTTON, self.OnFont) self.fontBox.Add(fontButton, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL) self.hidevbox.Add(self.fontBox, proportion=0, flag=wx.EXPAND | wx.BOTTOM, border=5) # image self.imageBox = wx.BoxSizer(wx.HORIZONTAL) filetype, ltype = GetImageHandlers(wx.EmptyImage(10, 10)) self.browse = filebrowse.FileBrowseButton(parent=panel, id=wx.ID_ANY, fileMask=filetype, labelText=_("Image file:"), dialogTitle=_('Choose image file'), buttonText=_('Browse'), startDirectory=os.getcwd(), fileMode=wx.OPEN, changeCallback=self.OnSetImage) self.imageBox.Add(self.browse, proportion=1, flag=wx.EXPAND) self.hidevbox.Add(self.imageBox, proportion=0, flag=wx.EXPAND | wx.BOTTOM, border=5) # text self.textBox = wx.BoxSizer(wx.HORIZONTAL) self.textBox.Add(wx.StaticText(panel, id=wx.ID_ANY, label=_("Text:")), proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border=5) self.textCtrl = wx.TextCtrl(panel, id=wx.ID_ANY) self.textCtrl.Bind(wx.EVT_TEXT, self.OnText) self.textBox.Add(self.textCtrl, proportion=1, flag=wx.EXPAND) self.hidevbox.Add(self.textBox, proportion=0, flag=wx.EXPAND) self.posBox = self._positionWidget(panel) self.hidevbox.Add(self.posBox, proportion=0, flag=wx.EXPAND | wx.TOP, border=5) return self.hidevbox def _positionWidget(self, panel): grid = wx.GridBagSizer(vgap=5, hgap=5) label = wx.StaticText(panel, id=wx.ID_ANY, label=_("Placement as percentage of" " screen coordinates (X: 0, Y: 0 is top left):")) label.Wrap(400) self.spinX = wx.SpinCtrl(panel, id=wx.ID_ANY, min=0, max=100, initial=10) self.spinY = wx.SpinCtrl(panel, id=wx.ID_ANY, min=0, max=100, initial=10) self.spinX.Bind(wx.EVT_SPINCTRL, lambda evt, temp='X': self.OnPosition(evt, temp)) self.spinY.Bind(wx.EVT_SPINCTRL, lambda evt, temp='Y': self.OnPosition(evt, temp)) grid.Add(label, pos=(0, 0), span = (1, 4), flag = wx.EXPAND) grid.Add(wx.StaticText(panel, id=wx.ID_ANY, label=_("X:")), pos=(1, 0), flag = wx.ALIGN_CENTER_VERTICAL) grid.Add(wx.StaticText(panel, id=wx.ID_ANY, label=_("Y:")), pos=(1, 2), flag = wx.ALIGN_CENTER_VERTICAL) grid.Add(self.spinX, pos=(1, 1)) grid.Add(self.spinY, pos=(1, 3)) return grid def _createExportFormatPanel(self, notebook): panel = wx.Panel(notebook, id=wx.ID_ANY) borderSizer = wx.BoxSizer(wx.VERTICAL) hSizer = wx.BoxSizer(wx.HORIZONTAL) choices = [_("image sequence"), _("animated GIF"), _("SWF"), _("AVI")] self.formatChoice = wx.Choice(parent=panel, id=wx.ID_ANY, choices=choices) self.formatChoice.Bind(wx.EVT_CHOICE, lambda event: self.ChangeFormat(event.GetSelection())) hSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("Export to:")), proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=2) hSizer.Add(item=self.formatChoice, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND | wx.ALL, border=2) borderSizer.Add(item=hSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=3) helpSizer = wx.BoxSizer(wx.HORIZONTAL) helpSizer.AddStretchSpacer(1) self.formatPanelSizer = wx.BoxSizer(wx.VERTICAL) helpSizer.Add(self.formatPanelSizer, proportion=5, flag=wx.EXPAND) borderSizer.Add(helpSizer, proportion=1, flag=wx.EXPAND) self.formatPanels = [] # panel for image sequence imSeqPanel = wx.Panel(parent=panel, id=wx.ID_ANY) prefixLabel = wx.StaticText(imSeqPanel, id=wx.ID_ANY, label=_("File prefix:")) self.prefixCtrl = wx.TextCtrl(imSeqPanel, id=wx.ID_ANY, value=_("animation_")) formatLabel = wx.StaticText(imSeqPanel, id=wx.ID_ANY, label=_("File format:")) imageTypes = ['PNG', 'JPEG', 'GIF', 'TIFF', 'PPM', 'BMP'] self.imSeqFormatChoice = wx.Choice(imSeqPanel, choices=imageTypes) self.imSeqFormatChoice.SetSelection(0) self.dirBrowse = filebrowse.DirBrowseButton(parent=imSeqPanel, id=wx.ID_ANY, labelText=_("Directory:"), dialogTitle=_("Choose directory for export"), buttonText=_("Browse"), startDirectory=os.getcwd()) dirGridSizer = wx.GridBagSizer(hgap=5, vgap=5) dirGridSizer.Add(prefixLabel, pos=(0, 0), flag = wx.ALIGN_CENTER_VERTICAL) dirGridSizer.Add(self.prefixCtrl, pos=(0, 1), flag = wx.EXPAND) dirGridSizer.Add(formatLabel, pos=(1, 0), flag = wx.ALIGN_CENTER_VERTICAL) dirGridSizer.Add(self.imSeqFormatChoice, pos=(1, 1), flag = wx.EXPAND) dirGridSizer.Add(self.dirBrowse, pos=(2, 0), flag = wx.EXPAND, span = (1, 2)) dirGridSizer.AddGrowableCol(1) imSeqPanel.SetSizer(dirGridSizer) dirGridSizer.Fit(imSeqPanel) self.formatPanelSizer.Add(item=imSeqPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) self.formatPanels.append(imSeqPanel) # panel for gif gifPanel = wx.Panel(parent=panel, id=wx.ID_ANY) self.gifBrowse = filebrowse.FileBrowseButton(parent=gifPanel, id=wx.ID_ANY, fileMask="GIF file (*.gif)|*.gif", labelText=_("GIF file:"), dialogTitle=_("Choose file to save animation"), buttonText=_("Browse"), startDirectory=os.getcwd(), fileMode=wx.SAVE) gifGridSizer = wx.GridBagSizer(hgap=5, vgap=5) gifGridSizer.AddGrowableCol(0) gifGridSizer.Add(self.gifBrowse, pos=(0, 0), flag = wx.EXPAND) gifPanel.SetSizer(gifGridSizer) gifGridSizer.Fit(gifPanel) self.formatPanelSizer.Add(item=gifPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) self.formatPanels.append(gifPanel) # panel for swf swfPanel = wx.Panel(parent=panel, id=wx.ID_ANY) self.swfBrowse = filebrowse.FileBrowseButton(parent=swfPanel, id=wx.ID_ANY, fileMask="SWF file (*.swf)|*.swf", labelText=_("SWF file:"), dialogTitle=_("Choose file to save animation"), buttonText=_("Browse"), startDirectory=os.getcwd(), fileMode=wx.SAVE) swfGridSizer = wx.GridBagSizer(hgap=5, vgap=5) swfGridSizer.AddGrowableCol(0) swfGridSizer.Add(self.swfBrowse, pos=(0, 0), flag = wx.EXPAND) swfPanel.SetSizer(swfGridSizer) swfGridSizer.Fit(swfPanel) self.formatPanelSizer.Add(item=swfPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) self.formatPanels.append(swfPanel) # panel for avi aviPanel = wx.Panel(parent=panel, id=wx.ID_ANY) ffmpeg = gcore.find_program('ffmpeg', '--help') if not ffmpeg: warning = _("Program 'ffmpeg' was not found.\nPlease install it first " "and make sure\nit's in the PATH variable.") warningLabel = wx.StaticText(parent=aviPanel, label=warning) warningLabel.SetForegroundColour(wx.RED) self.aviBrowse = filebrowse.FileBrowseButton(parent=aviPanel, id=wx.ID_ANY, fileMask="AVI file (*.avi)|*.avi", labelText=_("AVI file:"), dialogTitle=_("Choose file to save animation"), buttonText=_("Browse"), startDirectory=os.getcwd(), fileMode=wx.SAVE) encodingLabel = wx.StaticText(parent=aviPanel, id=wx.ID_ANY, label=_("Video codec:")) self.encodingText = wx.TextCtrl(parent=aviPanel, id=wx.ID_ANY, value='mpeg4') optionsLabel = wx.StaticText(parent=aviPanel, label=_("Additional options:")) self.optionsText = wx.TextCtrl(parent=aviPanel) self.optionsText.SetToolTipString(_("Consider adding '-sameq' or '-qscale 1' " "if not satisfied with video quality. " "Options depend on ffmpeg version.")) aviGridSizer = wx.GridBagSizer(hgap=5, vgap=5) aviGridSizer.Add(self.aviBrowse, pos=(0, 0), span = (1, 2), flag = wx.EXPAND) aviGridSizer.Add(encodingLabel, pos=(1, 0), flag = wx.ALIGN_CENTER_VERTICAL) aviGridSizer.Add(self.encodingText, pos=(1, 1), flag = wx.EXPAND) aviGridSizer.Add(optionsLabel, pos=(2, 0), flag=wx.ALIGN_CENTER_VERTICAL) aviGridSizer.Add(self.optionsText, pos=(2, 1), flag=wx.EXPAND) if not ffmpeg: aviGridSizer.Add(warningLabel, pos=(3, 0), span=(1, 2), flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) aviGridSizer.AddGrowableCol(1) aviPanel.SetSizer(aviGridSizer) aviGridSizer.Fit(aviPanel) self.formatPanelSizer.Add(item=aviPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) self.formatPanels.append(aviPanel) fpsSizer = wx.BoxSizer(wx.HORIZONTAL) fps = 1000 / self.timeTick fpsSizer.Add(wx.StaticText(panel, id=wx.ID_ANY, label=_("Current frame rate: %.2f fps") % fps), proportion=1, flag=wx.EXPAND) borderSizer.Add(fpsSizer, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5) panel.SetSizer(borderSizer) borderSizer.Fit(panel) self.ChangeFormat(index=0) return panel def ChangeFormat(self, index): for i, panel in enumerate(self.formatPanels): self.formatPanelSizer.Show(item=panel, show=(i == index)) self.formatPanelSizer.Layout() def OnFont(self, event): index = self.listbox.GetSelection() # should not happen if index == wx.NOT_FOUND: return cdata = self.listbox.GetClientData(index) font = cdata['font'] fontdata = wx.FontData() fontdata.EnableEffects(True) fontdata.SetColour('black') fontdata.SetInitialFont(font) dlg = wx.FontDialog(self, fontdata) dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_OK: newfontdata = dlg.GetFontData() font = newfontdata.GetChosenFont() self.sampleLabel.SetFont(font) cdata['font'] = font self.Layout() def OnPosition(self, event, coord): index = self.listbox.GetSelection() # should not happen if index == wx.NOT_FOUND: return cdata = self.listbox.GetClientData(index) cdata['pos'][coord == 'Y'] = event.GetInt() def OnSetImage(self, event): index = self.listbox.GetSelection() # should not happen if index == wx.NOT_FOUND: return cdata = self.listbox.GetClientData(index) cdata['file'] = event.GetString() def OnAddDecoration(self, event, name): if name == 'time': timeInfo = {'name': name, 'font': self.GetFont(), 'pos': [10, 10]} self.decorations.append(timeInfo) elif name == 'image': imageInfo = {'name': name, 'file': '', 'pos': [10, 10]} self.decorations.append(imageInfo) elif name == 'text': textInfo = {'name': name, 'font': self.GetFont(), 'text': '', 'pos': [10, 10]} self.decorations.append(textInfo) self._updateListBox() self.listbox.SetSelection(self.listbox.GetCount() - 1) self.OnSelectionChanged(event=None) def OnSelectionChanged(self, event): index = self.listbox.GetSelection() if index == wx.NOT_FOUND: self._hideAll() return cdata = self.listbox.GetClientData(index) self.hidevbox.Show(self.fontBox, (cdata['name'] in ('time', 'text'))) self.hidevbox.Show(self.imageBox, (cdata['name'] == 'image')) self.hidevbox.Show(self.textBox, (cdata['name'] == 'text')) self.hidevbox.Show(self.posBox, True) self.hidevbox.Show(self.informBox, False) self.spinX.SetValue(cdata['pos'][0]) self.spinY.SetValue(cdata['pos'][1]) if cdata['name'] == 'image': self.browse.SetValue(cdata['file']) elif cdata['name'] in ('time', 'text'): self.sampleLabel.SetFont(cdata['font']) if cdata['name'] == 'text': self.textCtrl.SetValue(cdata['text']) self.hidevbox.Layout() # self.Layout() def OnText(self, event): index = self.listbox.GetSelection() # should not happen if index == wx.NOT_FOUND: return cdata = self.listbox.GetClientData(index) cdata['text'] = event.GetString() def OnRemove(self, event): index = self.listbox.GetSelection() if index == wx.NOT_FOUND: return decData = self.listbox.GetClientData(index) self.decorations.remove(decData) self._updateListBox() if self.listbox.GetCount(): self.listbox.SetSelection(0) self.OnSelectionChanged(event=None) def OnExport(self, event): for decor in self.decorations: if decor['name'] == 'image': if not os.path.exists(decor['file']): if decor['file']: GError(parent=self, message=_("File %s not found.") % decor['file']) else: GError(parent=self, message=_("Decoration image file is missing.")) return if self.formatChoice.GetSelection() == 0: name = self.dirBrowse.GetValue() if not os.path.exists(name): if name: GError(parent=self, message=_("Directory %s not found.") % name) else: GError(parent=self, message=_("Export directory is missing.")) return elif self.formatChoice.GetSelection() == 1: if not self.gifBrowse.GetValue(): GError(parent=self, message=_("Export file is missing.")) return elif self.formatChoice.GetSelection() == 2: if not self.swfBrowse.GetValue(): GError(parent=self, message=_("Export file is missing.")) return # hide only to keep previous values self.Hide() self.doExport.emit(exportInfo=self.GetExportInformation(), decorations=self.GetDecorations()) def GetDecorations(self): return self.decorations def GetExportInformation(self): info = {} if self.formatChoice.GetSelection() == 0: info['method'] = 'sequence' info['directory'] = self.dirBrowse.GetValue() info['prefix'] = self.prefixCtrl.GetValue() info['format'] = self.imSeqFormatChoice.GetStringSelection() elif self.formatChoice.GetSelection() == 1: info['method'] = 'gif' info['file'] = self.gifBrowse.GetValue() elif self.formatChoice.GetSelection() == 2: info['method'] = 'swf' info['file'] = self.swfBrowse.GetValue() elif self.formatChoice.GetSelection() == 3: info['method'] = 'avi' info['file'] = self.aviBrowse.GetValue() info['encoding'] = self.encodingText.GetValue() info['options'] = self.optionsText.GetValue() return info def _updateListBox(self): self.listbox.Clear() names = {'time': _("Time stamp"), 'image': _("Image"), 'text': _("Text")} for decor in self.decorations: self.listbox.Append(names[decor['name']], clientData=decor) def _hideAll(self): self.hidevbox.Show(self.fontBox, False) self.hidevbox.Show(self.imageBox, False) self.hidevbox.Show(self.textBox, False) self.hidevbox.Show(self.posBox, False) self.hidevbox.Show(self.informBox, True) self.hidevbox.Layout() class AnimSimpleLayerManager(SimpleLayerManager): """!Simple layer manager for animation tool. Allows to add space-time dataset or series of maps. """ def __init__(self, parent, layerList, lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_TOP | SIMPLE_LMGR_STDS, toolbarCls=AnimSimpleLmgrToolbar, modal=True): SimpleLayerManager.__init__(self, parent, layerList, lmgrStyle, toolbarCls, modal) self._3dActivated = False def OnAddStds(self, event): """!Opens dialog for specifying temporal dataset. Dummy layer is added first.""" layer = AnimLayer() layer.hidden = True self._layerList.AddLayer(layer) self.SetStdsProperties(layer) event.Skip() def SetStdsProperties(self, layer): dlg = AddTemporalLayerDialog(parent=self, layer=layer, volume=self._3dActivated) # first get hidden property, it's altered afterwards hidden = layer.hidden dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_OK: layer = dlg.GetLayer() if hidden: signal = self.layerAdded else: signal = self.cmdChanged signal.emit(index=self._layerList.GetLayerIndex(layer), layer=layer) else: if hidden: self._layerList.RemoveLayer(layer) dlg.Destroy() self._update() self.anyChange.emit() def _layerChangeProperties(self, layer): """!Opens new module dialog or recycles it.""" if not hasattr(layer, 'maps'): GUI(parent=self, giface=None, modal=self._modal).ParseCommand(cmd=layer.cmd, completed=(self.GetOptData, layer, '')) else: self.SetStdsProperties(layer) def Activate3D(self, activate=True): """!Activates/deactivates certain tool depending on 2D/3D view.""" self._toolbar.EnableTools(['addRaster', 'addVector', 'opacity', 'up', 'down'], not activate) self._3dActivated = activate class AddTemporalLayerDialog(wx.Dialog): """!Dialog for adding space-time dataset/ map series.""" def __init__(self, parent, layer, volume=False, title=_("Add space-time dataset layer")): wx.Dialog.__init__(self, parent=parent, title=title) self.layer = layer self._mapType = None self._name = None self._cmd = None self.tselect = Select(parent=self, type='strds') iconTheme = UserSettings.Get(group='appearance', key='iconTheme', subkey='type') bitmapPath = os.path.join(globalvar.ICONDIR, 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(self, bitmap=bitmap) self.addManyMapsButton.Bind(wx.EVT_BUTTON, self._onAddMaps) types = [('rast', _("Multiple raster maps")), ('vect', _("Multiple vector maps")), ('rast3d', _("Multiple 3D raster maps")), ('strds', _("Space time raster dataset")), ('stvds', _("Space time vector dataset")), ('str3ds', _("Space time 3D raster dataset"))] if not volume: del types[5] del types[2] self._types = dict(types) self.tchoice = wx.Choice(parent=self) for type_, text in types: self.tchoice.Append(text, clientData=type_) self.editBtn = wx.Button(parent=self, label='Set properties') self.okBtn = wx.Button(parent=self, id=wx.ID_OK) self.cancelBtn = wx.Button(parent=self, id=wx.ID_CANCEL) self.okBtn.Bind(wx.EVT_BUTTON, self._onOK) self.editBtn.Bind(wx.EVT_BUTTON, self._onProperties) self.tchoice.Bind(wx.EVT_CHOICE, lambda evt: self._setType()) self.tselect.Bind(wx.EVT_TEXT, lambda evt: self._datasetChanged()) if self.layer.mapType: self._setType(self.layer.mapType) else: self._setType('rast') if self.layer.name: self.tselect.SetValue(self.layer.name) if self.layer.cmd: self._cmd = self.layer.cmd self._layout() self.SetSize(self.GetBestSize()) def _layout(self): mainSizer = wx.BoxSizer(wx.VERTICAL) bodySizer = wx.BoxSizer(wx.VERTICAL) typeSizer = wx.BoxSizer(wx.HORIZONTAL) selectSizer = wx.BoxSizer(wx.HORIZONTAL) typeSizer.Add(wx.StaticText(self, label=_("Input data type:")), flag=wx.ALIGN_CENTER_VERTICAL) typeSizer.AddStretchSpacer() typeSizer.Add(self.tchoice) bodySizer.Add(typeSizer, flag=wx.EXPAND | wx.BOTTOM, border=5) selectSizer.Add(self.tselect, flag=wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, border=5) selectSizer.Add(self.addManyMapsButton, flag=wx.EXPAND) bodySizer.Add(selectSizer, flag=wx.BOTTOM, border=5) bodySizer.Add(self.editBtn, flag=wx.BOTTOM, border=5) mainSizer.Add(bodySizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=10) btnSizer = wx.StdDialogButtonSizer() btnSizer.AddButton(self.okBtn) btnSizer.AddButton(self.cancelBtn) btnSizer.Realize() mainSizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=10) self.SetSizer(mainSizer) mainSizer.Fit(self) def _datasetChanged(self): if self._name != self.tselect.GetValue(): self._name = self.tselect.GetValue() self._cmd = None def _setType(self, typeName=None): if typeName: self.tchoice.SetStringSelection(self._types[typeName]) self.tselect.SetType(typeName) if typeName in ('strds', 'stvds', 'str3ds'): self.tselect.SetType(typeName, multiple=False) self.addManyMapsButton.Disable() else: self.tselect.SetType(typeName, multiple=True) self.addManyMapsButton.Enable() self._mapType = typeName self.tselect.SetValue('') else: typeName = self.tchoice.GetClientData(self.tchoice.GetSelection()) if typeName in ('strds', 'stvds', 'str3ds'): self.tselect.SetType(typeName, multiple=False) self.addManyMapsButton.Disable() else: self.tselect.SetType(typeName, multiple=True) self.addManyMapsButton.Enable() if typeName != self._mapType: self._cmd = None self._mapType = typeName self.tselect.SetValue('') def _createDefaultCommand(self): cmd = [] if self._mapType in ('rast', 'strds'): cmd.append('d.rast') elif self._mapType in ('vect', 'stvds'): cmd.append('d.vect') elif self._mapType in ('rast3d', 'str3ds'): cmd.append('d.rast3d') if self._name: if self._mapType in ('rast', 'vect', 'rast3d'): cmd.append('map={name}'.format(name=self._name.split(',')[0])) else: try: maps = getRegisteredMaps(self._name, etype=self._mapType) if maps: cmd.append('map={name}'.format(name=maps[0])) except gcore.ScriptError, e: GError(parent=self, message=str(e), showTraceback=False) return None return cmd def _onAddMaps(self, event): dlg = MapLayersDialog(self, title=_("Select raster/vector maps.")) dlg.applyAddingMapLayers.connect(lambda mapLayers: self.tselect.SetValue(','.join(mapLayers))) if self._mapType == 'rast': index = 0 elif self._mapType == 'vect': index = 2 else: # rast3d index = 1 dlg.layerType.SetSelection(index) dlg.LoadMapLayers(dlg.GetLayerType(cmd=True), dlg.mapset.GetStringSelection()) dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_OK: self.tselect.SetValue(','.join(dlg.GetMapLayers())) dlg.Destroy() def _onProperties(self, event): self._checkInput() if self._cmd: GUI(parent=self, show=True, modal=True).ParseCommand(cmd=self._cmd, completed=(self._getOptData, '', '')) def _checkInput(self): if not self.tselect.GetValue(): GMessage(parent=self, message=_("Please select maps or dataset first.")) return if not self._cmd: self._cmd = self._createDefaultCommand() def _getOptData(self, dcmd, layer, params, propwin): if dcmd: self._cmd = dcmd def _onOK(self, event): self._checkInput() if self._cmd: try: self.layer.hidden = False self.layer.mapType = self._mapType self.layer.name = self._name self.layer.cmd = self._cmd event.Skip() except (GException, gcore.ScriptError), e: GError(parent=self, message=str(e)) def GetLayer(self): return self.layer class PreferencesDialog(PreferencesBaseDialog): """!Animation preferences dialog""" def __init__(self, parent, giface, title=_("Animation Tool settings"), settings=UserSettings): PreferencesBaseDialog.__init__(self, parent=parent, giface=giface, title=title, settings=settings, size=(-1, 270)) self._timeFormats = ['%Y-%m-%d %H:%M:%S', # 2013-12-29 11:16:26 '%Y-%m-%d', # 2013-12-29 '%c', # Sun Dec 29 11:16:26 2013 (locale-dependent) '%x', # 12/29/13 (locale-dependent) '%X', # 11:16:26 (locale-dependent) '%b %d, %Y', # Dec 29, 2013 '%B %d, %Y', # December 29, 2013 '%B, %Y', # December 2013 '%I:%M %p', # 11:16 AM '%I %p', # 11 AM ] self._format = None # create notebook pages self._createGeneralPage(self.notebook) self._createTemporalPage(self.notebook) self.SetMinSize(self.GetBestSize()) self.SetSize(self.size) def _createGeneralPage(self, notebook): """!Create notebook page for general settings""" panel = SP.ScrolledPanel(parent=notebook) panel.SetupScrolling(scroll_x=False, scroll_y=True) notebook.AddPage(page=panel, text=_("General")) border = wx.BoxSizer(wx.VERTICAL) sizer = wx.BoxSizer(wx.VERTICAL) gridSizer = wx.GridBagSizer(hgap=3, vgap=3) row = 0 gridSizer.Add(item=wx.StaticText(parent=panel, label=_("Background color:")), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) color = csel.ColourSelect(parent=panel, colour=UserSettings.Get(group='animation', key='bgcolor', subkey='color'), size=globalvar.DIALOG_COLOR_SIZE) color.SetName('GetColour') self.winId['animation:bgcolor:color'] = color.GetId() gridSizer.Add(item=color, pos=(row, 1), flag=wx.ALIGN_RIGHT) gridSizer.AddGrowableCol(1) sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=3) border.Add(item=sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3) panel.SetSizer(border) return panel def _createTemporalPage(self, notebook): """!Create notebook page for temporal settings""" panel = SP.ScrolledPanel(parent=notebook) panel.SetupScrolling(scroll_x=False, scroll_y=True) notebook.AddPage(page=panel, text=_("Time")) border = wx.BoxSizer(wx.VERTICAL) sizer = wx.BoxSizer(wx.VERTICAL) gridSizer = wx.GridBagSizer(hgap=5, vgap=5) row = 0 gridSizer.Add(item=wx.StaticText(parent=panel, label=_("Absolute time format:")), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, pos=(row, 0)) self.tempFormat = wx.ComboBox(parent=panel, name='GetValue') self.tempFormat.SetItems(self._timeFormats) self.tempFormat.SetValue(self.settings.Get(group='animation', key='temporal', subkey='format')) self.winId['animation:temporal:format'] = self.tempFormat.GetId() gridSizer.Add(item=self.tempFormat, pos=(row, 1), flag=wx.ALIGN_RIGHT) self.infoTimeLabel = wx.StaticText(parent=panel) self.tempFormat.Bind(wx.EVT_COMBOBOX, lambda evt: self._setTimeFormat(self.tempFormat.GetValue())) self.tempFormat.Bind(wx.EVT_TEXT, lambda evt: self._setTimeFormat(self.tempFormat.GetValue())) self.tempFormat.SetToolTipString(_("Click and then press key up or down to preview " "different date and time formats. " "Type custom format string.")) row += 1 gridSizer.Add(item=self.infoTimeLabel, pos=(row, 0), span=(1, 2), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT) self._setTimeFormat(self.tempFormat.GetValue()) row += 1 link = wx.HyperlinkCtrl(panel, id=wx.ID_ANY, label=_("Learn more about formatting options"), url="http://docs.python.org/2/library/datetime.html#" "strftime-and-strptime-behavior") link.SetNormalColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_GRAYTEXT)) link.SetVisitedColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_GRAYTEXT)) gridSizer.Add(item=link, pos=(row, 0), span=(1, 2), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT) row += 2 noDataCheck = wx.CheckBox(panel, label=_("Display instances with no data")) noDataCheck.SetToolTipString(_("When animating instant-based data which have irregular timestamps " "you can display 'no data frame' (checked option) or " "keep last frame.")) noDataCheck.SetValue(self.settings.Get(group='animation', key='temporal', subkey=['nodata', 'enable'])) self.winId['animation:temporal:nodata:enable'] = noDataCheck.GetId() gridSizer.Add(item=noDataCheck, pos=(row, 0), span=(1, 2), flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT) gridSizer.AddGrowableCol(1) sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=3) border.Add(item=sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3) panel.SetSizer(border) return panel def _setTimeFormat(self, formatString): now = datetime.datetime.now() try: label = datetime.datetime.strftime(now, formatString) self._format = formatString except ValueError: label = _("Invalid") self.infoTimeLabel.SetLabel(label) self.infoTimeLabel.GetContainingSizer().Layout() def _updateSettings(self): self.tempFormat.SetValue(self._format) return PreferencesBaseDialog._updateSettings(self) def test(): import wx.lib.inspection app = wx.App() # testTemporalLayer() # testAnimLmgr() testAnimInput() # wx.lib.inspection.InspectionTool().Show() app.MainLoop() def testAnimInput(): anim = AnimationData() anim.SetDefaultValues(animationIndex=0, windowIndex=0) dlg = InputDialog(parent=None, mode='add', animationData=anim) dlg.Show() def testAnimEdit(): anim = AnimationData() anim.SetDefaultValues(animationIndex=0, windowIndex=0) dlg = EditDialog(parent=None, animationData=[anim]) dlg.Show() def testExport(): dlg = ExportDialog(parent=None, temporal=TemporalMode.TEMPORAL, timeTick=200) if dlg.ShowModal() == wx.ID_OK: print dlg.GetDecorations() print dlg.GetExportInformation() dlg.Destroy() else: dlg.Destroy() def testTemporalLayer(): frame = wx.Frame(None) frame.Show() layer = AnimLayer() dlg = AddTemporalLayerDialog(parent=frame, layer=layer) if dlg.ShowModal() == wx.ID_OK: layer = dlg.GetLayer() print layer.name, layer.cmd, layer.mapType dlg.Destroy() else: dlg.Destroy() def testAnimLmgr(): from core.layerlist import LayerList frame = wx.Frame(None) mgr = AnimSimpleLayerManager(parent=frame, layerList=LayerList()) frame.mgr = mgr frame.Show() if __name__ == '__main__': gcore.set_raise_on_error(True) test()