dialogs.py 69 KB

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