ObjectPropertyUI.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. """
  2. UI for object property control
  3. """
  4. import wx
  5. import os
  6. import math
  7. from wx.lib.embeddedimage import PyEmbeddedImage
  8. from wx.lib.scrolledpanel import ScrolledPanel
  9. from wx.lib.agw.cubecolourdialog import CCD_SHOW_ALPHA, CubeColourDialog
  10. from direct.wxwidgets.WxSlider import WxSlider
  11. from panda3d.core import BitMask32, Filename
  12. from . import ObjectGlobals as OG
  13. from . import AnimGlobals as AG
  14. #----------------------------------------------------------------------
  15. Key = PyEmbeddedImage(
  16. "iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAIAAACQKrqGAAAAA3NCSVQICAjb4U/gAAABIElE"
  17. "QVQokZWSMW7CQBBFZ2Z3sQ02Ni4sOS6QiLgO5yBXIMcJ1KENje8QLESH7F3FVFQIIS3eTWGJ"
  18. "VE7Iq6Z4+tL8GVRSwmPQg94fKiIOBoNer2et/U1FRER8X6+LonBdFwB4l+p53mq1qqRUUsZx"
  19. "nKYpBwDOuRACEQGgaRoAYETn8/l4PL4uFkqp/X6fZRlnjO12u7KqENEa43keADDGvuo6Go0A"
  20. "wPd9YkxrzY0x4/FYKlXX9eVymc1mjIiIgiD43G4BwFprmgYRubU2DMPnySTw/ev1+pSmRISI"
  21. "SZJ8bDan06ksSyLiQmDXCfr9fp7nb8vldDp9mc9d1/1R27XaClscxzkcDlEUhcOhvt06U1uE"
  22. "EMaYtpbOXlu01vf5Hz/wDRuDdIDl5WtQAAAAAElFTkSuQmCC")
  23. #----------------------------------------------------------------------
  24. class AnimFileDrop(wx.FileDropTarget):
  25. def __init__(self, editor):
  26. wx.FileDropTarget.__init__(self)
  27. self.editor = editor
  28. def OnDropFiles(self, x, y, filenames):
  29. obj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
  30. if obj is None:
  31. return
  32. objDef = obj[OG.OBJ_DEF]
  33. if not objDef.actor:
  34. return
  35. objNP = obj[OG.OBJ_NP]
  36. for filename in filenames:
  37. name = os.path.basename(filename)
  38. animName = Filename.fromOsSpecific(filename).getFullpath()
  39. if name.endswith('.mb') or\
  40. name.endswith('.ma'):
  41. self.editor.convertMaya(animName, self.editor.ui.protoPaletteUI.addNewItem, obj, isAnim=True)
  42. return
  43. if animName not in objDef.anims:
  44. objDef.anims.append(animName)
  45. objNP.loadAnims({name:animName})
  46. objNP.loop(name)
  47. obj[OG.OBJ_ANIM] = animName
  48. self.editor.ui.objectPropertyUI.updateProps(obj)
  49. class ObjectPropUI(wx.Panel):
  50. """
  51. Base class for ObjectPropUIs,
  52. It consists of label area and ui area.
  53. """
  54. def __init__(self, parent, label):
  55. wx.Panel.__init__(self, parent)
  56. self.parent = parent
  57. self.labelPane = wx.Panel(self)
  58. self.label = wx.StaticText(self.labelPane, label=label)
  59. self.labelSizer = wx.BoxSizer(wx.HORIZONTAL)
  60. self.labelSizer.Add(self.label)
  61. bmpKey = Key.GetBitmap()
  62. self.setKeyButton = wx.BitmapButton(self.labelPane, -1, bmpKey, size = (15,15),style = wx.BU_AUTODRAW)
  63. self.labelSizer.Add(self.setKeyButton)
  64. self.labelPane.SetSizer(self.labelSizer)
  65. self.uiPane = wx.Panel(self)
  66. sizer = wx.BoxSizer(wx.VERTICAL)
  67. sizer.Add(self.labelPane)
  68. sizer.Add(self.uiPane, 1, wx.EXPAND, 0)
  69. self.SetSizer(sizer)
  70. self.setKeyButton.Bind(wx.EVT_BUTTON, self.onKey)
  71. def onKey(self, evt):
  72. self.parent = wx.GetTopLevelParent(self)
  73. if self.parent.editor.mode == self.parent.editor.ANIM_MODE:
  74. obj = self.parent.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
  75. value = self.getValue()
  76. frame = self.parent.editor.ui.animUI.curFrame
  77. if property in self.parent.editor.animMgr.keyFramesInfo:
  78. for i in range(len(self.parent.editor.animMgr.keyFramesInfo[property])):
  79. if self.parent.editor.animMgr.keyFramesInfo[property][i][AG.FRAME] == frame:
  80. del self.parent.editor.animMgr.keyFramesInfo[property][i]
  81. self.parent.editor.animMgr.keyFramesInfo[property].append([frame, value, [], []])
  82. #sort keyFrameInfo list by the order of frame number
  83. sortKeyList = self.parent.editor.animMgr.keyFramesInfo[property]
  84. for i in range(0, len(sortKeyList)-1):
  85. for j in range(i+1, len(sortKeyList)):
  86. if sortKeyList[i][AG.FRAME]>sortKeyList[j][AG.FRAME]:
  87. temp = sortKeyList[i]
  88. sortKeyList[i] = sortKeyList[j]
  89. sortKeyList[j] = temp
  90. self.parent.editor.animMgr.generateSlope(self.parent.editor.animMgr.keyFramesInfo[property])
  91. else:
  92. self.parent.editor.animMgr.keyFramesInfo[property] = [[frame, value, [], []]]
  93. exist = False
  94. for keyFrame in self.parent.editor.animMgr.keyFrames:
  95. if frame == keyFrame:
  96. exist = True
  97. break
  98. if not exist:
  99. self.parent.editor.animMgr.keyFrames.append(frame)
  100. self.parent.editor.ui.animUI.OnPropKey()
  101. else:
  102. evt.Skip()
  103. def setValue(self, value):
  104. self.ui.SetValue(value)
  105. def getValue(self):
  106. return self.ui.GetValue()
  107. def bindFunc(self, inFunc, outFunc, valFunc = None):
  108. self.ui.Bind(wx.EVT_ENTER_WINDOW, inFunc)
  109. self.ui.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
  110. if valFunc:
  111. self.ui.Bind(self.eventType, valFunc)
  112. class ObjectPropUIEntry(ObjectPropUI):
  113. """ UI for string value properties """
  114. def __init__(self, parent, label):
  115. ObjectPropUI.__init__(self, parent, label)
  116. self.ui = wx.TextCtrl(self.uiPane, -1)
  117. self.eventType = wx.EVT_TEXT_ENTER
  118. self.Layout()
  119. def setValue(self, value):
  120. self.ui.SetValue(str(value))
  121. class ObjectPropUISlider(ObjectPropUI):
  122. """ UI for float value properties """
  123. def __init__(self, parent, label, value, minValue, maxValue):
  124. ObjectPropUI.__init__(self, parent, label)
  125. self.ui = WxSlider(self.uiPane, -1, value, minValue, maxValue,
  126. pos = (0,0), size=(140, -1),
  127. style=wx.SL_HORIZONTAL | wx.SL_LABELS)
  128. self.ui.Enable()
  129. self.Layout()
  130. def bindFunc(self, inFunc, outFunc, valFunc = None):
  131. self.ui.Bind(wx.EVT_ENTER_WINDOW, inFunc)
  132. self.ui.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
  133. self.ui.textValue.Bind(wx.EVT_ENTER_WINDOW, inFunc)
  134. self.ui.textValue.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
  135. if valFunc:
  136. self.ui.bindFunc(valFunc)
  137. class ObjectPropUISpinner(ObjectPropUI):
  138. """ UI for int value properties """
  139. def __init__(self, parent, label, value, minValue, maxValue):
  140. ObjectPropUI.__init__(self, parent, label)
  141. self.ui = wx.SpinCtrl(self.uiPane, -1, "", min=minValue, max=maxValue, initial=value)
  142. self.eventType = wx.EVT_SPIN
  143. self.Layout()
  144. class ObjectPropUICheck(ObjectPropUI):
  145. def __init__(self, parent, label, value):
  146. ObjectPropUI.__init__(self, parent, label)
  147. self.ui = wx.CheckBox(self.uiPane, -1, "", size=(50, 30))
  148. self.setValue(value)
  149. self.eventType = wx.EVT_CHECKBOX
  150. self.Layout()
  151. class ObjectPropUIRadio(ObjectPropUI):
  152. def __init__(self, parent, label, value, valueList):
  153. ObjectPropUI.__init__(self, parent, label)
  154. self.ui = wx.RadioBox(self.uiPane, -1, "", choices=valueList, majorDimension=1, style=wx.RA_SPECIFY_COLS)
  155. self.setValue(value)
  156. self.eventType = wx.EVT_RADIOBOX
  157. self.Layout()
  158. def setValue(self, value):
  159. self.ui.SetStringSelection(value)
  160. def getValue(self):
  161. return self.ui.GetStringSelection()
  162. class ObjectPropUICombo(ObjectPropUI):
  163. def __init__(self, parent, label, value, valueList, obj=None, callBack=None):
  164. ObjectPropUI.__init__(self, parent, label)
  165. self.ui = wx.Choice(self.uiPane, -1, choices=valueList)
  166. if callBack is not None:
  167. button = wx.Button(self.labelPane, -1, 'Update', size = (100, 18))
  168. button.Bind(wx.EVT_BUTTON, lambda p0=None, p1=obj, p2=self: callBack(p0, p1, p2))
  169. self.labelSizer.Add(button)
  170. self.setValue(value)
  171. self.eventType = wx.EVT_CHOICE
  172. self.Layout()
  173. def setValue(self, value):
  174. self.ui.SetStringSelection(value)
  175. def getValue(self):
  176. return self.ui.GetStringSelection()
  177. def setItems(self, valueList):
  178. self.ui.SetItems(valueList)
  179. class ObjectPropUITime(wx.Panel):
  180. def __init__(self, parent, label, value):
  181. wx.Panel.__init__(self, parent)
  182. self.parent = parent
  183. self.labelPane = wx.Panel(self)
  184. self.label = wx.StaticText(self.labelPane, label=label)
  185. self.labelSizer = wx.BoxSizer(wx.HORIZONTAL)
  186. self.labelSizer.Add(self.label)
  187. self.labelPane.SetSizer(self.labelSizer)
  188. self.uiPane = wx.Panel(self)
  189. sizer = wx.BoxSizer(wx.VERTICAL)
  190. sizer.Add(self.labelPane)
  191. sizer.Add(self.uiPane, 1, wx.EXPAND, 0)
  192. self.SetSizer(sizer)
  193. hSizer = wx.BoxSizer(wx.HORIZONTAL)
  194. self.uiAmPm = wx.Choice(self.uiPane, -1, choices=['AM', 'PM'])
  195. self.uiHour = wx.Choice(self.uiPane, -1, choices=[str(x) for x in range(1, 13)])
  196. self.uiMin = wx.Choice(self.uiPane, -1, choices=[str(x) for x in range(0, 60, 15)])
  197. hSizer.Add(self.uiAmPm)
  198. hSizer.Add(self.uiHour)
  199. hSizer.Add(self.uiMin)
  200. self.uiPane.SetSizer(hSizer)
  201. self.setValue(value)
  202. self.eventType = wx.EVT_CHOICE
  203. self.Layout()
  204. def setValue(self, value):
  205. hourVal = int(math.floor(value))
  206. minVal = [0, 15, 30, 45][int((value - hourVal) * 4)]
  207. if hourVal > 11:
  208. ampmStr = 'PM'
  209. hourVal = hourVal - 12
  210. else:
  211. ampmStr = 'AM'
  212. if hourVal == 0:
  213. hourVal = 12
  214. self.uiAmPm.SetStringSelection(ampmStr)
  215. self.uiHour.SetStringSelection(str(hourVal))
  216. self.uiMin.SetStringSelection(str(minVal))
  217. def getValue(self):
  218. ampmStr = self.uiAmPm.GetStringSelection()
  219. hourVal = int(self.uiHour.GetStringSelection())
  220. if hourVal == 12:
  221. hourVal = 0
  222. if ampmStr == 'PM':
  223. hourVal += 12
  224. minVal = float(self.uiMin.GetStringSelection())
  225. value = float(hourVal) + minVal / 60.0
  226. return value
  227. def bindFunc(self, inFunc, outFunc, valFunc = None):
  228. self.uiAmPm.Bind(wx.EVT_ENTER_WINDOW, inFunc)
  229. self.uiAmPm.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
  230. self.uiHour.Bind(wx.EVT_ENTER_WINDOW, inFunc)
  231. self.uiHour.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
  232. self.uiMin.Bind(wx.EVT_ENTER_WINDOW, inFunc)
  233. self.uiMin.Bind(wx.EVT_LEAVE_WINDOW, outFunc)
  234. if valFunc:
  235. self.uiAmPm.Bind(self.eventType, valFunc)
  236. self.uiHour.Bind(self.eventType, valFunc)
  237. self.uiMin.Bind(self.eventType, valFunc)
  238. class ColorPicker(CubeColourDialog):
  239. def __init__(self, parent, colourData=None, style=CCD_SHOW_ALPHA,
  240. alpha=255, updateCB=None, exitCB=None):
  241. self.updateCB = updateCB
  242. CubeColourDialog.__init__(self, parent, colourData, style)
  243. self.okButton.Hide()
  244. self.cancelButton.Hide()
  245. self._colour.alpha = alpha
  246. self.alphaSpin.SetValue(self._colour.alpha)
  247. self.DrawAlpha()
  248. if exitCB:
  249. self.Bind(wx.EVT_CLOSE, exitCB)
  250. def SetPanelColours(self):
  251. self.oldColourPanel.RefreshColour(self._oldColour)
  252. self.newColourPanel.RefreshColour(self._colour)
  253. if self.updateCB:
  254. self.updateCB(self._colour.r, self._colour.g, self._colour.b, self._colour.alpha)
  255. class ObjectPropertyUI(ScrolledPanel):
  256. def __init__(self, parent, editor):
  257. self.editor = editor
  258. self.colorPicker = None
  259. self.lastColorPickerPos = None
  260. self.lastPropTab = None
  261. ScrolledPanel.__init__(self, parent)
  262. parentSizer = wx.BoxSizer(wx.VERTICAL)
  263. parentSizer.Add(self, 1, wx.EXPAND, 0)
  264. parent.SetSizer(parentSizer)
  265. parent.Layout()
  266. self.SetDropTarget(AnimFileDrop(self.editor))
  267. def clearPropUI(self):
  268. sizer = self.GetSizer()
  269. if sizer is not None:
  270. self.lastPropTab = self.nb.GetCurrentPage().GetName()
  271. sizer.Remove(self.propPane)
  272. self.propPane.Destroy()
  273. self.SetSizer(None)
  274. self.Layout()
  275. self.SetupScrolling(self, scroll_y=True, rate_y=20)
  276. def colorPickerExitCB(self, evt=None):
  277. self.lastColorPickerPos = self.colorPicker.GetPosition()
  278. self.colorPicker.Destroy()
  279. self.colorPicker = None
  280. def colorPickerUpdateCB(self, rr, gg, bb, aa):
  281. r = rr / 255.0
  282. g = gg / 255.0
  283. b = bb / 255.0
  284. a = aa / 255.0
  285. self.propCR.setValue(r)
  286. self.propCG.setValue(g)
  287. self.propCB.setValue(b)
  288. self.propCA.setValue(a)
  289. self.editor.objectMgr.updateObjectColor(r, g, b, a)
  290. def onColorSlider(self, evt):
  291. r = float(self.editor.ui.objectPropertyUI.propCR.getValue())
  292. g = float(self.editor.ui.objectPropertyUI.propCG.getValue())
  293. b = float(self.editor.ui.objectPropertyUI.propCB.getValue())
  294. a = float(self.editor.ui.objectPropertyUI.propCA.getValue())
  295. if self.colorPicker:
  296. evtObj = evt.GetEventObject()
  297. if evtObj == self.propCR.ui or\
  298. evtObj == self.propCR.ui.textValue:
  299. self.colorPicker.redSpin.SetValue(r * 255)
  300. self.colorPicker.AssignColourValue('r', r * 255, 255, 0)
  301. elif evtObj == self.propCG.ui or\
  302. evtObj == self.propCG.ui.textValue:
  303. self.colorPicker.greenSpin.SetValue(g * 255)
  304. self.colorPicker.AssignColourValue('g', g * 255, 255, 0)
  305. elif evtObj == self.propCB.ui or\
  306. evtObj == self.propCB.ui.textValue:
  307. self.colorPicker.blueSpin.SetValue(b * 255)
  308. self.colorPicker.AssignColourValue('b', b * 255, 255, 0)
  309. else:
  310. self.colorPicker._colour.alpha = a * 255
  311. self.colorPicker.alphaSpin.SetValue(self.colorPicker._colour.alpha)
  312. self.colorPicker.DrawAlpha()
  313. self.editor.objectMgr.updateObjectColor(r, g, b, a)
  314. def openColorPicker(self, evt, colourData, alpha):
  315. if self.colorPicker:
  316. self.lastColorPickerPos = self.colorPicker.GetPosition()
  317. self.colorPicker.Destroy()
  318. self.colorPicker = ColorPicker(self, colourData, alpha=alpha, updateCB=self.colorPickerUpdateCB, exitCB=self.colorPickerExitCB)
  319. self.colorPicker.GetColourData().SetChooseFull(True)
  320. self.colorPicker.Show()
  321. if self.lastColorPickerPos:
  322. self.colorPicker.SetPosition(self.lastColorPickerPos)
  323. def updateProps(self, obj, movable=True):
  324. self.clearPropUI()
  325. self.propPane = wx.Panel(self)
  326. mainSizer = wx.BoxSizer(wx.VERTICAL)
  327. mainSizer.Add(self.propPane, 1, wx.EXPAND, 0)
  328. self.SetSizer(mainSizer)
  329. self.nb = wx.Notebook(self.propPane, style=wx.NB_BOTTOM)
  330. sizer = wx.BoxSizer(wx.VERTICAL)
  331. sizer.Add(self.nb, 1, wx.EXPAND)
  332. self.propPane.SetSizer(sizer)
  333. self.transformPane = wx.Panel(self.nb, -1, name='Transform')
  334. self.nb.AddPage(self.transformPane, 'Transform')
  335. self.propX = ObjectPropUIEntry(self.transformPane, 'X')
  336. self.propY = ObjectPropUIEntry(self.transformPane, 'Y')
  337. self.propZ = ObjectPropUIEntry(self.transformPane, 'Z')
  338. self.propH = ObjectPropUISlider(self.transformPane, 'H', 0, 0, 360)
  339. self.propP = ObjectPropUISlider(self.transformPane, 'P', 0, 0, 360)
  340. self.propR = ObjectPropUISlider(self.transformPane, 'R', 0, 0, 360)
  341. self.propSX = ObjectPropUIEntry(self.transformPane, 'SX')
  342. self.propSY = ObjectPropUIEntry(self.transformPane, 'SY')
  343. self.propSZ = ObjectPropUIEntry(self.transformPane, 'SZ')
  344. transformProps = [
  345. self.propX, self.propY, self.propZ,
  346. self.propH, self.propP, self.propR,
  347. self.propSX, self.propSY, self.propSZ,
  348. ]
  349. sizer = wx.BoxSizer(wx.VERTICAL)
  350. sizer.AddMany(transformProps)
  351. self.transformPane.SetSizer(sizer)
  352. for transformProp in transformProps:
  353. transformProp.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
  354. self.editor.objectMgr.onLeaveObjectPropUI,
  355. self.editor.objectMgr.updateObjectTransform)
  356. if not movable:
  357. for transformProp in transformProps:
  358. transformProp.ui.Disable()
  359. self.lookPane = wx.Panel(self.nb, -1, name='Look')
  360. self.nb.AddPage(self.lookPane, 'Look')
  361. objNP = obj[OG.OBJ_NP]
  362. objRGBA = obj[OG.OBJ_RGBA]
  363. self.propCR = ObjectPropUISlider(self.lookPane, 'CR', objRGBA[0], 0, 1)
  364. self.propCG = ObjectPropUISlider(self.lookPane, 'CG', objRGBA[1], 0, 1)
  365. self.propCB = ObjectPropUISlider(self.lookPane, 'CB', objRGBA[2], 0, 1)
  366. self.propCA = ObjectPropUISlider(self.lookPane, 'CA', objRGBA[3], 0, 1)
  367. colorProps = [self.propCR, self.propCG, self.propCB, self.propCA]
  368. for colorProp in colorProps:
  369. colorProp.ui.bindFunc(self.onColorSlider)
  370. sizer = wx.BoxSizer(wx.VERTICAL)
  371. sizer.AddMany(colorProps)
  372. button = wx.Button(self.lookPane, -1, 'Color Picker', (0,0), (140, 20))
  373. _colourData = wx.ColourData()
  374. _colourData.SetColour(wx.Colour(objRGBA[0] * 255, objRGBA[1] * 255, objRGBA[2] * 255))
  375. button.Bind(wx.EVT_BUTTON, lambda p0=None, p1=_colourData, p2=objRGBA[3] * 255: self.openColorPicker(p0, p1, p2))
  376. sizer.Add(button)
  377. if self.colorPicker:
  378. self.openColorPicker(None, _colourData, objRGBA[3] * 255)
  379. objDef = obj[OG.OBJ_DEF]
  380. if objDef.updateModelFunction is not None or (objDef.model is not None and len(objDef.models) > 0):
  381. defaultModel = obj[OG.OBJ_MODEL]
  382. if defaultModel is None:
  383. defaultModel = ''
  384. if len(objDef.models) == 0:
  385. modelList = ''
  386. else:
  387. modelList = objDef.models
  388. propUI = ObjectPropUICombo(self.lookPane, 'model', defaultModel, modelList, obj, callBack=objDef.updateModelFunction)
  389. sizer.Add(propUI)
  390. propUI.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
  391. self.editor.objectMgr.onLeaveObjectPropUI,
  392. lambda p0=None, p1=obj: self.editor.objectMgr.updateObjectModelFromUI(p0, p1))
  393. animList = objDef.animDict.get(obj[OG.OBJ_MODEL])
  394. if len(objDef.anims) > 0 or animList:
  395. if animList is None:
  396. animList = objDef.anims
  397. propUI = ObjectPropUICombo(self.lookPane, 'anim', obj[OG.OBJ_ANIM], animList)
  398. sizer.Add(propUI)
  399. propUI.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
  400. self.editor.objectMgr.onLeaveObjectPropUI,
  401. lambda p0=None, p1=obj: self.editor.objectMgr.updateObjectAnimFromUI(p0, p1))
  402. self.lookPane.SetSizer(sizer)
  403. self.propsPane = wx.Panel(self.nb, -1, name='Properties')
  404. self.nb.AddPage(self.propsPane, 'Properties')
  405. sizer = wx.BoxSizer(wx.VERTICAL)
  406. propNames = objDef.orderedProperties[:]
  407. for key in list(objDef.properties.keys()):
  408. if key not in propNames:
  409. propNames.append(key)
  410. for key in propNames:
  411. # handling properties mask
  412. propMask = BitMask32()
  413. for modeKey in list(objDef.propertiesMask.keys()):
  414. if key in objDef.propertiesMask[modeKey]:
  415. propMask |= modeKey
  416. if not propMask.isZero():
  417. if (self.editor.mode & propMask).isZero():
  418. continue
  419. propDef = objDef.properties[key]
  420. propType = propDef[OG.PROP_TYPE]
  421. propDataType = propDef[OG.PROP_DATATYPE]
  422. value = obj[OG.OBJ_PROP].get(key)
  423. if propType == OG.PROP_UI_ENTRY:
  424. propUI = ObjectPropUIEntry(self.propsPane, key)
  425. propUI.setValue(value)
  426. sizer.Add(propUI)
  427. elif propType == OG.PROP_UI_SLIDE:
  428. if len(propDef) <= OG.PROP_RANGE:
  429. continue
  430. propRange = propDef[OG.PROP_RANGE]
  431. if value is None:
  432. continue
  433. if propDataType != OG.PROP_FLOAT:
  434. value = float(value)
  435. propUI = ObjectPropUISlider(self.propsPane, key, value, propRange[OG.RANGE_MIN], propRange[OG.RANGE_MAX])
  436. sizer.Add(propUI)
  437. elif propType == OG.PROP_UI_SPIN:
  438. if len(propDef) <= OG.PROP_RANGE:
  439. continue
  440. propRange = propDef[OG.PROP_RANGE]
  441. if value is None:
  442. continue
  443. propUI = ObjectPropUISpinner(self.propsPane, key, value, propRange[OG.RANGE_MIN], propRange[OG.RANGE_MAX])
  444. sizer.Add(propUI)
  445. elif propType == OG.PROP_UI_CHECK:
  446. if value is None:
  447. continue
  448. propUI = ObjectPropUICheck(self.propsPane, key, value)
  449. sizer.Add(propUI)
  450. elif propType == OG.PROP_UI_RADIO:
  451. if len(propDef) <= OG.PROP_RANGE:
  452. continue
  453. propRange = propDef[OG.PROP_RANGE]
  454. if value is None:
  455. continue
  456. if propDataType != OG.PROP_STR:
  457. for i in range(len(propRange)):
  458. propRange[i] = str(propRange[i])
  459. value = str(value)
  460. propUI = ObjectPropUIRadio(self.propsPane, key, value, propRange)
  461. sizer.Add(propUI)
  462. elif propType == OG.PROP_UI_COMBO:
  463. if len(propDef) <= OG.PROP_RANGE:
  464. continue
  465. propRange = propDef[OG.PROP_RANGE]
  466. if value is None:
  467. continue
  468. if propDataType != OG.PROP_STR:
  469. for i in range(len(propRange)):
  470. propRange[i] = str(propRange[i])
  471. value = str(value)
  472. propUI = ObjectPropUICombo(self.propsPane, key, value, propRange)
  473. sizer.Add(propUI)
  474. elif propType == OG.PROP_UI_COMBO_DYNAMIC:
  475. if len(propDef) <= OG.PROP_DYNAMIC_KEY:
  476. continue
  477. propDynamicKey = propDef[OG.PROP_DYNAMIC_KEY]
  478. if propDynamicKey == OG.PROP_MODEL:
  479. dynamicRangeKey = obj[OG.OBJ_MODEL]
  480. else:
  481. dynamicRangeKey = obj[OG.OBJ_PROP].get(propDynamicKey)
  482. if dynamicRangeKey is None:
  483. self.editor.objectMgr.updateObjectPropValue(obj, key, propDef[OG.PROP_DEFAULT], fUndo=False)
  484. continue
  485. propRange = propDef[OG.PROP_RANGE].get(dynamicRangeKey)
  486. if propRange is None:
  487. self.editor.objectMgr.updateObjectPropValue(obj, key, propDef[OG.PROP_DEFAULT], fUndo=False)
  488. continue
  489. if value is None:
  490. continue
  491. if propDataType != OG.PROP_STR:
  492. for i in range(len(propRange)):
  493. propRange[i] = str(propRange[i])
  494. value = str(value)
  495. if value not in propRange:
  496. value = propRange[0]
  497. self.editor.objectMgr.updateObjectPropValue(obj, key, value, fUndo=False)
  498. propUI = ObjectPropUICombo(self.propsPane, key, value, propRange)
  499. sizer.Add(propUI)
  500. elif propType == OG.PROP_UI_TIME:
  501. if value is None:
  502. continue
  503. propUI = ObjectPropUITime(self.propsPane, key, value)
  504. sizer.Add(propUI)
  505. else:
  506. # unspported property type
  507. continue
  508. propUI.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
  509. self.editor.objectMgr.onLeaveObjectPropUI,
  510. lambda p0=None, p1=obj, p2=key: self.editor.objectMgr.updateObjectProperty(p0, p1, p2))
  511. self.propsPane.SetSizer(sizer)
  512. self.Layout()
  513. self.SetupScrolling(self, scroll_y = True, rate_y = 20)
  514. if self.lastPropTab == 'Transform':
  515. self.nb.SetSelection(0)
  516. elif self.lastPropTab == 'Look':
  517. self.nb.SetSelection(1)
  518. elif self.lastPropTab == 'Properties':
  519. self.nb.SetSelection(2)