dialogs.py 69 KB

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