dialogs.py 75 KB

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