dialogs.py 69 KB

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