dialogs.py 75 KB

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