main.py (42070B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\UITree\main.py 2 from __future__ import print_function 3 __author__ = 'fridrik' 4 import os 5 import sys 6 import weakref 7 import blue 8 import carbonui 9 import eveicon 10 import trinity 11 import uthread 12 from carbon.common.script.util import timerstuff 13 from carbonui import TextColor, uiconst 14 from carbonui.control.buttonIcon import ButtonIcon 15 from carbonui.control.contextMenu.menuData import MenuData 16 from carbonui.control.contextMenu.menuEntryData import MenuEntryDataRadioButton 17 from carbonui.control.dragResizeCont import DragResizeCont 18 from clientsettings.setting import UserSettingBool, UserSettingEnum 19 from carbonui.uicore import uicore 20 from carbonui.primitives.base import Base 21 from carbonui.primitives.desktop import UIRoot 22 from carbonui.control.layer import LayerCore 23 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText 24 from carbonui.primitives.container import Container 25 from carbonui.primitives.containerAutoSize import ContainerAutoSize 26 from carbonui.primitives.frame import Frame 27 from carbonui.primitives.fill import Fill 28 from carbonui.primitives.sprite import Sprite 29 from carbonui.control.scrollentries import SE_BaseClassCore 30 from carbonui.control.scrollentries import ScrollEntryNode 31 from carbonui.util.various_unsorted import GetAttrs, GetWindowAbove, SortListOfTuples 32 from eve.client.script.ui import eveColor 33 from eve.client.script.ui.control import eveLabel 34 from eve.client.script.ui.control.eveLabel import EveLabelMedium as Label 35 from eve.client.script.ui.control.eveScroll import Scroll 36 from eve.client.script.ui.shared.mapView.dockPanel import DockablePanel 37 LAYER_DEFAULT = 1 38 LAYER_MODAL = 2 39 LAYER_DESKTOP = 3 40 debugger_parent_setting = UserSettingEnum('uitreeParentLayer', LAYER_DEFAULT, (LAYER_DEFAULT, LAYER_MODAL, LAYER_DESKTOP)) 41 show_pick_hilite_setting = UserSettingBool('uitreeShowPickHilite', True) 42 ignore_full_screen_pick_setting = UserSettingBool('uitreeIgnoreFullScreenPick', False) 43 RESROOT = os.path.dirname(__file__) + '\\res\\' 44 VALUEOFFSET = 160 45 IGNORE_PROPERTIES = ['dead'] 46 ALIGNMENTNAMES = {val:key for key, val in carbonui.Align.__dict__.items() if not key.startswith('_')} 47 PICKSTATENAMES = {} 48 for k, v in uiconst.__dict__.iteritems(): 49 if k.startswith('TR2_SPS'): 50 PICKSTATENAMES[v] = k 51 52 SPRITEEFFECTNAMES = {} 53 BLENDMODENAMES = {} 54 OUTPUTMODENAMES = {uiconst.OUTPUT_COLOR: 'OUTPUT_COLOR', 55 uiconst.OUTPUT_GLOW: 'OUTPUT_GLOW', 56 uiconst.OUTPUT_COLOR_AND_GLOW: 'OUTPUT_COLOR_AND_GLOW'} 57 for k, v in trinity.__dict__.iteritems(): 58 if k.startswith('TR2_SFX'): 59 SPRITEEFFECTNAMES[v] = k 60 if k.startswith('TR2_SBM'): 61 BLENDMODENAMES[v] = k 62 63 COMBOPROPERTIES = {'align': ALIGNMENTNAMES, 64 'pickState': PICKSTATENAMES, 65 'spriteEffect': SPRITEEFFECTNAMES, 66 'blendMode': BLENDMODENAMES, 67 'outputMode': OUTPUTMODENAMES} 68 COMBOPROPERTIES_NAMESPACE = {'align': 'uiconst', 69 'pickState': 'uiconst', 70 'spriteEffect': 'trinity', 71 'blendMode': 'trinity', 72 'outputMode': 'uiconst'} 73 PROPERTYCAPS = {'opacity': (0.0, 5.0), 74 'color.r': (0.0, 5.0), 75 'color.g': (0.0, 5.0), 76 'color.b': (0.0, 5.0), 77 'color.a': (0.0, 5.0), 78 'color': (0.0, 5.0), 79 'pickRadius': (-1, sys.maxint)} 80 PYOBJECT_SUBLISTS = ('children', 'background') 81 PYOBJECT_SUBDICTS = () 82 83 class UITree(DockablePanel): 84 default_name = 'UITree' 85 default_windowID = 'uitree' 86 default_caption = 'UI Tree' 87 default_width = 400 88 default_height = 400 89 default_minSize = (300, default_height) 90 default_left = '__center__' 91 default_top = '__center__' 92 filterString = None 93 panelID = default_windowID 94 95 def ApplyAttributes(self, attributes): 96 super(UITree, self).ApplyAttributes(attributes) 97 mainArea = self.GetMainArea() 98 mainArea.padding = 6 99 mainArea.padTop = 28 100 self._selectedObject = None 101 self._infoContainer = Container(parent=mainArea, align=uiconst.TOTOP, height=72) 102 self.searchInput = SingleLineEditText(parent=self._infoContainer, align=uiconst.BOTTOMRIGHT, left=4, top=8, width=100, OnChange=self.OnSearchInputChange, hintText='Search') 103 self.searchInput.SetHistoryVisibility(False) 104 self.searchInput.ShowClearButton() 105 menuBtn = ButtonIcon(parent=self._infoContainer, align=uiconst.TOPRIGHT, texturePath=eveicon.settings) 106 menuBtn.expandOnLeft = True 107 menuBtn.GetMenu = self.GetSettingMenu 108 self._infoLabel = Label(parent=self._infoContainer, state=uiconst.UI_DISABLED, color=(1.0, 1.0, 1.0, 0.75), left=2) 109 self.searchResultParent = ContainerAutoSize(parent=mainArea, align=uiconst.TOTOP_NOPUSH, padding=(26, -6, 4, 0), bgColor=(0.5, 0.5, 0.5, 1)) 110 resizeCont = DragResizeCont(parent=mainArea, align=uiconst.TOBOTTOM, settingsID='UITree_ResizeCont', minSize=50, maxSize=500) 111 self.attributeScroll = Scroll(parent=resizeCont.mainCont, name='attributeScroll', align=uiconst.TOALL) 112 self.scroll = Scroll(parent=mainArea, name='treeScroll') 113 self._hiliteFrame = Fill(parent=uicore.desktop, align=uiconst.ABSOLUTE, color=(0, 1, 0, 0.3), idx=0, state=uiconst.UI_DISABLED) 114 self._selectedFrame = Frame(parent=uicore.desktop, align=uiconst.ABSOLUTE, color=(0, 1, 0, 0.3), idx=0, state=uiconst.UI_DISABLED) 115 self.ReloadUIRoots() 116 uthread.new(self.UpdateInfo) 117 debugger_parent_setting.on_change.connect(self._UpdateParent) 118 if not debugger_parent_setting.is_equal(LAYER_DEFAULT): 119 uthread.new(self._UpdateParent) 120 self._keyDownCookie = uicore.uilib.RegisterForTriuiEvents([uiconst.UI_KEYDOWN], self.OnGlobalKeyDown) 121 122 def OnSearchInputChange(self, *args): 123 self.searchThread = timerstuff.AutoTimer(250, self.SearchTree) 124 125 def SearchTree(self): 126 self.searchThread = None 127 self.filterString = self.searchInput.GetValue() 128 if not self.filterString: 129 self.searchResultParent.Hide() 130 self.searchResultParent.Flush() 131 return 132 self.searchResultParent.Flush() 133 res = [] 134 searchFor = self.filterString.lower() 135 136 def Crawl(obj, path): 137 if obj is self: 138 return 139 if searchFor in obj.name.lower(): 140 if path: 141 res.append((obj, path + '/ <b>' + obj.name + '</b>')) 142 else: 143 res.append((obj, '<b>' + obj.name + '</b>')) 144 if hasattr(obj, 'children'): 145 for each in obj.children: 146 if path: 147 Crawl(each, path + '/' + obj.name) 148 else: 149 Crawl(each, obj.name) 150 151 for root in uicore.uilib.rootObjects: 152 Crawl(root, '') 153 154 if res: 155 for obj, path in res[:20]: 156 label = Label(parent=self.searchResultParent, align=uiconst.TOTOP, text=path, state=uiconst.UI_NORMAL, padding=(10, 2, 10, 2)) 157 label._searchObj = obj 158 label.hint = path 159 label.OnClick = (self.OnSearchResultClick, obj) 160 161 if len(res) > 20: 162 Label(parent=self.searchResultParent, align=uiconst.TOTOP, text='and even more... (%s found)' % len(res), padding=(10, 2, 10, 2)) 163 else: 164 Label(parent=self.searchResultParent, align=uiconst.TOTOP, text='No Match!', padding=(10, 3, 10, 3)) 165 self.searchResultParent.Show() 166 167 def OnSearchResultClick(self, obj): 168 self.searchResultParent.Hide() 169 self.ShowUIObject(obj) 170 171 def GetSettingMenu(self, *args): 172 menuData = MenuData() 173 menuData.AddCheckbox('Show pick hilite', setting=show_pick_hilite_setting) 174 menuData.AddCheckbox('Ignore fullscreen hilite', setting=ignore_full_screen_pick_setting) 175 menuData.AddEntry('Configure parent', subMenuData=[MenuEntryDataRadioButton('Default', LAYER_DEFAULT, setting=debugger_parent_setting), MenuEntryDataRadioButton('uicore.layer.modal (above most things)', LAYER_MODAL, setting=debugger_parent_setting), MenuEntryDataRadioButton('uicore.desktop (above everything)', LAYER_DESKTOP, setting=debugger_parent_setting)]) 176 menuData.AddEntry('Reload Textures', self.ReloadTextures, texturePath=eveicon.refresh) 177 return menuData 178 179 def ReloadTextures(self): 180 for resPath in blue.motherLode.GetNonCachedKeys() + blue.motherLode.GetCachedKeys(): 181 if resPath.lower().startswith('res:/ui'): 182 res = blue.motherLode.Lookup(resPath) 183 if res: 184 if hasattr(res, 'Reload'): 185 res.Reload() 186 187 def FinalizeModeChange(self, registerSettings = True): 188 super(UITree, self).FinalizeModeChange(registerSettings) 189 self._UpdateParent() 190 191 def _UpdateParent(self, *args): 192 if debugger_parent_setting.is_equal(LAYER_DEFAULT): 193 if self.IsFloating(): 194 self.SetParent(uicore.layer.main, idx=0) 195 else: 196 self.SetParent(uicore.layer.sidePanels, idx=0) 197 elif debugger_parent_setting.is_equal(LAYER_MODAL): 198 self.SetParent(uicore.layer.modal, idx=0) 199 else: 200 self.SetParent(uicore.desktop, idx=0) 201 202 def ToggleCheckboxSetting(self, settingName, *args): 203 checked = settings.user.ui.Get('uitree' + settingName, True) 204 settings.user.ui.Set('uitree' + settingName, not checked) 205 206 def Close(self, *args, **kwds): 207 frame = getattr(self, '_hiliteFrame', None) 208 if frame: 209 frame.Close() 210 if getattr(self, '_selectedFrame', None): 211 self._selectedFrame.Close() 212 super(UITree, self).Close(*args, **kwds) 213 214 def OnGlobalKeyDown(self, *args, **kwds): 215 if self.destroyed: 216 return False 217 ctrl = uicore.uilib.Key(uiconst.VK_CONTROL) 218 alt = uicore.uilib.Key(uiconst.VK_MENU) 219 if ctrl and not (uicore.uilib.mouseOver.IsUnder(self) or uicore.uilib.mouseOver is self): 220 self.ShowUIObject(uicore.uilib.mouseOver) 221 return True 222 223 def UpdateInfo(self): 224 while not self.destroyed: 225 mo = uicore.uilib.mouseOver 226 if mo: 227 infoText = 'MouseOver: %s' % mo.name 228 else: 229 infoText = 'MouseOver: None' 230 active = uicore.registry.GetActive() 231 if active: 232 infoText += '<br>Active: %s' % active.name 233 else: 234 infoText += '<br>Active: None' 235 focus = uicore.registry.GetFocus() 236 if focus: 237 infoText += '<br>Focus: %s' % (focus.name or getattr(focus, '__guid__', focus.__class__.__name__)) 238 else: 239 infoText += '<br>Focus: None' 240 capture = uicore.uilib.GetMouseCapture() 241 if capture: 242 infoText += '<br>Capture: %s' % (capture.name or getattr(capture, '__guid__', capture.__class__.__name__)) 243 else: 244 infoText += '<br>Capture: None' 245 infoText += '<br>MouseX/Y: %s, %s' % (uicore.uilib.x, uicore.uilib.y) 246 showHilite = settings.user.ui.Get('uitreeShowPickHilite', True) 247 ignoreFullScreenPick = settings.user.ui.Get('uitreeIgnoreFullScreenPick', False) 248 hiliteUIObject = None 249 if showHilite: 250 if mo.IsUnder(self): 251 if mo.IsUnder(self.scroll): 252 uiObjectGetAbs = GetAttrs(mo, 'sr', 'node', 'uiObject', 'GetAbsolute') 253 if uiObjectGetAbs: 254 hiliteUIObject = GetAttrs(mo, 'sr', 'node', 'uiObject') 255 elif mo is not uicore.desktop and mo is not self and not mo.IsUnder(self) and self.state == uiconst.UI_NORMAL: 256 hiliteUIObject = mo 257 if hiliteUIObject and ignoreFullScreenPick and isinstance(hiliteUIObject, (LayerCore, UIRoot)): 258 hiliteUIObject = None 259 self.ShowHilitedObjectInUI(hiliteUIObject) 260 selectedObject = None 261 if self._selectedObject: 262 selectedObject = self._selectedObject() 263 if getattr(selectedObject, 'destroyed', False): 264 selectedObject = None 265 for node in self.scroll.sr.nodes: 266 if node.panel: 267 node.panel.UpdateSelected(selectedObject) 268 node.panel.UpdatePickHilite(hiliteUIObject) 269 if getattr(node.uiObject, 'destroyed', False): 270 node.panel.CheckDestroyed() 271 272 self.ShowSelectedObjectInUI(selectedObject) 273 self._infoLabel.text = infoText 274 self._infoContainer.height = self._infoLabel.textheight + 5 275 blue.pyos.synchro.SleepWallclock(100) 276 277 def ShowSelectedObjectInUI(self, uiObject): 278 if uiObject and hasattr(uiObject, 'GetAbsolute'): 279 self._selectedFrame.pos = uiObject.GetAbsolute() 280 self._selectedFrame.display = True 281 else: 282 self._selectedFrame.display = False 283 284 def ShowHilitedObjectInUI(self, uiObject): 285 if self.destroyed: 286 return 287 if uiObject: 288 self._hiliteFrame.pos = uiObject.GetAbsolute() 289 self._hiliteFrame.display = True 290 else: 291 self._hiliteFrame.display = False 292 293 def ReloadUIRoots(self, *args): 294 startRoot = uicore.uilib.rootObjects 295 scrollNodes = [] 296 for root in startRoot: 297 self._CrawlUIObject(root, scrollNodes, 0) 298 299 self.scroll.LoadContent(contentList=scrollNodes) 300 301 def _AddUIObject(self, uiObject, scrollList, lvl, isExpanded = False, objectLabel = None, isExpandable = False): 302 if len(scrollList) > 1 and lvl: 303 for i in xrange(1, len(scrollList) - 1): 304 last = scrollList[-i] 305 if last.level < lvl: 306 break 307 if lvl not in last.connectLevels: 308 last.connectLevels.append(lvl) 309 310 node = ScrollEntryNode(decoClass=UITreeEntry, uiObject=uiObject, level=lvl, isExpanded=isExpanded, objectLabel=objectLabel, isExpandable=isExpandable, connectLevels=[lvl]) 311 scrollList.append(node) 312 313 def IsExpanded(self, uiObject): 314 desktopObjectID = id(uicore.desktop) 315 allPrefs = settings.user.ui.Get('UITreeExpandedObjects', {}) 316 if desktopObjectID not in allPrefs: 317 return False 318 uiObjectID = id(uiObject) 319 if getattr(uiObject, 'parent', None): 320 uiObjectID = (id(uiObject.parent), uiObjectID) 321 return uiObjectID in allPrefs[desktopObjectID] 322 323 def ExpandUIObject(self, uiObject): 324 uiObjectID = id(uiObject) 325 if getattr(uiObject, 'parent', None): 326 uiObjectID = (id(uiObject.parent), uiObjectID) 327 desktopObjectID = id(uicore.desktop) 328 allPrefs = settings.user.ui.Get('UITreeExpandedObjects', {}) 329 if desktopObjectID not in allPrefs: 330 allPrefs[desktopObjectID] = [] 331 if uiObjectID not in allPrefs[desktopObjectID]: 332 allPrefs[desktopObjectID].append(uiObjectID) 333 settings.user.ui.Set('UITreeExpandedObjects', allPrefs) 334 335 def ToggleExpandedObject(self, uiObject): 336 uiObjectID = id(uiObject) 337 if getattr(uiObject, 'parent', None): 338 uiObjectID = (id(uiObject.parent), uiObjectID) 339 desktopObjectID = id(uicore.desktop) 340 allPrefs = settings.user.ui.Get('UITreeExpandedObjects', {}) 341 if desktopObjectID not in allPrefs: 342 allPrefs[desktopObjectID] = [] 343 if uiObjectID in allPrefs[desktopObjectID]: 344 allPrefs[desktopObjectID].remove(uiObjectID) 345 else: 346 allPrefs[desktopObjectID].append(uiObjectID) 347 settings.user.ui.Set('UITreeExpandedObjects', allPrefs) 348 self.ReloadUIRoots() 349 350 def _CrawlUIObject(self, uiObject, scrollNodes, lvl, objectLabel = None): 351 isExpandable = UITree._IsExpandable(uiObject) 352 if isExpandable: 353 isExpanded = self.IsExpanded(uiObject) 354 else: 355 isExpanded = False 356 self._AddUIObject(uiObject, scrollNodes, lvl, isExpanded, objectLabel=objectLabel, isExpandable=isExpandable) 357 if isExpanded: 358 if isinstance(uiObject, Base): 359 allPy = dir(uiObject) 360 for propertyName in allPy: 361 if propertyName.startswith('_') or propertyName in IGNORE_PROPERTIES: 362 continue 363 try: 364 prop = getattr(uiObject, propertyName, None) 365 except: 366 print('Failed on property', propertyName) 367 continue 368 369 try: 370 if getattr(prop, 'TypeInfo', None): 371 self._CrawlUIObject(prop, scrollNodes, lvl + 1, objectLabel=propertyName) 372 except (KeyError, RuntimeError): 373 pass 374 375 for dictName in PYOBJECT_SUBDICTS: 376 dct = getattr(uiObject, dictName, {}) 377 if dct: 378 for k, v in dct.iteritems(): 379 self._CrawlUIObject(v, scrollNodes, lvl + 1, objectLabel='%s: %s' % (dictName.lstrip('_'), k)) 380 381 for listName in PYOBJECT_SUBLISTS: 382 lst = getattr(uiObject, listName, []) 383 if lst: 384 for objChild in lst: 385 if listName == 'background': 386 self._CrawlUIObject(objChild, scrollNodes, lvl + 1, objectLabel=listName) 387 else: 388 self._CrawlUIObject(objChild, scrollNodes, lvl + 1) 389 390 else: 391 allC = dir(uiObject) 392 for propertyName in allC: 393 if propertyName.startswith('_'): 394 continue 395 prop = getattr(uiObject, propertyName, None) 396 if callable(prop): 397 continue 398 if getattr(prop, '__bluetype__', None) == 'blue.List': 399 isExpanded = self.IsExpanded(prop) 400 self._AddUIObject(prop, scrollNodes, lvl + 1, isExpanded, isExpandable=True, objectLabel='%s (%s)' % (propertyName, len(prop))) 401 if isExpanded: 402 for each in prop: 403 self._CrawlUIObject(each, scrollNodes, lvl + 2) 404 405 continue 406 elif getattr(prop, '__bluetype__', None) == 'blue.Dict': 407 isExpanded = self.IsExpanded(prop) 408 self._AddUIObject(prop, scrollNodes, lvl + 1, isExpanded, isExpandable=True, objectLabel=propertyName) 409 if isExpanded: 410 for k, v in prop.items(): 411 self._CrawlUIObject(v, scrollNodes, lvl + 2, objectLabel=k) 412 413 continue 414 elif hasattr(prop, 'TypeInfo'): 415 self._CrawlUIObject(prop, scrollNodes, lvl + 1, objectLabel=propertyName) 416 417 @classmethod 418 def _IsExpandable(cls, uiObject): 419 if isinstance(uiObject, Base): 420 return True 421 allC = dir(uiObject) 422 for propertyName in allC: 423 if propertyName.startswith('_'): 424 continue 425 prop = getattr(uiObject, propertyName, None) 426 if getattr(prop, '__bluetype__', None) in ('blue.List', 'blue.Dict'): 427 return True 428 if hasattr(prop, 'TypeInfo'): 429 return True 430 431 return False 432 433 def ShowUIObject(self, uiObject): 434 traceUp = [uiObject] 435 parent = uiObject.parent 436 while parent: 437 traceUp.insert(0, parent) 438 parent = parent.parent 439 440 for each in traceUp: 441 self.ExpandUIObject(each) 442 443 self.ShowPropertiesForObject(uiObject) 444 for node in self.scroll.sr.nodes: 445 if node.uiObject is uiObject: 446 self.scroll.ShowNodeIdx(node.idx) 447 break 448 449 def ShowPropertiesForObject(self, uiObject): 450 if uiObject is None: 451 return 452 try: 453 len(uiObject) 454 self.attributeScroll.LoadContent(contentList=[]) 455 return 456 except: 457 pass 458 459 self._selectedObject = weakref.ref(uiObject) 460 level = 0 461 newNodes = [] 462 if isinstance(uiObject, Base): 463 combined = [] 464 if hasattr(uiObject, 'color'): 465 combined.append(('color', ('color.r', 'color.g', 'color.b', 'color.a'))) 466 for propertyName, subs in combined: 467 propertyNode = ScrollEntryNode(decoClass=UITreePropertyEntry, uiObject=uiObject, level=level, propertyName=propertyName, combineProperties=subs) 468 newNodes.append(propertyNode) 469 470 basics = ['__class__', 471 'name', 472 'display', 473 'opacity', 474 'align', 475 'pickState', 476 'pos', 477 'padding', 478 'uniqueUiName', 479 'elementId', 480 'windowID', 481 'windowInstanceID', 482 'displayRect', 483 'blendMode', 484 'spriteEffect', 485 'outputMode', 486 'clipChildren', 487 'pickRadius', 488 'absoluteCoordinates', 489 'cacheContents', 490 'text', 491 'effectAmount', 492 'effectAmount2', 493 'glowBrightness', 494 'useSizeFromTexture', 495 'rotation', 496 'rotationCenter', 497 'scale', 498 'scalingCenter', 499 'scalingRotation', 500 '', 501 '__guid__'] 502 for propertyName in basics: 503 prop = getattr(uiObject, propertyName, '_!_') 504 if prop == '_!_': 505 continue 506 propertyNode = ScrollEntryNode(decoClass=UITreePropertyEntry, uiObject=uiObject, level=level, propertyName=propertyName) 507 newNodes.append(propertyNode) 508 509 else: 510 for attr in dir(uiObject): 511 if attr[0] == attr[0].upper(): 512 continue 513 if attr[0] == '_': 514 continue 515 if attr in ('children', 'background'): 516 continue 517 propertyNode = ScrollEntryNode(decoClass=UITreePropertyEntry, uiObject=uiObject, level=level, propertyName=attr) 518 newNodes.append((attr, propertyNode)) 519 520 newNodes = SortListOfTuples(newNodes) 521 self.ReloadUIRoots() 522 self.attributeScroll.LoadContent(contentList=newNodes) 523 524 525 class UITreeNumberInput(Container): 526 default_state = uiconst.UI_NORMAL 527 _downPos = 0 528 529 def ApplyAttributes(self, attributes): 530 Container.ApplyAttributes(self, attributes) 531 self._value = attributes.value 532 self._propertyName = attributes.propertyName 533 self._propertyIndex = attributes.propertyIndex 534 self._uiObject = attributes.uiObject 535 self._editType = type(self._value) 536 self._label = eveLabel.EveLabelMedium(parent=self, state=uiconst.UI_DISABLED, align=uiconst.CENTERLEFT, text=str(self._value)) 537 if self._propertyName in COMBOPROPERTIES: 538 self._label.text = COMBOPROPERTIES[self._propertyName].get(self._value, 'Unknown value!') 539 self.expandOnLeft = True 540 elif self._editType == float: 541 self.hint = 'MouseDown and Drag to change value<br>ALT for 1/100, ALT-SHIFT for 1/1000' 542 if -1 <= self._value <= 1: 543 self._label.text = '%.3f' % self._value 544 elif self._editType == int: 545 self.hint = 'MouseDown and Drag to change value' 546 elif self._editType == type: 547 try: 548 text = repr(self._value).replace("<class '", '').replace("'>", '') 549 text = text.split('.')[-1] + ' (%s)' % text 550 self._label.text = text 551 except: 552 pass 553 554 self.UpdateLabelColor() 555 self.UpdateSize() 556 557 def UpdateLabelColor(self): 558 if self._propertyName == 'display' and self._value is False: 559 color = TextColor.DANGER 560 elif self._propertyName == 'opacity' and self._value == 0.0: 561 color = TextColor.DANGER 562 elif self._propertyName == 'opacity' and self._value > 1.0: 563 color = TextColor.WARNING 564 else: 565 color = TextColor.NORMAL 566 self._label.rgba = color 567 568 def GetMenu(self): 569 m = [] 570 if self._propertyName in COMBOPROPERTIES: 571 m += self.GetComboOptions() 572 if m: 573 m.append(None) 574 m.append(('Copy value', self.CopyValue)) 575 return m 576 577 def CopyValue(self): 578 blue.pyos.SetClipboardData(repr(self._value)) 579 580 def GetComboOptions(self, *args): 581 m = [] 582 for value, name in COMBOPROPERTIES[self._propertyName].iteritems(): 583 m.append((name, self.ChangeComboOption, (value,))) 584 585 return m 586 587 def ChangeComboOption(self, newValue, *args): 588 self._value = newValue 589 self._label.text = COMBOPROPERTIES[self._propertyName].get(self._value, 'Unknown value!') 590 self.UpdateSize() 591 self.UpdateLabelColor() 592 self.SetPropertyValue(self._value) 593 594 def UpdateSize(self): 595 self.width = self._label.textwidth + 6 596 597 def OnClick(self, *args): 598 if self._propertyName in COMBOPROPERTIES: 599 return 600 if self._propertyName == '__class__': 601 OpenInEditor(self._uiObject) 602 elif self._editType in (bool,): 603 self._value = not self._value 604 self.SetPropertyValue(self._value) 605 self._label.text = str(self._value) 606 self.UpdateLabelColor() 607 self.UpdateSize() 608 609 def OnMouseDown(self, *args): 610 if self._propertyName in COMBOPROPERTIES: 611 return 612 if self._editType in (int, float): 613 self._downPos = uicore.uilib.x 614 self._updateValue = timerstuff.AutoTimer(10, self.ScrollValue) 615 616 def OnMouseUp(self, *args): 617 if self._propertyName in COMBOPROPERTIES: 618 return 619 if self._editType in (int, float): 620 self._updateValue = None 621 self.ScrollValue(register=True) 622 623 def ScrollValue(self, register = False): 624 posDiff = (uicore.uilib.x - self._downPos) / 2 625 alt = uicore.uilib.Key(uiconst.VK_MENU) 626 shift = uicore.uilib.Key(uiconst.VK_SHIFT) 627 if self._editType == float: 628 if shift: 629 newValue = round(self._value + posDiff / 1000.0, 3) 630 elif alt: 631 newValue = round(self._value + posDiff / 100.0, 3) 632 else: 633 newValue = round(self._value + posDiff / 10.0, 3) 634 elif shift: 635 newValue = self._value + posDiff / 4 636 elif alt: 637 newValue = self._value + posDiff / 2 638 else: 639 newValue = self._value + posDiff 640 PROPERTYCAPS = {'opacity': (0.0, 5.0), 641 'color.r': (0.0, 5.0), 642 'color.g': (0.0, 5.0), 643 'color.b': (0.0, 5.0), 644 'color.a': (0.0, 5.0), 645 'color': (0.0, 5.0), 646 'pickRadius': (-1, sys.maxint)} 647 for capName, (mn, mx) in PROPERTYCAPS.iteritems(): 648 if capName.lower() in self._propertyName.lower(): 649 newValue = min(max(newValue, mn), mx) 650 651 self.SetPropertyValue(newValue) 652 if self._editType in (float,) and -1 <= newValue <= 1: 653 self._label.text = '%.3f' % newValue 654 else: 655 self._label.text = str(newValue) 656 if register: 657 self._value = newValue 658 self.UpdateLabelColor() 659 self.UpdateSize() 660 661 def SetPropertyValue(self, value): 662 obj = self._uiObject 663 propertyName = self._propertyName 664 if '.' in self._propertyName: 665 trace = self._propertyName.split('.') 666 for each in trace[:-1]: 667 obj = getattr(obj, each, None) 668 if not obj: 669 return 670 671 propertyName = trace[-1] 672 if self._propertyIndex is not None: 673 current = list(getattr(obj, propertyName)) 674 current[self._propertyIndex] = value 675 value = tuple(current) 676 try: 677 setattr(obj, propertyName, value) 678 except: 679 pass 680 681 682 INDENTSIZE = 18 683 684 def GetClassPath(obj): 685 ret = None 686 module = obj.__module__ 687 exec 'import %s' % module in globals() 688 exec 'ret = %s.__file__' % module 689 return ret 690 691 692 def OpenInEditor(uiObjectOrRenderObject): 693 blue.os.ShellExecute(GetClassPath(uiObjectOrRenderObject), 'edit') 694 695 696 class UITreeEntry(SE_BaseClassCore): 697 ENTRYHEIGHT = 18 698 699 def ApplyAttributes(self, attributes): 700 SE_BaseClassCore.ApplyAttributes(self, attributes) 701 self._expandIcon = Sprite(parent=self, align=uiconst.CENTERLEFT, pos=(0, 0, 18, 18)) 702 self._expandIcon.OnClick = self.ExpandUIObject 703 self._label = Label(parent=self, state=uiconst.UI_DISABLED, align=uiconst.CENTERLEFT, left=20, color=(1.0, 1.0, 1.0, 1)) 704 self._pickHilite = Frame(name='_pickHilite', bgParent=self, texturePath=RESROOT + 'hiliteBackground.png', color=(0, 1, 0, 0.3), state=uiconst.UI_HIDDEN) 705 self._hilite = Fill(name='_hilite', bgParent=self, color=(1, 1, 1, 0.125), state=uiconst.UI_HIDDEN) 706 self.loaded = False 707 708 def Load(self, node): 709 self.CheckDestroyed() 710 if self.loaded: 711 return 712 self.loaded = True 713 if isinstance(node.uiObject, Base): 714 self._label.text = node.uiObject.name or node.uiObject.__class__ 715 if node.objectLabel: 716 self._label.text = '%s: %s' % (node.objectLabel, self._label.text) 717 else: 718 self._label.italic = True 719 if node.objectLabel: 720 self._label.text = node.objectLabel 721 elif getattr(node.uiObject, 'name', None): 722 self._label.text = '%s (%s)' % (node.uiObject.name, node.uiObject.__typename__) 723 else: 724 self._label.text = node.uiObject.__typename__ 725 self._label.color = (1.0, 1.0, 1.0, 0.5) 726 self._label.left = INDENTSIZE * node.level + 20 727 if hasattr(node.uiObject, 'Reload') or 'Reload' in getattr(node.uiObject, '__methods__', []): 728 reloadIcon = Sprite(texturePath=RESROOT + 'tinyReload.png', pos=(3, 0, 16, 16), parent=self, align=uiconst.CENTERRIGHT, idx=0) 729 reloadIcon.OnClick = self.ReloadUIObject 730 if node.isExpandable: 731 if node.isExpanded: 732 self._expandIcon.texturePath = RESROOT + 'containerExpanded_down.png' 733 else: 734 self._expandIcon.texturePath = RESROOT + 'containerCollapsed.png' 735 else: 736 self._expandIcon.texturePath = RESROOT + 'nonContainer.png' 737 self._expandIcon.state = uiconst.UI_DISABLED 738 self._expandIcon.left = INDENTSIZE * node.level 739 nextNode = node.scroll.GetNode(node.idx + 1) 740 if node.connectLevels is not None: 741 for i, connectLevel in enumerate(node.connectLevels): 742 if connectLevel == node.level: 743 if not nextNode or connectLevel not in nextNode.connectLevels: 744 texturePath = RESROOT + 'connectionL.png' 745 else: 746 texturePath = RESROOT + 'connectionT.png' 747 else: 748 texturePath = RESROOT + 'connectionI.png' 749 Sprite(parent=self, texturePath=texturePath, align=uiconst.CENTERLEFT, pos=((connectLevel - 1) * INDENTSIZE, 750 0, 751 INDENTSIZE, 752 INDENTSIZE)) 753 754 def ReloadUIObject(self, *args): 755 if hasattr(self.sr.node.uiObject, 'Reload'): 756 self.CallUIObjectAndReload('Reload', True, self.sr.node.uiObject) 757 elif 'Reload' in self.sr.node.uiObject.__methods__: 758 self.CallUIObjectAndReload('Reload') 759 760 def CheckDestroyed(self): 761 if isinstance(self.sr.node.uiObject, Base) and self.sr.node.uiObject.destroyed: 762 if not getattr(self, '_destroyedIndicator', None): 763 self._destroyedIndicator = Fill(parent=self, idx=0, color=(1, 0, 0, 0.3)) 764 self.state = uiconst.UI_DISABLED 765 766 def UpdateSelected(self, selectedObject): 767 if self.sr.node.uiObject and selectedObject is self.sr.node.uiObject: 768 if not getattr(self, '_selectedSprite', None): 769 self._selectedSprite = Frame(bgParent=self, color=(0, 1, 0, 0.3), padding=(1, 0, 1, 0)) 770 self._selectedSprite.display = True 771 elif getattr(self, '_selectedSprite', None): 772 self._selectedSprite.display = False 773 774 def UpdatePickHilite(self, hilitedObject): 775 if hilitedObject and self.sr.node.uiObject: 776 wnd = UITree.GetIfOpen() 777 if hilitedObject is self.sr.node.uiObject: 778 self._pickHilite.display = True 779 self._pickHilite.spriteEffect = trinity.TR2_SFX_FILL 780 elif hilitedObject.IsUnder(self.sr.node.uiObject) and not (wnd and uicore.uilib.mouseOver.IsUnder(wnd.scroll)): 781 self._pickHilite.display = True 782 self._pickHilite.spriteEffect = trinity.TR2_SFX_COPY 783 else: 784 self._pickHilite.display = False 785 else: 786 self._pickHilite.display = False 787 788 def GetMenu(self): 789 m = MenuData() 790 if isinstance(self.sr.node.uiObject, Base): 791 if hasattr(self.sr.node.uiObject, 'Reload'): 792 m.AddEntry('Reload', lambda : self.CallUIObjectAndReload('Reload', True, self.sr.node.uiObject)) 793 if hasattr(self.sr.node.uiObject, 'Play'): 794 m.AddEntry('Play', lambda : self.CallUIObjectAndReload('Play', True)) 795 m.AddEntry('Close', lambda : self.CallUIObjectAndReload('Close'), texturePath=eveicon.close) 796 if hasattr(self.sr.node.uiObject, 'Flush'): 797 m.AddEntry('Flush', lambda : self.CallUIObjectAndReload('Flush')) 798 m.AddEntry('Move Up', lambda : self.MoveUIObject(-1)) 799 m.AddEntry('Move Down', lambda : self.MoveUIObject(1)) 800 m.AddSeparator() 801 m.AddEntry('Open In Jessica', lambda : self.OpenInJessica(self.sr.node.uiObject), texturePath=eveicon.look_at) 802 m.AddEntry('Open In Graphite', lambda : self.OpenInGraphite(self.sr.node.uiObject), texturePath=eveicon.look_at) 803 m.AddSeparator() 804 m.AddEntry('Open Class In Editor', lambda : OpenInEditor(self.sr.node.uiObject), texturePath=eveicon.open_window) 805 m.AddEntry('Copy Name', lambda : self.CopyName(self.sr.node.uiObject.name)) 806 elif self.sr.node.uiObject: 807 if 'Reload' in self.sr.node.uiObject.__methods__: 808 m.AddEntry('Reload', lambda : self.CallUIObjectAndReload('Reload')) 809 if hasattr(self.sr.node.uiObject, 'name') and self.sr.node.uiObject.name not in ('children', 'background'): 810 m.AddEntry('Open In Jessica', lambda : self.OpenInJessica(self.sr.node.uiObject)) 811 m.AddEntry('Open In Graphite', lambda : self.OpenInGraphite(self.sr.node.uiObject)) 812 return m 813 814 def CallUIObjectAndReload(self, functionName, setFocus = False, *args): 815 func = getattr(self.sr.node.uiObject, functionName, None) 816 if func: 817 returnValue = func(*args) 818 self.ReloadUITree() 819 if setFocus and returnValue: 820 wnd = UITree.GetIfOpen() 821 if wnd: 822 uthread.new(wnd.ShowUIObject, returnValue) 823 824 def MoveUIObject(self, direction): 825 uiObject = self.sr.node.uiObject 826 currentIndex = uiObject.parent.children.index(uiObject) 827 uiObject.SetParent(uiObject.parent, direction + currentIndex) 828 self.ReloadUITree() 829 830 def ReloadUITree(self): 831 wnd = GetWindowAbove(self) 832 if wnd: 833 wnd.ReloadUIRoots() 834 835 def CopySourceToClipboard(self): 836 uiObject = self.sr.node.uiObject 837 objClass = uiObject.__class__ 838 wrap = '%s = %s(\n' % (uiObject.name, uiObject.__class__.__name__) 839 defaultAttrs = [ each for each in dir(objClass) if each.startswith('default_') ] 840 for parentClass in objClass.__bases__: 841 for each in dir(parentClass): 842 if each.startswith('default_') and each not in defaultAttrs: 843 defaultAttrs.append(each) 844 845 for defaultAttr in defaultAttrs: 846 attr = defaultAttr[8:] 847 defaultAttrValue = getattr(objClass, defaultAttr) 848 objAttrValue = getattr(uiObject, attr, '__attr_notset__') 849 if objAttrValue != '__attr_notset__' and objAttrValue != defaultAttrValue: 850 if isinstance(objAttrValue, basestring): 851 wrap += ' %s="%s",\n' % (attr, objAttrValue) 852 elif attr == 'color': 853 wrap += ' %s=%s,\n' % (attr, objAttrValue.GetRGBA()) 854 elif attr == 'parent': 855 wrap += ' %s=%s,\n' % (attr, objAttrValue.name) 856 elif attr in ('state',): 857 continue 858 elif attr in COMBOPROPERTIES: 859 objAttrValue = COMBOPROPERTIES[attr].get(objAttrValue, 'Unknown value!') 860 nameSpace = COMBOPROPERTIES_NAMESPACE.get(attr, 'Unknown namespace') 861 wrap += ' %s=%s.%s,\n' % (attr, nameSpace, objAttrValue) 862 else: 863 wrap += ' %s=%s,\n' % (attr, objAttrValue) 864 865 wrap += ' )' 866 867 def ExpandUIObject(self, *args): 868 wnd = GetWindowAbove(self) 869 if wnd: 870 wnd.ToggleExpandedObject(self.sr.node.uiObject) 871 872 def OnClick(self, *args): 873 wnd = GetWindowAbove(self) 874 uiObject = self.sr.node.uiObject 875 if uicore.uilib.Key(uiconst.VK_MENU): 876 uiObject.display = not uiObject.display 877 if wnd: 878 wnd.ShowPropertiesForObject(uiObject) 879 880 def OpenInJessica(self, uiObjectOrRenderObject): 881 try: 882 import wx 883 tree = wx.FindWindowByName('TreeView2') 884 tree.AddUITree(uiObjectOrRenderObject.name, uiObjectOrRenderObject) 885 except ImportError: 886 pass 887 888 def OpenInGraphite(self, obj): 889 try: 890 from platformtools.compatibility.exposure.graphite.graphiteutils import IsGraphiteAttached, createTreeViewForItem 891 if IsGraphiteAttached(): 892 createTreeViewForItem(obj) 893 except ImportError: 894 pass 895 896 def CopyName(self, name): 897 blue.pyos.SetClipboardData(name) 898 899 def OnMouseExit(self, *args): 900 self._hilite.display = False 901 902 def OnMouseEnter(self, *args): 903 self._hilite.display = True 904 905 906 class UITreePropertyEntry(SE_BaseClassCore): 907 ENTRYHEIGHT = 18 908 909 def ApplyAttributes(self, attributes): 910 SE_BaseClassCore.ApplyAttributes(self, attributes) 911 cnt = Container(parent=self, pos=(0, 0, 0, 0), state=uiconst.UI_PICKCHILDREN) 912 self._label = eveLabel.EveLabelMedium(parent=cnt, state=uiconst.UI_DISABLED, align=uiconst.CENTERLEFT) 913 self._valueContainer = Container(parent=cnt, state=uiconst.UI_PICKCHILDREN, align=uiconst.TOALL, padLeft=VALUEOFFSET) 914 self._hilite = Fill(bgParent=self, color=(1, 1, 1, 0.125), state=uiconst.UI_HIDDEN) 915 self._mainContainer = cnt 916 917 def Load(self, node): 918 self._mainContainer.padLeft = 6 + 16 * node.level 919 self._label.text = node.propertyName 920 self._valueContainer.Flush() 921 if node.combineProperties: 922 for propertyName in node.combineProperties: 923 currentValue = GetAttrs(node.uiObject, *tuple(propertyName.split('.'))) 924 inpt = UITreeNumberInput(parent=self._valueContainer, value=currentValue, uiObject=node.uiObject, propertyName=propertyName, align=uiconst.TOLEFT) 925 926 else: 927 currentValue = GetAttrs(node.uiObject, *tuple(node.propertyName.split('.'))) 928 if isinstance(currentValue, (tuple, list)): 929 for i in xrange(len(currentValue)): 930 inpt = UITreeNumberInput(parent=self._valueContainer, value=currentValue[i], uiObject=node.uiObject, propertyName=node.propertyName, propertyIndex=i, align=uiconst.TOLEFT) 931 932 else: 933 inpt = UITreeNumberInput(parent=self._valueContainer, value=currentValue, uiObject=node.uiObject, propertyName=node.propertyName, align=uiconst.TOLEFT) 934 935 def OnMouseExit(self, *args): 936 self._hilite.display = False 937 938 def OnMouseEnter(self, *args): 939 self._hilite.display = True 940 941 def GetMenu(self): 942 m = [] 943 m.append(('Copy', self.CopyItem)) 944 m.append(('Copy value', self.CopyValue)) 945 m.append(('Copy All Properties', self.CopyAll)) 946 return m 947 948 def CopyAll(self): 949 allNodes = self.sr.node.scroll.sr.nodes 950 allCopyStrings = [] 951 for node in allNodes: 952 allCopyStrings.append(self._GetCopyString(node)) 953 954 blue.pyos.SetClipboardData('\n'.join(allCopyStrings)) 955 956 def CopyItem(self): 957 blue.pyos.SetClipboardData(self._GetCopyString(self.sr.node)) 958 959 def CopyValue(self): 960 blue.pyos.SetClipboardData(self._GetValueString(self.sr.node)) 961 962 def _GetCopyString(self, node): 963 return '%s = %s' % (node.propertyName, self._GetValueString(node)) 964 965 def _GetValueString(self, node): 966 propertyName = node.propertyName 967 currentValue = getattr(node.uiObject, propertyName, '__notfound__') 968 if propertyName in COMBOPROPERTIES: 969 objAttrValue = COMBOPROPERTIES[propertyName].get(currentValue, 'Unknown value!') 970 nameSpace = COMBOPROPERTIES_NAMESPACE.get(propertyName, 'Unknown namespace') 971 currentValue = '%s.%s' % (nameSpace, objAttrValue) 972 return str(currentValue)