dialogs.py 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537
  1. """!
  2. @package animation.dialogs
  3. @brief Dialogs for animation management, changing speed of animation
  4. Classes:
  5. - dialogs::SpeedDialog
  6. - dialogs::InputDialog
  7. - dialogs::EditDialog
  8. - dialogs::ExportDialog
  9. - dialogs::AnimSimpleLayerManager
  10. - dialogs::AddTemporalLayerDialog
  11. (C) 2013 by the GRASS Development Team
  12. This program is free software under the GNU General Public License
  13. (>=v2). Read the file COPYING that comes with GRASS for details.
  14. @author Anna Petrasova <kratochanna gmail.com>
  15. """
  16. import os
  17. import sys
  18. import wx
  19. import copy
  20. import datetime
  21. import wx.lib.filebrowsebutton as filebrowse
  22. import wx.lib.scrolledpanel as SP
  23. import wx.lib.colourselect as csel
  24. if __name__ == '__main__':
  25. sys.path.append(os.path.join(os.environ['GISBASE'], "etc", "gui", "wxpython"))
  26. from core.gcmd import GMessage, GError, GException
  27. from core import globalvar
  28. from gui_core.dialogs import MapLayersDialog, GetImageHandlers
  29. from gui_core.preferences import PreferencesBaseDialog
  30. from gui_core.forms import GUI
  31. from core.settings import UserSettings
  32. from core.utils import _
  33. from gui_core.gselect import Select
  34. from animation.utils import TemporalMode, getRegisteredMaps
  35. from animation.data import AnimationData, AnimLayer
  36. from animation.toolbars import AnimSimpleLmgrToolbar, SIMPLE_LMGR_STDS
  37. from gui_core.simplelmgr import SimpleLayerManager, \
  38. SIMPLE_LMGR_RASTER, SIMPLE_LMGR_VECTOR, SIMPLE_LMGR_TB_TOP
  39. from grass.pydispatch.signal import Signal
  40. import grass.script.core as gcore
  41. class SpeedDialog(wx.Dialog):
  42. def __init__(self, parent, title=_("Adjust speed of animation"),
  43. temporalMode=None, minimumDuration=20, timeGranularity=None,
  44. initialSpeed=200):
  45. wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=title,
  46. style=wx.DEFAULT_DIALOG_STYLE)
  47. # signal emitted when speed has changed; has attribute 'ms'
  48. self.speedChanged = Signal('SpeedDialog.speedChanged')
  49. self.minimumDuration = minimumDuration
  50. # self.framesCount = framesCount
  51. self.defaultSpeed = initialSpeed
  52. self.lastAppliedValue = self.defaultSpeed
  53. self.lastAppliedValueTemp = self.defaultSpeed
  54. self._layout()
  55. self.temporalMode = temporalMode
  56. self.timeGranularity = timeGranularity
  57. self._fillUnitChoice(self.choiceUnits)
  58. self.InitTimeSpin(self.defaultSpeed)
  59. def SetTimeGranularity(self, gran):
  60. self._timeGranularity = gran
  61. def GetTimeGranularity(self):
  62. return self._timeGranularity
  63. timeGranularity = property(fset=SetTimeGranularity, fget=GetTimeGranularity)
  64. def SetTemporalMode(self, mode):
  65. self._temporalMode = mode
  66. self._setTemporalMode()
  67. def GetTemporalMode(self):
  68. return self._temporalMode
  69. temporalMode = property(fset=SetTemporalMode, fget=GetTemporalMode)
  70. def _layout(self):
  71. """!Layout window"""
  72. mainSizer = wx.BoxSizer(wx.VERTICAL)
  73. #
  74. # simple mode
  75. #
  76. self.nontemporalBox = wx.StaticBox(parent=self, id=wx.ID_ANY,
  77. label=' %s ' % _("Simple mode"))
  78. box = wx.StaticBoxSizer(self.nontemporalBox, wx.VERTICAL)
  79. gridSizer = wx.GridBagSizer(hgap=5, vgap=5)
  80. labelDuration = wx.StaticText(self, id=wx.ID_ANY, label=_("Frame duration:"))
  81. labelUnits = wx.StaticText(self, id=wx.ID_ANY, label=_("ms"))
  82. self.spinDuration = wx.SpinCtrl(self, id=wx.ID_ANY, min=self.minimumDuration,
  83. max=10000, initial=self.defaultSpeed)
  84. # TODO total time
  85. gridSizer.Add(item=labelDuration, pos=(0, 0), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
  86. gridSizer.Add(item=self.spinDuration, pos=(0, 1), flag = wx.ALIGN_CENTER)
  87. gridSizer.Add(item=labelUnits, pos=(0, 2), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
  88. gridSizer.AddGrowableCol(0)
  89. box.Add(item=gridSizer, proportion=1, border=5, flag=wx.ALL | wx.EXPAND)
  90. self.nontemporalSizer = gridSizer
  91. mainSizer.Add(item=box, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
  92. #
  93. # temporal mode
  94. #
  95. self.temporalBox = wx.StaticBox(parent=self, id=wx.ID_ANY,
  96. label=' %s ' % _("Temporal mode"))
  97. box = wx.StaticBoxSizer(self.temporalBox, wx.VERTICAL)
  98. gridSizer = wx.GridBagSizer(hgap=5, vgap=5)
  99. labelTimeUnit = wx.StaticText(self, id=wx.ID_ANY, label=_("Time unit:"))
  100. labelDuration = wx.StaticText(self, id=wx.ID_ANY, label=_("Duration of time unit:"))
  101. labelUnits = wx.StaticText(self, id=wx.ID_ANY, label=_("ms"))
  102. self.spinDurationTemp = wx.SpinCtrl(self, id=wx.ID_ANY, min=self.minimumDuration,
  103. max=10000, initial=self.defaultSpeed)
  104. self.choiceUnits = wx.Choice(self, id=wx.ID_ANY)
  105. # TODO total time
  106. gridSizer.Add(item=labelTimeUnit, pos=(0, 0), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
  107. gridSizer.Add(item=self.choiceUnits, pos=(0, 1), flag = wx.ALIGN_CENTER | wx.EXPAND)
  108. gridSizer.Add(item=labelDuration, pos=(1, 0), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
  109. gridSizer.Add(item=self.spinDurationTemp, pos=(1, 1), flag = wx.ALIGN_CENTER | wx.EXPAND)
  110. gridSizer.Add(item=labelUnits, pos=(1, 2), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
  111. gridSizer.AddGrowableCol(1)
  112. self.temporalSizer = gridSizer
  113. box.Add(item=gridSizer, proportion=1, border=5, flag=wx.ALL | wx.EXPAND)
  114. mainSizer.Add(item=box, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
  115. self.btnOk = wx.Button(self, wx.ID_OK)
  116. self.btnApply = wx.Button(self, wx.ID_APPLY)
  117. self.btnCancel = wx.Button(self, wx.ID_CANCEL)
  118. self.btnOk.SetDefault()
  119. self.btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
  120. self.btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
  121. self.btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
  122. self.Bind(wx.EVT_CLOSE, self.OnCancel)
  123. # button sizer
  124. btnStdSizer = wx.StdDialogButtonSizer()
  125. btnStdSizer.AddButton(self.btnOk)
  126. btnStdSizer.AddButton(self.btnApply)
  127. btnStdSizer.AddButton(self.btnCancel)
  128. btnStdSizer.Realize()
  129. mainSizer.Add(item=btnStdSizer, proportion=0,
  130. flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)
  131. self.SetSizer(mainSizer)
  132. mainSizer.Fit(self)
  133. def _setTemporalMode(self):
  134. self.nontemporalBox.Enable(self.temporalMode == TemporalMode.NONTEMPORAL)
  135. self.temporalBox.Enable(self.temporalMode == TemporalMode.TEMPORAL)
  136. for child in self.temporalSizer.GetChildren():
  137. child.GetWindow().Enable(self.temporalMode == TemporalMode.TEMPORAL)
  138. for child in self.nontemporalSizer.GetChildren():
  139. child.GetWindow().Enable(self.temporalMode == TemporalMode.NONTEMPORAL)
  140. self.Layout()
  141. def _fillUnitChoice(self, choiceWidget):
  142. timeUnitsChoice = [_("year"), _("month"), _("day"), _("hour"), _("minute"), _("second")]
  143. timeUnits = ["years", "months", "days", "hours", "minutes", "seconds"]
  144. for item, cdata in zip(timeUnitsChoice, timeUnits):
  145. choiceWidget.Append(item, cdata)
  146. if self.temporalMode == TemporalMode.TEMPORAL:
  147. unit = self.timeGranularity[1]
  148. try:
  149. index = timeUnits.index(unit)
  150. except ValueError:
  151. index = 0
  152. choiceWidget.SetSelection(index)
  153. else:
  154. choiceWidget.SetSelection(0)
  155. def OnOk(self, event):
  156. self._apply()
  157. self.OnCancel(None)
  158. def OnApply(self, event):
  159. self._apply()
  160. def OnCancel(self, event):
  161. self.spinDuration.SetValue(self.lastAppliedValue)
  162. self.spinDurationTemp.SetValue(self.lastAppliedValueTemp)
  163. self.Hide()
  164. def InitTimeSpin(self, timeTick):
  165. if self.temporalMode == TemporalMode.TEMPORAL:
  166. index = self.choiceUnits.GetSelection()
  167. unit = self.choiceUnits.GetClientData(index)
  168. delta = self._timedelta(unit=unit, number=1)
  169. seconds1 = self._total_seconds(delta)
  170. number, unit = self.timeGranularity
  171. number = float(number)
  172. delta = self._timedelta(unit=unit, number=number)
  173. seconds2 = self._total_seconds(delta)
  174. value = timeTick
  175. ms = value * seconds1 / float(seconds2)
  176. self.spinDurationTemp.SetValue(ms)
  177. else:
  178. self.spinDuration.SetValue(timeTick)
  179. def _apply(self):
  180. if self.temporalMode == TemporalMode.NONTEMPORAL:
  181. ms = self.spinDuration.GetValue()
  182. self.lastAppliedValue = self.spinDuration.GetValue()
  183. elif self.temporalMode == TemporalMode.TEMPORAL:
  184. index = self.choiceUnits.GetSelection()
  185. unit = self.choiceUnits.GetClientData(index)
  186. delta = self._timedelta(unit=unit, number=1)
  187. seconds1 = self._total_seconds(delta)
  188. number, unit = self.timeGranularity
  189. number = float(number)
  190. delta = self._timedelta(unit=unit, number=number)
  191. seconds2 = self._total_seconds(delta)
  192. value = self.spinDurationTemp.GetValue()
  193. ms = value * seconds2 / float(seconds1)
  194. if ms < self.minimumDuration:
  195. GMessage(parent=self, message=_("Animation speed is too high."))
  196. return
  197. self.lastAppliedValueTemp = self.spinDurationTemp.GetValue()
  198. else:
  199. return
  200. self.speedChanged.emit(ms=ms)
  201. def _timedelta(self, unit, number):
  202. if unit in "years":
  203. delta = datetime.timedelta(days=365.25 * number)
  204. elif unit in "months":
  205. delta = datetime.timedelta(days=30.4375 * number) # 365.25/12
  206. elif unit in "days":
  207. delta = datetime.timedelta(days=1 * number)
  208. elif unit in "hours":
  209. delta = datetime.timedelta(hours=1 * number)
  210. elif unit in "minutes":
  211. delta = datetime.timedelta(minutes=1 * number)
  212. elif unit in "seconds":
  213. delta = datetime.timedelta(seconds=1 * number)
  214. return delta
  215. def _total_seconds(self, delta):
  216. """!timedelta.total_seconds is new in version 2.7.
  217. """
  218. return delta.seconds + delta.days * 24 * 3600
  219. class InputDialog(wx.Dialog):
  220. def __init__(self, parent, mode, animationData):
  221. wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY,
  222. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
  223. if mode == 'add':
  224. self.SetTitle(_("Add new animation"))
  225. elif mode == 'edit':
  226. self.SetTitle(_("Edit animation"))
  227. self.animationData = animationData
  228. self._tmpLegendCmd = None
  229. self._layout()
  230. self.OnViewMode(event=None)
  231. def _layout(self):
  232. mainSizer = wx.BoxSizer(wx.VERTICAL)
  233. self.windowChoice = wx.Choice(self, id=wx.ID_ANY,
  234. choices=[_("top left"), _("top right"),
  235. _("bottom left"), _("bottom right")])
  236. self.windowChoice.SetSelection(self.animationData.windowIndex)
  237. self.nameCtrl = wx.TextCtrl(self, id=wx.ID_ANY, value=self.animationData.name)
  238. self.nDChoice = wx.Choice(self, id=wx.ID_ANY)
  239. mode = self.animationData.viewMode
  240. index = 0
  241. for i, (viewMode, viewModeName) in enumerate(self.animationData.viewModes):
  242. self.nDChoice.Append(viewModeName, clientData=viewMode)
  243. if mode == viewMode:
  244. index = i
  245. self.nDChoice.SetSelection(index)
  246. self.nDChoice.SetToolTipString(_("Select 2D or 3D view"))
  247. self.nDChoice.Bind(wx.EVT_CHOICE, self.OnViewMode)
  248. gridSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5)
  249. gridSizer.Add(item=wx.StaticText(self, id=wx.ID_ANY, label=_("Name:")),
  250. flag=wx.ALIGN_CENTER_VERTICAL)
  251. gridSizer.Add(item=self.nameCtrl, proportion=1, flag=wx.EXPAND)
  252. gridSizer.Add(item=wx.StaticText(self, id=wx.ID_ANY, label=_("Window position:")),
  253. flag=wx.ALIGN_CENTER_VERTICAL)
  254. gridSizer.Add(item=self.windowChoice, proportion=1, flag=wx.ALIGN_RIGHT)
  255. gridSizer.Add(item=wx.StaticText(self, id=wx.ID_ANY, label=_("View mode:")),
  256. flag=wx.ALIGN_CENTER_VERTICAL)
  257. gridSizer.Add(item=self.nDChoice, proportion=1, flag=wx.ALIGN_RIGHT)
  258. gridSizer.AddGrowableCol(0, 1)
  259. gridSizer.AddGrowableCol(1, 1)
  260. mainSizer.Add(item=gridSizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)
  261. label = _("For 3D animation, please select only one space-time dataset\n"
  262. "or one series of map layers.")
  263. self.warning3DLayers = wx.StaticText(self, label=label)
  264. self.warning3DLayers.SetForegroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_GRAYTEXT))
  265. mainSizer.Add(item=self.warning3DLayers, proportion=0, flag=wx.EXPAND | wx.LEFT, border=5)
  266. self.dataPanel = self._createDataPanel()
  267. self.threeDPanel = self._create3DPanel()
  268. mainSizer.Add(item=self.dataPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=3)
  269. mainSizer.Add(item=self.threeDPanel, proportion=0, flag=wx.EXPAND | wx.ALL, border=3)
  270. # buttons
  271. self.btnOk = wx.Button(self, wx.ID_OK)
  272. self.btnCancel = wx.Button(self, wx.ID_CANCEL)
  273. self.btnOk.SetDefault()
  274. self.btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
  275. # button sizer
  276. btnStdSizer = wx.StdDialogButtonSizer()
  277. btnStdSizer.AddButton(self.btnOk)
  278. btnStdSizer.AddButton(self.btnCancel)
  279. btnStdSizer.Realize()
  280. mainSizer.Add(item=btnStdSizer, proportion=0,
  281. flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)
  282. self.SetSizer(mainSizer)
  283. mainSizer.Fit(self)
  284. def _createDataPanel(self):
  285. panel = wx.Panel(self)
  286. slmgrSizer = wx.BoxSizer(wx.VERTICAL)
  287. self._layerList = copy.deepcopy(self.animationData.layerList)
  288. self.simpleLmgr = AnimSimpleLayerManager(parent=panel,
  289. layerList=self._layerList,
  290. modal=True)
  291. self.simpleLmgr.SetMinSize((globalvar.DIALOG_GSELECT_SIZE[0], 120))
  292. slmgrSizer.Add(self.simpleLmgr, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  293. self.legend = wx.CheckBox(panel, label=_("Show raster legend"))
  294. self.legend.SetValue(bool(self.animationData.legendCmd))
  295. self.legendBtn = wx.Button(panel, label=_("Set options"))
  296. self.legend.Bind(wx.EVT_CHECKBOX, self.OnLegend)
  297. self.legendBtn.Bind(wx.EVT_BUTTON, self.OnLegendProperties)
  298. hbox = wx.BoxSizer(wx.HORIZONTAL)
  299. hbox.Add(item=self.legend, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL)
  300. hbox.Add(item=self.legendBtn, proportion=0, flag=wx.LEFT, border=5)
  301. slmgrSizer.Add(item=hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=3)
  302. panel.SetSizerAndFit(slmgrSizer)
  303. panel.SetAutoLayout(True)
  304. return panel
  305. def _create3DPanel(self):
  306. panel = wx.Panel(self, id=wx.ID_ANY)
  307. dataStBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
  308. label=' %s ' % _("3D view parameters"))
  309. dataBoxSizer = wx.StaticBoxSizer(dataStBox, wx.VERTICAL)
  310. # workspace file
  311. self.fileSelector = \
  312. filebrowse.FileBrowseButton(parent=panel, id=wx.ID_ANY,
  313. size=globalvar.DIALOG_GSELECT_SIZE,
  314. labelText=_("Workspace file:"),
  315. dialogTitle=_("Choose workspace file to "
  316. "import 3D view parameters"),
  317. buttonText=_('Browse'),
  318. startDirectory=os.getcwd(), fileMode=0,
  319. fileMask="GRASS Workspace File (*.gxw)|*.gxw")
  320. if self.animationData.workspaceFile:
  321. self.fileSelector.SetValue(self.animationData.workspaceFile)
  322. self.paramLabel = wx.StaticText(panel, wx.ID_ANY, label=_("Parameter for animation:"))
  323. self.paramChoice = wx.Choice(panel, id=wx.ID_ANY, choices=self.animationData.nvizParameters)
  324. self.paramChoice.SetStringSelection(self.animationData.nvizParameter)
  325. hbox = wx.BoxSizer(wx.HORIZONTAL)
  326. hbox.Add(item=self.fileSelector, proportion=1, flag=wx.EXPAND | wx.ALIGN_CENTER)
  327. dataBoxSizer.Add(item=hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=3)
  328. hbox = wx.BoxSizer(wx.HORIZONTAL)
  329. hbox.Add(item=self.paramLabel, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL)
  330. hbox.Add(item=self.paramChoice, proportion=1, flag=wx.EXPAND)
  331. dataBoxSizer.Add(item=hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=3)
  332. panel.SetSizerAndFit(dataBoxSizer)
  333. panel.SetAutoLayout(True)
  334. return panel
  335. def OnViewMode(self, event):
  336. mode = self.nDChoice.GetSelection()
  337. self.Freeze()
  338. self.simpleLmgr.Activate3D(mode == 1)
  339. self.warning3DLayers.Show(mode == 1)
  340. sizer = self.threeDPanel.GetContainingSizer()
  341. sizer.Show(self.threeDPanel, mode == 1, True)
  342. sizer.Layout()
  343. self.Layout()
  344. self.Fit()
  345. self.Thaw()
  346. def OnLegend(self, event):
  347. if not self.legend.IsChecked():
  348. return
  349. if self._tmpLegendCmd or self.animationData.legendCmd:
  350. return
  351. cmd = ['d.legend', 'at=5,50,2,5']
  352. GUI(parent=self, modal=True).ParseCommand(cmd=cmd,
  353. completed=(self.GetOptData, '', ''))
  354. def OnLegendProperties(self, event):
  355. """!Set options for legend"""
  356. if self._tmpLegendCmd:
  357. cmd = self._tmpLegendCmd
  358. elif self.animationData.legendCmd:
  359. cmd = self.animationData.legendCmd
  360. else:
  361. cmd = ['d.legend', 'at=5,50,2,5']
  362. GUI(parent=self, modal=True).ParseCommand(cmd=cmd,
  363. completed=(self.GetOptData, '', ''))
  364. def GetOptData(self, dcmd, layer, params, propwin):
  365. """!Process decoration layer data"""
  366. if dcmd:
  367. self._tmpLegendCmd = dcmd
  368. if not self.legend.IsChecked():
  369. self.legend.SetValue(True)
  370. else:
  371. if not self._tmpLegendCmd and not self.animationData.legendCmd:
  372. self.legend.SetValue(False)
  373. def _update(self):
  374. if self.nDChoice.GetSelection() == 1 and len(self._layerList) > 1:
  375. raise GException(_("Only one series or space-time "
  376. "dataset is accepted for 3D mode."))
  377. hasSeries = False
  378. for layer in self._layerList:
  379. if layer.active and hasattr(layer, 'maps'):
  380. hasSeries = True
  381. break
  382. if not hasSeries:
  383. raise GException(_("No map series or space-time dataset added."))
  384. self.animationData.layerList = self._layerList
  385. self.animationData.name = self.nameCtrl.GetValue()
  386. self.animationData.windowIndex = self.windowChoice.GetSelection()
  387. sel = self.nDChoice.GetSelection()
  388. self.animationData.viewMode = self.nDChoice.GetClientData(sel)
  389. self.animationData.legendCmd = None
  390. if self._tmpLegendCmd:
  391. if self.legend.IsChecked():
  392. self.animationData.legendCmd = self._tmpLegendCmd
  393. if self.threeDPanel.IsShown():
  394. self.animationData.workspaceFile = self.fileSelector.GetValue()
  395. if self.threeDPanel.IsShown():
  396. self.animationData.nvizParameter = self.paramChoice.GetStringSelection()
  397. def OnOk(self, event):
  398. try:
  399. self._update()
  400. self.EndModal(wx.ID_OK)
  401. except (GException, ValueError, IOError) as e:
  402. GError(message=str(e), showTraceback=False, caption=_("Invalid input"))
  403. class EditDialog(wx.Dialog):
  404. def __init__(self, parent, evalFunction, animationData, maxAnimations):
  405. wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY,
  406. style=wx.DEFAULT_DIALOG_STYLE)
  407. self.animationData = copy.deepcopy(animationData)
  408. self.eval = evalFunction
  409. self.SetTitle(_("Add, edit or remove animations"))
  410. self._layout()
  411. self.SetSize((300, -1))
  412. self.maxAnimations = maxAnimations
  413. self.result = None
  414. def _layout(self):
  415. mainSizer = wx.BoxSizer(wx.VERTICAL)
  416. box = wx.StaticBox (parent=self, id=wx.ID_ANY, label=" %s " % _("List of animations"))
  417. sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
  418. gridBagSizer = wx.GridBagSizer (hgap=5, vgap=5)
  419. gridBagSizer.AddGrowableCol(0)
  420. # gridBagSizer.AddGrowableCol(1,1)
  421. self.listbox = wx.ListBox(self, id=wx.ID_ANY, choices=[], style=wx.LB_SINGLE | wx.LB_NEEDED_SB)
  422. self.listbox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnEdit)
  423. self.addButton = wx.Button(self, id=wx.ID_ANY, label=_("Add"))
  424. self.addButton.Bind(wx.EVT_BUTTON, self.OnAdd)
  425. self.editButton = wx.Button(self, id=wx.ID_ANY, label=_("Edit"))
  426. self.editButton.Bind(wx.EVT_BUTTON, self.OnEdit)
  427. self.removeButton = wx.Button(self, id=wx.ID_ANY, label=_("Remove"))
  428. self.removeButton.Bind(wx.EVT_BUTTON, self.OnRemove)
  429. self._updateListBox()
  430. gridBagSizer.Add(self.listbox, pos=(0, 0), span = (3, 1),
  431. flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0)
  432. gridBagSizer.Add(self.addButton, pos=(0, 1),
  433. flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0)
  434. gridBagSizer.Add(self.editButton, pos=(1, 1),
  435. flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0)
  436. gridBagSizer.Add(self.removeButton, pos=(2, 1),
  437. flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0)
  438. sizer.Add(gridBagSizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=5)
  439. mainSizer.Add(item=sizer, proportion=0,
  440. flag=wx.EXPAND | wx.ALL, border=5)
  441. # buttons
  442. self.btnOk = wx.Button(self, wx.ID_OK)
  443. self.btnCancel = wx.Button(self, wx.ID_CANCEL)
  444. self.btnOk.SetDefault()
  445. self.btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
  446. # button sizer
  447. btnStdSizer = wx.StdDialogButtonSizer()
  448. btnStdSizer.AddButton(self.btnOk)
  449. btnStdSizer.AddButton(self.btnCancel)
  450. btnStdSizer.Realize()
  451. mainSizer.Add(item=btnStdSizer, proportion=0,
  452. flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)
  453. self.SetSizer(mainSizer)
  454. mainSizer.Fit(self)
  455. def _updateListBox(self):
  456. self.listbox.Clear()
  457. for anim in self.animationData:
  458. self.listbox.Append(anim.name, clientData=anim)
  459. if self.animationData:
  460. self.listbox.SetSelection(0)
  461. def _getNextIndex(self):
  462. indices = [anim.windowIndex for anim in self.animationData]
  463. for i in range(self.maxAnimations):
  464. if i not in indices:
  465. return i
  466. return None
  467. def OnAdd(self, event):
  468. windowIndex = self._getNextIndex()
  469. if windowIndex is None:
  470. GMessage(self, message=_("Maximum number of animations is %d.") % self.maxAnimations)
  471. return
  472. animData = AnimationData()
  473. # number of active animations
  474. animationIndex = len(self.animationData)
  475. animData.SetDefaultValues(windowIndex, animationIndex)
  476. dlg = InputDialog(parent=self, mode='add', animationData=animData)
  477. dlg.CenterOnParent()
  478. if dlg.ShowModal() == wx.ID_CANCEL:
  479. dlg.Destroy()
  480. return
  481. dlg.Destroy()
  482. self.animationData.append(animData)
  483. self._updateListBox()
  484. def OnEdit(self, event):
  485. index = self.listbox.GetSelection()
  486. if index == wx.NOT_FOUND:
  487. return
  488. animData = self.listbox.GetClientData(index)
  489. dlg = InputDialog(parent=self, mode='edit', animationData=animData)
  490. dlg.CenterOnParent()
  491. if dlg.ShowModal() == wx.ID_CANCEL:
  492. dlg.Destroy()
  493. return
  494. dlg.Destroy()
  495. self._updateListBox()
  496. def OnRemove(self, event):
  497. index = self.listbox.GetSelection()
  498. if index == wx.NOT_FOUND:
  499. return
  500. animData = self.listbox.GetClientData(index)
  501. self.animationData.remove(animData)
  502. self._updateListBox()
  503. def GetResult(self):
  504. return self.result
  505. def OnOk(self, event):
  506. indices = set([anim.windowIndex for anim in self.animationData])
  507. if len(indices) != len(self.animationData):
  508. GError(parent=self, message=_("More animations are using one window."
  509. " Please select different window for each animation."))
  510. return
  511. try:
  512. temporalMode, tempManager = self.eval(self.animationData)
  513. except GException, e:
  514. GError(parent=self, message=e.value, showTraceback=False)
  515. return
  516. self.result = (self.animationData, temporalMode, tempManager)
  517. self.EndModal(wx.ID_OK)
  518. class ExportDialog(wx.Dialog):
  519. def __init__(self, parent, temporal, timeTick):
  520. wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=_("Export animation"),
  521. style=wx.DEFAULT_DIALOG_STYLE)
  522. self.decorations = []
  523. self.temporal = temporal
  524. self.timeTick = timeTick
  525. self._layout()
  526. # export animation
  527. self.doExport = Signal('ExportDialog::doExport')
  528. wx.CallAfter(self._hideAll)
  529. def _layout(self):
  530. notebook = wx.Notebook(self, id=wx.ID_ANY)
  531. mainSizer = wx.BoxSizer(wx.VERTICAL)
  532. notebook.AddPage(page=self._createExportFormatPanel(notebook), text=_("Format"))
  533. notebook.AddPage(page=self._createDecorationsPanel(notebook), text=_("Decorations"))
  534. mainSizer.Add(item=notebook, proportion=0,
  535. flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)
  536. self.btnExport = wx.Button(self, wx.ID_OK)
  537. self.btnExport.SetLabel(_("Export"))
  538. self.btnCancel = wx.Button(self, wx.ID_CANCEL)
  539. self.btnExport.SetDefault()
  540. self.btnExport.Bind(wx.EVT_BUTTON, self.OnExport)
  541. # button sizer
  542. btnStdSizer = wx.StdDialogButtonSizer()
  543. btnStdSizer.AddButton(self.btnExport)
  544. btnStdSizer.AddButton(self.btnCancel)
  545. btnStdSizer.Realize()
  546. mainSizer.Add(item=btnStdSizer, proportion=0,
  547. flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)
  548. self.SetSizer(mainSizer)
  549. # set the longest option to fit
  550. self.hidevbox.Show(self.fontBox, True)
  551. self.hidevbox.Show(self.imageBox, False)
  552. self.hidevbox.Show(self.textBox, True)
  553. self.hidevbox.Show(self.posBox, True)
  554. self.hidevbox.Show(self.informBox, False)
  555. mainSizer.Fit(self)
  556. def _createDecorationsPanel(self, notebook):
  557. panel = wx.Panel(notebook, id=wx.ID_ANY)
  558. sizer = wx.BoxSizer(wx.VERTICAL)
  559. sizer.Add(self._createDecorationsList(panel), proportion=0, flag=wx.ALL | wx.EXPAND, border=10)
  560. sizer.Add(self._createDecorationsProperties(panel), proportion=0, flag=wx.ALL | wx.EXPAND, border=10)
  561. panel.SetSizer(sizer)
  562. sizer.Fit(panel)
  563. return panel
  564. def _createDecorationsList(self, panel):
  565. gridBagSizer = wx.GridBagSizer(hgap=5, vgap=5)
  566. gridBagSizer.AddGrowableCol(0)
  567. self.listbox = wx.ListBox(panel, id=wx.ID_ANY, choices=[],
  568. style=wx.LB_SINGLE | wx.LB_NEEDED_SB)
  569. self.listbox.Bind(wx.EVT_LISTBOX, self.OnSelectionChanged)
  570. gridBagSizer.Add(self.listbox, pos=(0, 0), span=(4, 1),
  571. flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0)
  572. buttonNames = ['time', 'image', 'text']
  573. buttonLabels = [_("Add time stamp"), _("Add image"), _("Add text")]
  574. i = 0
  575. for buttonName, buttonLabel in zip(buttonNames, buttonLabels):
  576. if buttonName == 'time' and self.temporal == TemporalMode.NONTEMPORAL:
  577. continue
  578. btn = wx.Button(panel, id=wx.ID_ANY, name=buttonName, label=buttonLabel)
  579. btn.Bind(wx.EVT_BUTTON, lambda evt, temp=buttonName: self.OnAddDecoration(evt, temp))
  580. gridBagSizer.Add(btn, pos=(i, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0)
  581. i += 1
  582. removeButton = wx.Button(panel, id=wx.ID_ANY, label=_("Remove"))
  583. removeButton.Bind(wx.EVT_BUTTON, self.OnRemove)
  584. gridBagSizer.Add(removeButton, pos=(i, 1), flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0)
  585. return gridBagSizer
  586. def _createDecorationsProperties(self, panel):
  587. self.hidevbox = wx.BoxSizer(wx.VERTICAL)
  588. # inform label
  589. self.informBox = wx.BoxSizer(wx.HORIZONTAL)
  590. if self.temporal == TemporalMode.TEMPORAL:
  591. label = _("Add time stamp, image or text decoration by one of the buttons above.")
  592. else:
  593. label = _("Add image or text decoration by one of the buttons above.")
  594. label = wx.StaticText(panel, id=wx.ID_ANY, label=label)
  595. label.Wrap(400)
  596. self.informBox.Add(label, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border=5)
  597. self.hidevbox.Add(self.informBox, proportion=0, flag=wx.EXPAND | wx.BOTTOM, border=5)
  598. # font
  599. self.fontBox = wx.BoxSizer(wx.HORIZONTAL)
  600. self.fontBox.Add(wx.StaticText(panel, id=wx.ID_ANY, label=_("Font settings:")),
  601. proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border=5)
  602. self.sampleLabel = wx.StaticText(panel, id=wx.ID_ANY, label=_("Sample text"))
  603. self.fontBox.Add(self.sampleLabel, proportion=1,
  604. flag=wx.ALIGN_CENTER | wx.RIGHT | wx.LEFT, border=5)
  605. fontButton = wx.Button(panel, id=wx.ID_ANY, label=_("Set font"))
  606. fontButton.Bind(wx.EVT_BUTTON, self.OnFont)
  607. self.fontBox.Add(fontButton, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
  608. self.hidevbox.Add(self.fontBox, proportion=0, flag=wx.EXPAND | wx.BOTTOM, border=5)
  609. # image
  610. self.imageBox = wx.BoxSizer(wx.HORIZONTAL)
  611. filetype, ltype = GetImageHandlers(wx.EmptyImage(10, 10))
  612. self.browse = filebrowse.FileBrowseButton(parent=panel, id=wx.ID_ANY, fileMask=filetype,
  613. labelText=_("Image file:"),
  614. dialogTitle=_('Choose image file'),
  615. buttonText=_('Browse'),
  616. startDirectory=os.getcwd(), fileMode=wx.OPEN,
  617. changeCallback=self.OnSetImage)
  618. self.imageBox.Add(self.browse, proportion=1, flag=wx.EXPAND)
  619. self.hidevbox.Add(self.imageBox, proportion=0, flag=wx.EXPAND | wx.BOTTOM, border=5)
  620. # text
  621. self.textBox = wx.BoxSizer(wx.HORIZONTAL)
  622. self.textBox.Add(wx.StaticText(panel, id=wx.ID_ANY, label=_("Text:")),
  623. proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border=5)
  624. self.textCtrl = wx.TextCtrl(panel, id=wx.ID_ANY)
  625. self.textCtrl.Bind(wx.EVT_TEXT, self.OnText)
  626. self.textBox.Add(self.textCtrl, proportion=1, flag=wx.EXPAND)
  627. self.hidevbox.Add(self.textBox, proportion=0, flag=wx.EXPAND)
  628. self.posBox = self._positionWidget(panel)
  629. self.hidevbox.Add(self.posBox, proportion=0, flag=wx.EXPAND | wx.TOP, border=5)
  630. return self.hidevbox
  631. def _positionWidget(self, panel):
  632. grid = wx.GridBagSizer(vgap=5, hgap=5)
  633. label = wx.StaticText(panel, id=wx.ID_ANY, label=_("Placement as percentage of"
  634. " screen coordinates (X: 0, Y: 0 is top left):"))
  635. label.Wrap(400)
  636. self.spinX = wx.SpinCtrl(panel, id=wx.ID_ANY, min=0, max=100, initial=10)
  637. self.spinY = wx.SpinCtrl(panel, id=wx.ID_ANY, min=0, max=100, initial=10)
  638. self.spinX.Bind(wx.EVT_SPINCTRL, lambda evt, temp='X': self.OnPosition(evt, temp))
  639. self.spinY.Bind(wx.EVT_SPINCTRL, lambda evt, temp='Y': self.OnPosition(evt, temp))
  640. grid.Add(label, pos=(0, 0), span = (1, 4), flag = wx.EXPAND)
  641. grid.Add(wx.StaticText(panel, id=wx.ID_ANY, label=_("X:")), pos=(1, 0),
  642. flag = wx.ALIGN_CENTER_VERTICAL)
  643. grid.Add(wx.StaticText(panel, id=wx.ID_ANY, label=_("Y:")), pos=(1, 2),
  644. flag = wx.ALIGN_CENTER_VERTICAL)
  645. grid.Add(self.spinX, pos=(1, 1))
  646. grid.Add(self.spinY, pos=(1, 3))
  647. return grid
  648. def _createExportFormatPanel(self, notebook):
  649. panel = wx.Panel(notebook, id=wx.ID_ANY)
  650. borderSizer = wx.BoxSizer(wx.VERTICAL)
  651. hSizer = wx.BoxSizer(wx.HORIZONTAL)
  652. choices = [_("image sequence"), _("animated GIF"), _("SWF"), _("AVI")]
  653. self.formatChoice = wx.Choice(parent=panel, id=wx.ID_ANY,
  654. choices=choices)
  655. self.formatChoice.Bind(wx.EVT_CHOICE, lambda event: self.ChangeFormat(event.GetSelection()))
  656. hSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("Export to:")),
  657. proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=2)
  658. hSizer.Add(item=self.formatChoice, proportion=1,
  659. flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND | wx.ALL, border=2)
  660. borderSizer.Add(item=hSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=3)
  661. helpSizer = wx.BoxSizer(wx.HORIZONTAL)
  662. helpSizer.AddStretchSpacer(1)
  663. self.formatPanelSizer = wx.BoxSizer(wx.VERTICAL)
  664. helpSizer.Add(self.formatPanelSizer, proportion=5, flag=wx.EXPAND)
  665. borderSizer.Add(helpSizer, proportion=1, flag=wx.EXPAND)
  666. self.formatPanels = []
  667. # panel for image sequence
  668. imSeqPanel = wx.Panel(parent=panel, id=wx.ID_ANY)
  669. prefixLabel = wx.StaticText(imSeqPanel, id=wx.ID_ANY, label=_("File prefix:"))
  670. self.prefixCtrl = wx.TextCtrl(imSeqPanel, id=wx.ID_ANY, value=_("animation_"))
  671. formatLabel = wx.StaticText(imSeqPanel, id=wx.ID_ANY, label=_("File format:"))
  672. imageTypes = ['PNG', 'JPEG', 'GIF', 'TIFF', 'PPM', 'BMP']
  673. self.imSeqFormatChoice = wx.Choice(imSeqPanel, choices=imageTypes)
  674. self.imSeqFormatChoice.SetSelection(0)
  675. self.dirBrowse = filebrowse.DirBrowseButton(parent=imSeqPanel, id=wx.ID_ANY,
  676. labelText=_("Directory:"),
  677. dialogTitle=_("Choose directory for export"),
  678. buttonText=_("Browse"),
  679. startDirectory=os.getcwd())
  680. dirGridSizer = wx.GridBagSizer(hgap=5, vgap=5)
  681. dirGridSizer.AddGrowableCol(1)
  682. dirGridSizer.Add(prefixLabel, pos=(0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
  683. dirGridSizer.Add(self.prefixCtrl, pos=(0, 1), flag = wx.EXPAND)
  684. dirGridSizer.Add(formatLabel, pos=(1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
  685. dirGridSizer.Add(self.imSeqFormatChoice, pos=(1, 1), flag = wx.EXPAND)
  686. dirGridSizer.Add(self.dirBrowse, pos=(2, 0), flag = wx.EXPAND, span = (1, 2))
  687. imSeqPanel.SetSizer(dirGridSizer)
  688. dirGridSizer.Fit(imSeqPanel)
  689. self.formatPanelSizer.Add(item=imSeqPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  690. self.formatPanels.append(imSeqPanel)
  691. # panel for gif
  692. gifPanel = wx.Panel(parent=panel, id=wx.ID_ANY)
  693. self.gifBrowse = filebrowse.FileBrowseButton(parent=gifPanel, id=wx.ID_ANY,
  694. fileMask="GIF file (*.gif)|*.gif",
  695. labelText=_("GIF file:"),
  696. dialogTitle=_("Choose file to save animation"),
  697. buttonText=_("Browse"),
  698. startDirectory=os.getcwd(), fileMode=wx.SAVE)
  699. gifGridSizer = wx.GridBagSizer(hgap=5, vgap=5)
  700. gifGridSizer.AddGrowableCol(0)
  701. gifGridSizer.Add(self.gifBrowse, pos=(0, 0), flag = wx.EXPAND)
  702. gifPanel.SetSizer(gifGridSizer)
  703. gifGridSizer.Fit(gifPanel)
  704. self.formatPanelSizer.Add(item=gifPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  705. self.formatPanels.append(gifPanel)
  706. # panel for swf
  707. swfPanel = wx.Panel(parent=panel, id=wx.ID_ANY)
  708. self.swfBrowse = filebrowse.FileBrowseButton(parent=swfPanel, id=wx.ID_ANY,
  709. fileMask="SWF file (*.swf)|*.swf",
  710. labelText=_("SWF file:"),
  711. dialogTitle=_("Choose file to save animation"),
  712. buttonText=_("Browse"),
  713. startDirectory=os.getcwd(), fileMode=wx.SAVE)
  714. swfGridSizer = wx.GridBagSizer(hgap=5, vgap=5)
  715. swfGridSizer.AddGrowableCol(0)
  716. swfGridSizer.Add(self.swfBrowse, pos=(0, 0), flag = wx.EXPAND)
  717. swfPanel.SetSizer(swfGridSizer)
  718. swfGridSizer.Fit(swfPanel)
  719. self.formatPanelSizer.Add(item=swfPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  720. self.formatPanels.append(swfPanel)
  721. # panel for avi
  722. aviPanel = wx.Panel(parent=panel, id=wx.ID_ANY)
  723. ffmpeg = gcore.find_program('ffmpeg', '--help')
  724. if not ffmpeg:
  725. warning = _("Program 'ffmpeg' was not found.\nPlease install it first "
  726. "and make sure\nit's in the PATH variable.")
  727. warningLabel = wx.StaticText(parent=aviPanel, label=warning)
  728. warningLabel.SetForegroundColour(wx.RED)
  729. self.aviBrowse = filebrowse.FileBrowseButton(parent=aviPanel, id=wx.ID_ANY,
  730. fileMask="AVI file (*.avi)|*.avi",
  731. labelText=_("AVI file:"),
  732. dialogTitle=_("Choose file to save animation"),
  733. buttonText=_("Browse"),
  734. startDirectory=os.getcwd(), fileMode=wx.SAVE)
  735. encodingLabel = wx.StaticText(parent=aviPanel, id=wx.ID_ANY, label=_("Video codec:"))
  736. self.encodingText = wx.TextCtrl(parent=aviPanel, id=wx.ID_ANY, value='mpeg4')
  737. optionsLabel = wx.StaticText(parent=aviPanel, label=_("Additional options:"))
  738. self.optionsText = wx.TextCtrl(parent=aviPanel)
  739. self.optionsText.SetToolTipString(_("Consider adding '-sameq' or '-qscale 1' "
  740. "if not satisfied with video quality. "
  741. "Options depend on ffmpeg version."))
  742. aviGridSizer = wx.GridBagSizer(hgap=5, vgap=5)
  743. aviGridSizer.AddGrowableCol(1)
  744. aviGridSizer.Add(self.aviBrowse, pos=(0, 0), span = (1, 2), flag = wx.EXPAND)
  745. aviGridSizer.Add(encodingLabel, pos=(1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
  746. aviGridSizer.Add(self.encodingText, pos=(1, 1), flag = wx.EXPAND)
  747. aviGridSizer.Add(optionsLabel, pos=(2, 0), flag=wx.ALIGN_CENTER_VERTICAL)
  748. aviGridSizer.Add(self.optionsText, pos=(2, 1), flag=wx.EXPAND)
  749. if not ffmpeg:
  750. aviGridSizer.Add(warningLabel, pos=(3, 0), span=(1, 2),
  751. flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
  752. aviPanel.SetSizer(aviGridSizer)
  753. aviGridSizer.Fit(aviPanel)
  754. self.formatPanelSizer.Add(item=aviPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
  755. self.formatPanels.append(aviPanel)
  756. fpsSizer = wx.BoxSizer(wx.HORIZONTAL)
  757. fps = 1000 / self.timeTick
  758. fpsSizer.Add(wx.StaticText(panel, id=wx.ID_ANY, label=_("Current frame rate: %.2f fps") % fps),
  759. proportion=1, flag=wx.EXPAND)
  760. borderSizer.Add(fpsSizer, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
  761. panel.SetSizer(borderSizer)
  762. borderSizer.Fit(panel)
  763. self.ChangeFormat(index=0)
  764. return panel
  765. def ChangeFormat(self, index):
  766. for i, panel in enumerate(self.formatPanels):
  767. self.formatPanelSizer.Show(item=panel, show=(i == index))
  768. self.formatPanelSizer.Layout()
  769. def OnFont(self, event):
  770. index = self.listbox.GetSelection()
  771. # should not happen
  772. if index == wx.NOT_FOUND:
  773. return
  774. cdata = self.listbox.GetClientData(index)
  775. font = cdata['font']
  776. fontdata = wx.FontData()
  777. fontdata.EnableEffects(True)
  778. fontdata.SetColour('black')
  779. fontdata.SetInitialFont(font)
  780. dlg = wx.FontDialog(self, fontdata)
  781. dlg.CenterOnParent()
  782. if dlg.ShowModal() == wx.ID_OK:
  783. newfontdata = dlg.GetFontData()
  784. font = newfontdata.GetChosenFont()
  785. self.sampleLabel.SetFont(font)
  786. cdata['font'] = font
  787. self.Layout()
  788. def OnPosition(self, event, coord):
  789. index = self.listbox.GetSelection()
  790. # should not happen
  791. if index == wx.NOT_FOUND:
  792. return
  793. cdata = self.listbox.GetClientData(index)
  794. cdata['pos'][coord == 'Y'] = event.GetInt()
  795. def OnSetImage(self, event):
  796. index = self.listbox.GetSelection()
  797. # should not happen
  798. if index == wx.NOT_FOUND:
  799. return
  800. cdata = self.listbox.GetClientData(index)
  801. cdata['file'] = event.GetString()
  802. def OnAddDecoration(self, event, name):
  803. if name == 'time':
  804. timeInfo = {'name': name, 'font': self.GetFont(), 'pos': [10, 10]}
  805. self.decorations.append(timeInfo)
  806. elif name == 'image':
  807. imageInfo = {'name': name, 'file': '', 'pos': [10, 10]}
  808. self.decorations.append(imageInfo)
  809. elif name == 'text':
  810. textInfo = {'name': name, 'font': self.GetFont(), 'text': '', 'pos': [10, 10]}
  811. self.decorations.append(textInfo)
  812. self._updateListBox()
  813. self.listbox.SetSelection(self.listbox.GetCount() - 1)
  814. self.OnSelectionChanged(event=None)
  815. def OnSelectionChanged(self, event):
  816. index = self.listbox.GetSelection()
  817. if index == wx.NOT_FOUND:
  818. self._hideAll()
  819. return
  820. cdata = self.listbox.GetClientData(index)
  821. self.hidevbox.Show(self.fontBox, (cdata['name'] in ('time', 'text')))
  822. self.hidevbox.Show(self.imageBox, (cdata['name'] == 'image'))
  823. self.hidevbox.Show(self.textBox, (cdata['name'] == 'text'))
  824. self.hidevbox.Show(self.posBox, True)
  825. self.hidevbox.Show(self.informBox, False)
  826. self.spinX.SetValue(cdata['pos'][0])
  827. self.spinY.SetValue(cdata['pos'][1])
  828. if cdata['name'] == 'image':
  829. self.browse.SetValue(cdata['file'])
  830. elif cdata['name'] in ('time', 'text'):
  831. self.sampleLabel.SetFont(cdata['font'])
  832. if cdata['name'] == 'text':
  833. self.textCtrl.SetValue(cdata['text'])
  834. self.hidevbox.Layout()
  835. # self.Layout()
  836. def OnText(self, event):
  837. index = self.listbox.GetSelection()
  838. # should not happen
  839. if index == wx.NOT_FOUND:
  840. return
  841. cdata = self.listbox.GetClientData(index)
  842. cdata['text'] = event.GetString()
  843. def OnRemove(self, event):
  844. index = self.listbox.GetSelection()
  845. if index == wx.NOT_FOUND:
  846. return
  847. decData = self.listbox.GetClientData(index)
  848. self.decorations.remove(decData)
  849. self._updateListBox()
  850. if self.listbox.GetCount():
  851. self.listbox.SetSelection(0)
  852. self.OnSelectionChanged(event=None)
  853. def OnExport(self, event):
  854. for decor in self.decorations:
  855. if decor['name'] == 'image':
  856. if not os.path.exists(decor['file']):
  857. if decor['file']:
  858. GError(parent=self, message=_("File %s not found.") % decor['file'])
  859. else:
  860. GError(parent=self, message=_("Decoration image file is missing."))
  861. return
  862. if self.formatChoice.GetSelection() == 0:
  863. name = self.dirBrowse.GetValue()
  864. if not os.path.exists(name):
  865. if name:
  866. GError(parent=self, message=_("Directory %s not found.") % name)
  867. else:
  868. GError(parent=self, message=_("Export directory is missing."))
  869. return
  870. elif self.formatChoice.GetSelection() == 1:
  871. if not self.gifBrowse.GetValue():
  872. GError(parent=self, message=_("Export file is missing."))
  873. return
  874. elif self.formatChoice.GetSelection() == 2:
  875. if not self.swfBrowse.GetValue():
  876. GError(parent=self, message=_("Export file is missing."))
  877. return
  878. # hide only to keep previous values
  879. self.Hide()
  880. self.doExport.emit(exportInfo=self.GetExportInformation(),
  881. decorations=self.GetDecorations())
  882. def GetDecorations(self):
  883. return self.decorations
  884. def GetExportInformation(self):
  885. info = {}
  886. if self.formatChoice.GetSelection() == 0:
  887. info['method'] = 'sequence'
  888. info['directory'] = self.dirBrowse.GetValue()
  889. info['prefix'] = self.prefixCtrl.GetValue()
  890. info['format'] = self.imSeqFormatChoice.GetStringSelection()
  891. elif self.formatChoice.GetSelection() == 1:
  892. info['method'] = 'gif'
  893. info['file'] = self.gifBrowse.GetValue()
  894. elif self.formatChoice.GetSelection() == 2:
  895. info['method'] = 'swf'
  896. info['file'] = self.swfBrowse.GetValue()
  897. elif self.formatChoice.GetSelection() == 3:
  898. info['method'] = 'avi'
  899. info['file'] = self.aviBrowse.GetValue()
  900. info['encoding'] = self.encodingText.GetValue()
  901. info['options'] = self.optionsText.GetValue()
  902. return info
  903. def _updateListBox(self):
  904. self.listbox.Clear()
  905. names = {'time': _("Time stamp"), 'image': _("Image"), 'text': _("Text")}
  906. for decor in self.decorations:
  907. self.listbox.Append(names[decor['name']], clientData=decor)
  908. def _hideAll(self):
  909. self.hidevbox.Show(self.fontBox, False)
  910. self.hidevbox.Show(self.imageBox, False)
  911. self.hidevbox.Show(self.textBox, False)
  912. self.hidevbox.Show(self.posBox, False)
  913. self.hidevbox.Show(self.informBox, True)
  914. self.hidevbox.Layout()
  915. class AnimSimpleLayerManager(SimpleLayerManager):
  916. """!Simple layer manager for animation tool.
  917. Allows to add space-time dataset or series of maps.
  918. """
  919. def __init__(self, parent, layerList,
  920. lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_VECTOR |
  921. SIMPLE_LMGR_TB_TOP | SIMPLE_LMGR_STDS,
  922. toolbarCls=AnimSimpleLmgrToolbar, modal=True):
  923. SimpleLayerManager.__init__(self, parent, layerList, lmgrStyle, toolbarCls, modal)
  924. self._3dActivated = False
  925. def OnAddStds(self, event):
  926. """!Opens dialog for specifying temporal dataset.
  927. Dummy layer is added first."""
  928. layer = AnimLayer()
  929. layer.hidden = True
  930. self._layerList.AddLayer(layer)
  931. self.SetStdsProperties(layer)
  932. event.Skip()
  933. def SetStdsProperties(self, layer):
  934. dlg = AddTemporalLayerDialog(parent=self, layer=layer, volume=self._3dActivated)
  935. # first get hidden property, it's altered afterwards
  936. hidden = layer.hidden
  937. dlg.CenterOnParent()
  938. if dlg.ShowModal() == wx.ID_OK:
  939. layer = dlg.GetLayer()
  940. if hidden:
  941. signal = self.layerAdded
  942. else:
  943. signal = self.cmdChanged
  944. signal.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
  945. else:
  946. if hidden:
  947. self._layerList.RemoveLayer(layer)
  948. dlg.Destroy()
  949. self._update()
  950. self.anyChange.emit()
  951. def _layerChangeProperties(self, layer):
  952. """!Opens new module dialog or recycles it."""
  953. if not hasattr(layer, 'maps'):
  954. GUI(parent=self, giface=None,
  955. modal=self._modal).ParseCommand(cmd=layer.cmd,
  956. completed=(self.GetOptData, layer, ''))
  957. else:
  958. self.SetStdsProperties(layer)
  959. def Activate3D(self, activate=True):
  960. """!Activates/deactivates certain tool depending on 2D/3D view."""
  961. self._toolbar.EnableTools(['addRaster', 'addVector',
  962. 'opacity', 'up', 'down'], not activate)
  963. self._3dActivated = activate
  964. class AddTemporalLayerDialog(wx.Dialog):
  965. """!Dialog for adding space-time dataset/ map series."""
  966. def __init__(self, parent, layer, volume=False,
  967. title=_("Add space-time dataset layer")):
  968. wx.Dialog.__init__(self, parent=parent, title=title)
  969. self.layer = layer
  970. self._mapType = None
  971. self._name = None
  972. self._cmd = None
  973. self.tselect = Select(parent=self, type='strds')
  974. iconTheme = UserSettings.Get(group='appearance', key='iconTheme', subkey='type')
  975. bitmapPath = os.path.join(globalvar.ETCICONDIR, iconTheme, 'layer-open.png')
  976. if os.path.isfile(bitmapPath) and os.path.getsize(bitmapPath):
  977. bitmap = wx.Bitmap(name=bitmapPath)
  978. else:
  979. bitmap = wx.ArtProvider.GetBitmap(id=wx.ART_MISSING_IMAGE, client=wx.ART_TOOLBAR)
  980. self.addManyMapsButton = wx.BitmapButton(self, bitmap=bitmap)
  981. self.addManyMapsButton.Bind(wx.EVT_BUTTON, self._onAddMaps)
  982. types = [('rast', _("Multiple raster maps")),
  983. ('vect', _("Multiple vector maps")),
  984. ('rast3d', _("Multiple 3D raster maps")),
  985. ('strds', _("Space time raster dataset")),
  986. ('stvds', _("Space time vector dataset")),
  987. ('str3ds', _("Space time 3D raster dataset"))]
  988. if not volume:
  989. del types[5]
  990. del types[2]
  991. self._types = dict(types)
  992. self.tchoice = wx.Choice(parent=self)
  993. for type_, text in types:
  994. self.tchoice.Append(text, clientData=type_)
  995. self.editBtn = wx.Button(parent=self, label='Set properties')
  996. self.okBtn = wx.Button(parent=self, id=wx.ID_OK)
  997. self.cancelBtn = wx.Button(parent=self, id=wx.ID_CANCEL)
  998. self.okBtn.Bind(wx.EVT_BUTTON, self._onOK)
  999. self.editBtn.Bind(wx.EVT_BUTTON, self._onProperties)
  1000. self.tchoice.Bind(wx.EVT_CHOICE,
  1001. lambda evt: self._setType())
  1002. self.tselect.Bind(wx.EVT_TEXT,
  1003. lambda evt: self._datasetChanged())
  1004. if self.layer.mapType:
  1005. self._setType(self.layer.mapType)
  1006. else:
  1007. self._setType('rast')
  1008. if self.layer.name:
  1009. self.tselect.SetValue(self.layer.name)
  1010. if self.layer.cmd:
  1011. self._cmd = self.layer.cmd
  1012. self._layout()
  1013. self.SetSize(self.GetBestSize())
  1014. def _layout(self):
  1015. mainSizer = wx.BoxSizer(wx.VERTICAL)
  1016. bodySizer = wx.BoxSizer(wx.VERTICAL)
  1017. typeSizer = wx.BoxSizer(wx.HORIZONTAL)
  1018. selectSizer = wx.BoxSizer(wx.HORIZONTAL)
  1019. typeSizer.Add(wx.StaticText(self, label=_("Input data type:")),
  1020. flag=wx.ALIGN_CENTER_VERTICAL)
  1021. typeSizer.AddStretchSpacer()
  1022. typeSizer.Add(self.tchoice)
  1023. bodySizer.Add(typeSizer, flag=wx.EXPAND | wx.BOTTOM, border=5)
  1024. selectSizer.Add(self.tselect, flag=wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, border=5)
  1025. selectSizer.Add(self.addManyMapsButton, flag=wx.EXPAND)
  1026. bodySizer.Add(selectSizer, flag=wx.BOTTOM, border=5)
  1027. bodySizer.Add(self.editBtn, flag=wx.BOTTOM, border=5)
  1028. mainSizer.Add(bodySizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
  1029. btnSizer = wx.StdDialogButtonSizer()
  1030. btnSizer.AddButton(self.okBtn)
  1031. btnSizer.AddButton(self.cancelBtn)
  1032. btnSizer.Realize()
  1033. mainSizer.Add(btnSizer, proportion=0,
  1034. flag=wx.EXPAND | wx.ALL, border=10)
  1035. self.SetSizer(mainSizer)
  1036. mainSizer.Fit(self)
  1037. def _datasetChanged(self):
  1038. if self._name != self.tselect.GetValue():
  1039. self._name = self.tselect.GetValue()
  1040. self._cmd = None
  1041. def _setType(self, typeName=None):
  1042. if typeName:
  1043. self.tchoice.SetStringSelection(self._types[typeName])
  1044. self.tselect.SetType(typeName)
  1045. if typeName in ('strds', 'stvds', 'str3ds'):
  1046. self.tselect.SetType(typeName, multiple=False)
  1047. self.addManyMapsButton.Disable()
  1048. else:
  1049. self.tselect.SetType(typeName, multiple=True)
  1050. self.addManyMapsButton.Enable()
  1051. self._mapType = typeName
  1052. self.tselect.SetValue('')
  1053. else:
  1054. typeName = self.tchoice.GetClientData(self.tchoice.GetSelection())
  1055. if typeName in ('strds', 'stvds', 'str3ds'):
  1056. self.tselect.SetType(typeName, multiple=False)
  1057. self.addManyMapsButton.Disable()
  1058. else:
  1059. self.tselect.SetType(typeName, multiple=True)
  1060. self.addManyMapsButton.Enable()
  1061. if typeName != self._mapType:
  1062. self._cmd = None
  1063. self._mapType = typeName
  1064. self.tselect.SetValue('')
  1065. def _createDefaultCommand(self):
  1066. cmd = []
  1067. if self._mapType in ('rast', 'strds'):
  1068. cmd.append('d.rast')
  1069. elif self._mapType in ('vect', 'stvds'):
  1070. cmd.append('d.vect')
  1071. elif self._mapType in ('rast3d', 'str3ds'):
  1072. cmd.append('d.rast3d')
  1073. if self._name:
  1074. if self._mapType in ('rast', 'vect', 'rast3d'):
  1075. cmd.append('map={}'.format(self._name.split(',')[0]))
  1076. else:
  1077. try:
  1078. maps = getRegisteredMaps(self._name, etype=self._mapType)
  1079. if maps:
  1080. cmd.append('map={}'.format(maps[0]))
  1081. except gcore.ScriptError, e:
  1082. GError(parent=self, message=str(e), showTraceback=False)
  1083. return None
  1084. return cmd
  1085. def _onAddMaps(self, event):
  1086. dlg = MapLayersDialog(self, title=_("Select raster/vector maps."))
  1087. dlg.applyAddingMapLayers.connect(lambda mapLayers:
  1088. self.tselect.SetValue(','.join(mapLayers)))
  1089. if self._mapType == 'rast':
  1090. index = 0
  1091. elif self._mapType == 'vect':
  1092. index = 2
  1093. else: # rast3d
  1094. index = 1
  1095. dlg.layerType.SetSelection(index)
  1096. dlg.LoadMapLayers(dlg.GetLayerType(cmd=True),
  1097. dlg.mapset.GetStringSelection())
  1098. dlg.CenterOnParent()
  1099. if dlg.ShowModal() == wx.ID_OK:
  1100. self.tselect.SetValue(','.join(dlg.GetMapLayers()))
  1101. dlg.Destroy()
  1102. def _onProperties(self, event):
  1103. self._checkInput()
  1104. if self._cmd:
  1105. GUI(parent=self, show=True, modal=True).ParseCommand(cmd=self._cmd,
  1106. completed=(self._getOptData, '', ''))
  1107. def _checkInput(self):
  1108. if not self.tselect.GetValue():
  1109. GMessage(parent=self, message=_("Please select maps or dataset first."))
  1110. return
  1111. if not self._cmd:
  1112. self._cmd = self._createDefaultCommand()
  1113. def _getOptData(self, dcmd, layer, params, propwin):
  1114. if dcmd:
  1115. self._cmd = dcmd
  1116. def _onOK(self, event):
  1117. self._checkInput()
  1118. if self._cmd:
  1119. try:
  1120. self.layer.hidden = False
  1121. self.layer.mapType = self._mapType
  1122. self.layer.name = self._name
  1123. self.layer.cmd = self._cmd
  1124. event.Skip()
  1125. except (GException, gcore.ScriptError), e:
  1126. GError(parent=self, message=str(e))
  1127. def GetLayer(self):
  1128. return self.layer
  1129. class PreferencesDialog(PreferencesBaseDialog):
  1130. """!Animation preferences dialog"""
  1131. def __init__(self, parent, giface, title=_("Animation Tool settings"),
  1132. settings=UserSettings):
  1133. PreferencesBaseDialog.__init__(self, parent=parent, giface=giface, title=title,
  1134. settings=settings, size=(-1, 270))
  1135. self._timeFormats = ['%Y-%m-%d %H:%M:%S', # 2013-12-29 11:16:26
  1136. '%Y-%m-%d', # 2013-12-29
  1137. '%c', # Sun Dec 29 11:16:26 2013 (locale-dependent)
  1138. '%x', # 12/29/13 (locale-dependent)
  1139. '%X', # 11:16:26 (locale-dependent)
  1140. '%b %d, %Y', # Dec 29, 2013
  1141. '%B %d, %Y', # December 29, 2013
  1142. '%B, %Y', # December 2013
  1143. '%I:%M %p', # 11:16 AM
  1144. '%I %p', # 11 AM
  1145. ]
  1146. self._format = None
  1147. # create notebook pages
  1148. self._createGeneralPage(self.notebook)
  1149. self._createTemporalPage(self.notebook)
  1150. self.SetMinSize(self.GetBestSize())
  1151. self.SetSize(self.size)
  1152. def _createGeneralPage(self, notebook):
  1153. """!Create notebook page for general settings"""
  1154. panel = SP.ScrolledPanel(parent=notebook)
  1155. panel.SetupScrolling(scroll_x=False, scroll_y=True)
  1156. notebook.AddPage(page=panel, text=_("General"))
  1157. border = wx.BoxSizer(wx.VERTICAL)
  1158. sizer = wx.BoxSizer(wx.VERTICAL)
  1159. gridSizer = wx.GridBagSizer(hgap=3, vgap=3)
  1160. row = 0
  1161. gridSizer.Add(item=wx.StaticText(parent=panel,
  1162. label=_("Background color:")),
  1163. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
  1164. color = csel.ColourSelect(parent=panel,
  1165. colour=UserSettings.Get(group='animation',
  1166. key='bgcolor', subkey='color'),
  1167. size=globalvar.DIALOG_COLOR_SIZE)
  1168. color.SetName('GetColour')
  1169. self.winId['animation:bgcolor:color'] = color.GetId()
  1170. gridSizer.Add(item=color, pos=(row, 1), flag=wx.ALIGN_RIGHT)
  1171. gridSizer.AddGrowableCol(1)
  1172. sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=3)
  1173. border.Add(item=sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
  1174. panel.SetSizer(border)
  1175. return panel
  1176. def _createTemporalPage(self, notebook):
  1177. """!Create notebook page for temporal settings"""
  1178. panel = SP.ScrolledPanel(parent=notebook)
  1179. panel.SetupScrolling(scroll_x=False, scroll_y=True)
  1180. notebook.AddPage(page=panel, text=_("Time"))
  1181. border = wx.BoxSizer(wx.VERTICAL)
  1182. sizer = wx.BoxSizer(wx.VERTICAL)
  1183. gridSizer = wx.GridBagSizer(hgap=5, vgap=5)
  1184. row = 0
  1185. gridSizer.Add(item=wx.StaticText(parent=panel,
  1186. label=_("Absolute time format:")),
  1187. flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
  1188. self.tempFormat = wx.ComboBox(parent=panel, name='GetValue')
  1189. self.tempFormat.SetItems(self._timeFormats)
  1190. self.tempFormat.SetValue(self.settings.Get(group='animation', key='temporal',
  1191. subkey='format'))
  1192. self.winId['animation:temporal:format'] = self.tempFormat.GetId()
  1193. gridSizer.Add(item=self.tempFormat, pos=(row, 1), flag=wx.ALIGN_RIGHT)
  1194. self.infoTimeLabel = wx.StaticText(parent=panel)
  1195. self.tempFormat.Bind(wx.EVT_COMBOBOX, lambda evt: self._setTimeFormat(self.tempFormat.GetValue()))
  1196. self.tempFormat.Bind(wx.EVT_TEXT, lambda evt: self._setTimeFormat(self.tempFormat.GetValue()))
  1197. self.tempFormat.SetToolTipString(_("Click and then press key up or down to preview "
  1198. "different date and time formats. "
  1199. "Type custom format string."))
  1200. row += 1
  1201. gridSizer.Add(item=self.infoTimeLabel, pos=(row, 0), span=(1, 2),
  1202. flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
  1203. self._setTimeFormat(self.tempFormat.GetValue())
  1204. row += 1
  1205. link = wx.HyperlinkCtrl(panel, id=wx.ID_ANY, label=_("Learn more about formatting options"),
  1206. url="http://docs.python.org/2/library/datetime.html#"
  1207. "strftime-and-strptime-behavior")
  1208. link.SetNormalColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_GRAYTEXT))
  1209. link.SetVisitedColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_GRAYTEXT))
  1210. gridSizer.Add(item=link, pos=(row, 0), span=(1, 2),
  1211. flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
  1212. row += 2
  1213. noDataCheck = wx.CheckBox(panel, label=_("Display instances with no data"))
  1214. noDataCheck.SetToolTipString(_("When animating instant-based data which have irregular timestamps "
  1215. "you can display 'no data frame' (checked option) or "
  1216. "keep last frame."))
  1217. noDataCheck.SetValue(self.settings.Get(group='animation', key='temporal',
  1218. subkey=['nodata', 'enable']))
  1219. self.winId['animation:temporal:nodata:enable'] = noDataCheck.GetId()
  1220. gridSizer.Add(item=noDataCheck, pos=(row, 0), span=(1, 2),
  1221. flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
  1222. gridSizer.AddGrowableCol(1)
  1223. sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=3)
  1224. border.Add(item=sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
  1225. panel.SetSizer(border)
  1226. return panel
  1227. def _setTimeFormat(self, formatString):
  1228. now = datetime.datetime.now()
  1229. try:
  1230. label = datetime.datetime.strftime(now, formatString)
  1231. self._format = formatString
  1232. except ValueError:
  1233. label = _("Invalid")
  1234. self.infoTimeLabel.SetLabel(label)
  1235. self.infoTimeLabel.GetContainingSizer().Layout()
  1236. def _updateSettings(self):
  1237. self.tempFormat.SetValue(self._format)
  1238. return PreferencesBaseDialog._updateSettings(self)
  1239. def test():
  1240. import wx.lib.inspection
  1241. app = wx.PySimpleApp()
  1242. # testTemporalLayer()
  1243. # testAnimLmgr()
  1244. testAnimInput()
  1245. # wx.lib.inspection.InspectionTool().Show()
  1246. app.MainLoop()
  1247. def testAnimInput():
  1248. anim = AnimationData()
  1249. anim.SetDefaultValues(animationIndex=0, windowIndex=0)
  1250. dlg = InputDialog(parent=None, mode='add', animationData=anim)
  1251. dlg.Show()
  1252. def testAnimEdit():
  1253. anim = AnimationData()
  1254. anim.SetDefaultValues(animationIndex=0, windowIndex=0)
  1255. dlg = EditDialog(parent=None, animationData=[anim])
  1256. dlg.Show()
  1257. def testExport():
  1258. dlg = ExportDialog(parent=None, temporal=TemporalMode.TEMPORAL,
  1259. timeTick=200)
  1260. if dlg.ShowModal() == wx.ID_OK:
  1261. print dlg.GetDecorations()
  1262. print dlg.GetExportInformation()
  1263. dlg.Destroy()
  1264. else:
  1265. dlg.Destroy()
  1266. def testTemporalLayer():
  1267. frame = wx.Frame(None)
  1268. frame.Show()
  1269. layer = AnimLayer()
  1270. dlg = AddTemporalLayerDialog(parent=frame, layer=layer)
  1271. if dlg.ShowModal() == wx.ID_OK:
  1272. layer = dlg.GetLayer()
  1273. print layer.name, layer.cmd, layer.mapType
  1274. dlg.Destroy()
  1275. else:
  1276. dlg.Destroy()
  1277. def testAnimLmgr():
  1278. from core.layerlist import LayerList
  1279. frame = wx.Frame(None)
  1280. mgr = AnimSimpleLayerManager(parent=frame, layerList=LayerList())
  1281. frame.mgr = mgr
  1282. frame.Show()
  1283. if __name__ == '__main__':
  1284. gcore.set_raise_on_error(True)
  1285. test()