dialogs.py 75 KB

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