dialogs.py 75 KB

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