skillExtractorWindow.py (32680B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\skilltrading\skillExtractorWindow.py 2 import blue 3 from carbonui import TextColor, uiconst 4 from carbonui.button.const import HEIGHT_NORMAL 5 from carbonui.control.scrollContainer import ScrollContainer 6 from carbonui.primitives.container import Container 7 from carbonui.primitives.containerAutoSize import ContainerAutoSize 8 from carbonui.primitives.frame import Frame 9 from carbonui.primitives.fill import Fill 10 from carbonui.primitives.sprite import Sprite 11 from carbonui.uianimations import animations 12 from carbonui.util.color import Color 13 import contextlib 14 from carbonui.control.buttonIcon import ButtonIcon 15 from carbonui.control.button import Button 16 from eve.client.script.ui import eveThemeColor 17 from eve.client.script.ui.control import eveLabel 18 from eve.client.script.ui.control.eveIcon import Icon 19 from eve.client.script.ui.control.eveLabel import CaptionLabel, EveCaptionMedium, EveHeaderMedium, EveLabelMedium, EveLabelSmall 20 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel 21 from carbonui.control.window import Window 22 from eve.client.script.ui.control.gauge import Gauge 23 from eve.client.script.ui.control.skillBar.controller import SkillBarController 24 from eve.client.script.ui.control.skillBar.skillBar import SkillBar 25 from eve.client.script.ui.control.skillBar.skillLevel import SkillLevel, COLOR_TRAINED, COLOR_UNTRAINED 26 from eve.client.script.ui.control.treeData import TreeData 27 from eve.client.script.ui.control.treeViewEntry import TreeViewEntry 28 from eve.client.script.ui.control.utilMenu import UtilMenu 29 from eve.client.script.ui.quickFilter import QuickFilterEdit 30 from inventorycommon.const import groupCapsule 31 from skills.client.skillController import SkillController 32 from eve.client.script.ui.shared.neocom.skillConst import COLOR_UNALLOCATED_1 33 from eve.client.script.ui.skilltrading.skillExtractorController import SkillExtractorController, SkillExtractorError, RESTRICTION_IMPLANT, RESTRICTION_CLONE_STATE, RESTRICTION_JUMP_CLONE, RESTRICTION_PI_COLONY, RESTRICTION_PI_UPGRADE, RESTRICTION_OVER_TRAINED 34 from eve.client.script.ui.util.uiComponents import ButtonEffect, Component 35 import evelink.client 36 import evetypes 37 from inventorycommon import const as invconst 38 import itertoolsext 39 import localization 40 import signals 41 import trinity 42 import uthread 43 from eveservices.menu import GetMenuService 44 COLOR_EXTRACTED = (0.871, 45 0.118, 46 0.0, 47 1.0) 48 49 class SkillExtractorWindow(Window): 50 __guid__ = 'form.SkillExtractorWindow' 51 __notifyevents__ = ['OnSessionChanged'] 52 default_width = 450 53 default_height = 700 54 default_minSize = (default_width, default_height) 55 default_windowID = 'SkillExtractorWindow' 56 default_captionLabelPath = 'UI/SkillTrading/SkillExtractorWindowCaption' 57 default_iconNum = 'res:/UI/Texture/WindowIcons/augmentations.png' 58 default_clipChildren = True 59 default_isCollapseable = False 60 default_isLightBackgroundConfigurable = False 61 default_isStackable = False 62 default_isLockable = False 63 default_isOverlayable = False 64 65 @classmethod 66 def OpenOrReload(cls, *args, **kwargs): 67 if cls.IsOpen(): 68 wnd = cls.GetIfOpen() 69 if wnd.controller.isCompleted: 70 wnd.Close() 71 wnd = cls.Open(*args, **kwargs) 72 else: 73 wnd.controller.itemID = kwargs.get('itemID') 74 wnd.Maximize() 75 else: 76 cls.Open(*args, **kwargs) 77 78 def ApplyAttributes(self, attributes): 79 super(SkillExtractorWindow, self).ApplyAttributes(attributes) 80 self.controller = SkillExtractorController(attributes.itemID) 81 self.filterSettings = SkillFilterSettings() 82 self.Layout() 83 self.Reload() 84 self.filterSettings.onUpdate.connect(self.UpdateNoContentMessage) 85 self.controller.onUpdate.connect(self.OnUpdate) 86 self.controller.onSkillListUpdate.connect(self.Reload) 87 sm.GetService('audio').SendUIEvent('st_activate_skill_extractor_play') 88 89 def Layout(self): 90 self.contentCont = Container(parent=self.GetMainArea(), align=uiconst.TOALL) 91 topCont = Container(name='topCont', parent=self.contentCont, align=uiconst.TOTOP, height=128) 92 SkillExtractorBar(parent=topCont, align=uiconst.CENTER, state=uiconst.UI_NORMAL, width=300, controller=self.controller) 93 middleCont = Container(parent=self.contentCont, align=uiconst.TOALL, padding=(8, 0, 8, 8)) 94 self.filterCont = Container(name='filterCont', parent=middleCont, align=uiconst.TOTOP, height=HEIGHT_NORMAL, opacity=0.0) 95 SkillFilterMenu(parent=self.filterCont, align=uiconst.CENTERRIGHT, settings=self.filterSettings, hint=localization.GetByLabel('UI/SkillTrading/FilterSettings')) 96 SkillFilterEdit(parent=self.filterCont, align=uiconst.CENTERRIGHT, left=20, filterSettings=self.filterSettings, isTypeField=True) 97 self.skillScroll = ScrollContainer(parent=middleCont, align=uiconst.TOALL, showUnderlay=True, opacity=0.0, padTop=4) 98 self.loadingPanel = Container(parent=middleCont, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=250, height=150) 99 LoadingWheel(parent=self.loadingPanel, align=uiconst.CENTERTOP, width=42, height=42) 100 text = localization.GetByLabel('UI/SkillTrading/LoadingSkills') 101 EveHeaderMedium(parent=self.loadingPanel, align=uiconst.TOTOP, top=50, text='<center>%s</center>' % text) 102 self.messagePanel = ContainerAutoSize(parent=self.GetMainArea(), align=uiconst.CENTER, alignMode=uiconst.TOTOP, width=300, opacity=0.0, idx=0) 103 104 def Reload(self): 105 uthread.new(self._Reload) 106 107 def _Reload(self): 108 self.AnimShowLoading() 109 self.CheckShipAndShowMessage() 110 self.entryDataList = self._GenerateSkillEntries(self.controller.skills) 111 self._FlushAndPopulateSkillScroll(self.entryDataList) 112 self.UpdateNoContentMessage() 113 self.AnimHideLoading() 114 115 def AnimShowLoading(self): 116 self.skillScroll.Disable() 117 self.filterCont.Disable() 118 animations.FadeIn(self.loadingPanel) 119 animations.FadeOut(self.skillScroll, duration=0.3) 120 animations.FadeOut(self.filterCont, duration=0.3, sleep=True) 121 122 def AnimHideLoading(self): 123 self.skillScroll.Enable() 124 self.filterCont.Enable() 125 animations.FadeOut(self.loadingPanel) 126 animations.FadeIn(self.skillScroll) 127 animations.FadeIn(self.filterCont) 128 129 def _GenerateSkillEntries(self, skills): 130 skillDataByGroupID = itertoolsext.bucket(skills, keyprojection=lambda s: evetypes.GetGroupID(s.typeID), valueprojection=lambda s: SkillEntryData(s, self.controller, self.filterSettings)) 131 groups = [ SkillGroupData(gid, self.filterSettings, children=data) for gid, data in skillDataByGroupID.iteritems() ] 132 return sorted(groups, key=lambda x: x.GetLabel().lower()) 133 134 def _FlushAndPopulateSkillScroll(self, groups): 135 self.skillScroll.Flush() 136 for groupData in groups: 137 SkillGroupEntry(parent=self.skillScroll, data=groupData, defaultExpanded=False) 138 blue.pyos.BeNice() 139 140 def OnUpdate(self): 141 self.UpdateNoContentMessage() 142 self.CheckCompletedAndShowMessage() 143 144 def UpdateNoContentMessage(self): 145 self.skillScroll.HideNoContentHint() 146 if all((data.isFiltered for data in self.entryDataList)): 147 emptyHint = localization.GetByLabel('UI/SkillTrading/NoSkillsHint') 148 self.skillScroll.ShowNoContentHint(emptyHint) 149 150 def CheckCompletedAndShowMessage(self): 151 if self.controller.isCompleted: 152 self.PrepareCompleteMessage() 153 self.AnimShowMessage() 154 155 def PrepareCompleteMessage(self): 156 self.messagePanel.Flush() 157 text = localization.GetByLabel('UI/SkillTrading/CompleteMessageCaption') 158 EveCaptionMedium(parent=self.messagePanel, align=uiconst.TOTOP, text='<center>%s</center>' % text) 159 text = localization.GetByLabel('UI/SkillTrading/CompleteMessageMain', amount=self.controller.SKILL_POINT_GOAL, injector=invconst.typeSkillInjector) 160 EveLabelMedium(parent=self.messagePanel, align=uiconst.TOTOP, top=4, text='<center>%s</center>' % text) 161 iconCont = Container(parent=self.messagePanel, align=uiconst.TOTOP, top=8, height=64) 162 Icon(parent=iconCont, align=uiconst.CENTER, left=-40, typeID=invconst.typeSkillInjector, size=64, state=uiconst.UI_DISABLED) 163 Sprite(parent=iconCont, align=uiconst.CENTER, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/classes/skilltrading/arrow_right.png', width=32, height=32, opacity=0.6) 164 Sprite(parent=iconCont, align=uiconst.CENTER, state=uiconst.UI_DISABLED, left=40, texturePath='res:/UI/Texture/WindowIcons/itemHangar.png', width=64, height=64) 165 text = localization.GetByLabel('UI/SkillTrading/CompleteMessageTail', injector=invconst.typeSkillInjector) 166 EveLabelMedium(parent=self.messagePanel, align=uiconst.TOTOP, top=8, text='<center>%s</center>' % text) 167 buttonCont = Container(parent=self.messagePanel, align=uiconst.TOTOP, top=16, height=40) 168 Button(parent=buttonCont, align=uiconst.CENTER, label=localization.GetByLabel('UI/Common/Done'), func=self.Close) 169 170 def CheckShipAndShowMessage(self): 171 if self.controller.isCompleted: 172 return 173 ship = sm.GetService('godma').GetItem(session.shipid) 174 if ship.groupID != groupCapsule: 175 self.PrepareShipMessage() 176 self.AnimShowMessage() 177 else: 178 self.AnimHideMessage() 179 180 def PrepareShipMessage(self): 181 self.messagePanel.Flush() 182 EveCaptionMedium(parent=self.messagePanel, align=uiconst.TOTOP, text='<center>%s</center>' % localization.GetByLabel('UI/SkillTrading/ShipMessageHeader')) 183 EveLabelMedium(parent=self.messagePanel, align=uiconst.TOTOP, top=4, text='<center>%s</center>' % localization.GetByLabel('UI/SkillTrading/ShipMessage'), color=TextColor.SECONDARY) 184 buttonCont = Container(parent=self.messagePanel, align=uiconst.TOTOP, top=16, height=40) 185 Button(parent=buttonCont, align=uiconst.CENTER, label=localization.GetByLabel('UI/SkillTrading/LeaveShipButtonLabel'), func=self.LeaveShip, args=()) 186 187 def AnimShowMessage(self): 188 self.contentCont.state = uiconst.UI_DISABLED 189 animations.FadeTo(self.contentCont, startVal=self.contentCont.opacity, endVal=0.1) 190 animations.FadeIn(self.messagePanel) 191 192 def AnimHideMessage(self): 193 self.contentCont.state = uiconst.UI_PICKCHILDREN 194 animations.FadeTo(self.contentCont, startVal=self.contentCont.opacity, endVal=1.0) 195 animations.FadeOut(self.messagePanel) 196 197 def Close(self, *args, **kwargs): 198 super(SkillExtractorWindow, self).Close(*args, **kwargs) 199 self.controller.Close() 200 201 def OnSessionChanged(self, isRemote, sess, change): 202 if 'stationid' in change: 203 self.Close() 204 elif 'shipid' in change: 205 self.CheckShipAndShowMessage() 206 207 def LeaveShip(self): 208 ship = sm.GetService('godma').GetItem(session.shipid) 209 sm.StartService('station').TryLeaveShip(ship) 210 self.AnimHideMessage() 211 212 213 class SkillFilterEdit(QuickFilterEdit): 214 215 def ApplyAttributes(self, attributes): 216 super(SkillFilterEdit, self).ApplyAttributes(attributes) 217 self.filterSettings = attributes.filterSettings 218 219 def ReloadFunction(self): 220 self.filterSettings.SetNameFilter(self.quickFilterInput) 221 222 223 class SkillFilterSettings(object): 224 SHOW_SKILLS_KEY = 'skillExtractorWindow_showSkills' 225 HIDE_LOCKED_SKILLS_KEY = 'skillExtractorWindow_hideLockedSkills' 226 HIDE_EMPTY_SKILLS_KEY = 'skillExtractorWindow_hideEmptySkills' 227 SHOW_ALL_SKILLS = 'all' 228 SHOW_EXTRACTED_SKILLS = 'extracted' 229 OPTION_DEFAULTS = {SHOW_SKILLS_KEY: SHOW_ALL_SKILLS, 230 HIDE_LOCKED_SKILLS_KEY: False, 231 HIDE_EMPTY_SKILLS_KEY: False} 232 233 def __init__(self): 234 self.onUpdate = signals.Signal(signalName='onUpdate6') 235 self._nameFilter = '' 236 237 def GetNameFilter(self): 238 return self._nameFilter 239 240 def SetNameFilter(self, name): 241 current = self._nameFilter 242 self._nameFilter = name.strip().lower() if name is not None else '' 243 if current != self._nameFilter: 244 self.onUpdate() 245 246 def IsShowAllSkills(self): 247 return self.GetOption(self.SHOW_SKILLS_KEY) == self.SHOW_ALL_SKILLS 248 249 def SetShowAllSkills(self): 250 self.SetOption(self.SHOW_SKILLS_KEY, self.SHOW_ALL_SKILLS) 251 252 def IsShowExtractedSkills(self): 253 return self.GetOption(self.SHOW_SKILLS_KEY) == self.SHOW_EXTRACTED_SKILLS 254 255 def SetShowExtractedSkills(self): 256 self.SetOption(self.SHOW_SKILLS_KEY, self.SHOW_EXTRACTED_SKILLS) 257 258 def IsHideLockedSkills(self): 259 return self.GetOption(self.HIDE_LOCKED_SKILLS_KEY) 260 261 def ToggleHideLockedSkills(self): 262 self.ToggleOption(self.HIDE_LOCKED_SKILLS_KEY) 263 264 def IsHideEmptySkills(self): 265 return self.GetOption(self.HIDE_EMPTY_SKILLS_KEY) 266 267 def ToggleHideEmptySkills(self): 268 self.ToggleOption(self.HIDE_EMPTY_SKILLS_KEY) 269 270 def GetOption(self, option): 271 return settings.user.ui.Get(option, self.OPTION_DEFAULTS[option]) 272 273 def SetOption(self, option, value): 274 settings.user.ui.Set(option, value) 275 self.onUpdate() 276 277 def ToggleOption(self, option): 278 current = self.GetOption(option) 279 self.SetOption(option, not current) 280 281 282 class SkillFilterMenu(UtilMenu): 283 default_texturePath = 'res:/UI/Texture/SettingsCogwheel.png' 284 default_width = 16 285 default_height = 16 286 default_iconSize = 18 287 default_menuAlign = uiconst.TOPLEFT 288 289 def ApplyAttributes(self, attributes): 290 attributes.GetUtilMenu = self.GetSkillFilterMenu 291 super(SkillFilterMenu, self).ApplyAttributes(attributes) 292 self.settings = attributes.settings 293 294 def GetSkillFilterMenu(self, menu): 295 menu.AddRadioButton(text=localization.GetByLabel('UI/SkillTrading/ShowAllSkills'), checked=self.settings.IsShowAllSkills(), callback=self.settings.SetShowAllSkills) 296 menu.AddRadioButton(text=localization.GetByLabel('UI/SkillTrading/ShowExtractedSkills'), checked=self.settings.IsShowExtractedSkills(), callback=self.settings.SetShowExtractedSkills) 297 menu.AddDivider() 298 menu.AddCheckBox(text=localization.GetByLabel('UI/SkillTrading/HideLockedSkills'), checked=self.settings.IsHideLockedSkills(), callback=self.settings.ToggleHideLockedSkills) 299 menu.AddCheckBox(text=localization.GetByLabel('UI/SkillTrading/HideEmptySkills'), checked=self.settings.IsHideEmptySkills(), callback=self.settings.ToggleHideEmptySkills) 300 301 302 class SkillExtractorBar(ContainerAutoSize): 303 304 def ApplyAttributes(self, attributes): 305 super(SkillExtractorBar, self).ApplyAttributes(attributes) 306 self.controller = attributes.controller 307 self.Layout() 308 self.Update() 309 self.controller.onUpdate.connect(self.Update) 310 311 def Layout(self): 312 gaugeCont = Container(parent=self, align=uiconst.TOTOP, height=48) 313 Frame(parent=gaugeCont, cornerSize=3, opacity=0.5, color=eveThemeColor.THEME_FOCUS) 314 self.gaugeBG = Fill(bgParent=gaugeCont, align=uiconst.TOALL, color=eveThemeColor.THEME_FOCUSDARK, opacity=0.2) 315 self.extractButton = ExtractButton(parent=gaugeCont, align=uiconst.TOALL, controller=self.controller) 316 self.gauge = Gauge(parent=gaugeCont, align=uiconst.TOTOP, state=uiconst.UI_DISABLED, height=gaugeCont.height - 2, gaugeHeight=gaugeCont.height - 2, top=1, color=COLOR_UNALLOCATED_1, backgroundColor=(1.0, 1.0, 0.0, 0.0)) 317 self.noProgressHint = eveLabel.EveCaptionSmall(parent=gaugeCont, align=uiconst.CENTER, width=self.width, text=localization.GetByLabel('UI/SkillTrading/DropToExtractHint')) 318 self.amountLabel = ExtractorBarAmountLabel(parent=self, align=uiconst.TOTOP, padTop=4, goal=self.controller.SKILL_POINT_GOAL) 319 320 def Update(self): 321 self.gauge.SetValue(self.controller.progress) 322 self.amountLabel.AnimSetAmount(self.controller.extractedPoints) 323 opacity = 0.0 if self.controller.progress > 0.0 else TextColor.HIGHLIGHT.opacity 324 animations.FadeTo(self.noProgressHint, startVal=self.noProgressHint.opacity, endVal=opacity) 325 target = 0.5 - self.controller.progress if self.controller.progress > 0.0 else 0.0 326 animations.MorphVector2(self.gaugeBG, 'translationPrimary', startVal=self.gaugeBG.translationPrimary, endVal=(target, 0.0)) 327 328 def OnDropData(self, source, data): 329 for entry in data: 330 skillID = self._GetDropDataSkillID(entry) 331 if skillID is not None: 332 try: 333 self.controller.ExtractSkill(skillID) 334 sm.GetService('audio').SendUIEvent('st_select_skills_play') 335 except SkillExtractorError: 336 pass 337 338 def _GetDropDataSkillID(self, data): 339 for attr in ('skillID', 'typeID', 'invtype'): 340 skillID = getattr(data, attr, None) 341 if skillID is not None: 342 return skillID 343 344 345 @Component(ButtonEffect(bgElementFunc=lambda parent, _: parent.frame, opacityIdle=1.0, opacityHover=0.6, opacityMouseDown=0.2)) 346 347 class ExtractButton(Container): 348 default_opacity = 0.0 349 350 def ApplyAttributes(self, attributes): 351 super(ExtractButton, self).ApplyAttributes(attributes) 352 self.controller = attributes.controller 353 self._processing = False 354 self.controller.onUpdate.connect(self.Update) 355 self.Layout() 356 self.Update() 357 358 def Layout(self): 359 self.frame = Frame(bgParent=self, texturePath='res:/UI/Texture/Classes/Industry/CenterBar/buttonBg.png', cornerSize=5, color=Color.GRAY2) 360 self.arrows = Sprite(bgParent=self, texturePath='res:/UI/Texture/Classes/Industry/CenterBar/arrowMask.png', textureSecondaryPath='res:/UI/Texture/Classes/Industry/CenterBar/arrows.png', translationSecondary=(-0.16, 0), spriteEffect=trinity.TR2_SFX_MODULATE, color=Color.GRAY2, opacity=0.2) 361 animations.MorphVector2(self.arrows, 'translationSecondary', startVal=(0.16, 0.0), endVal=(-0.16, 0.0), duration=2.0, curveType=uiconst.ANIM_LINEAR, loops=uiconst.ANIM_REPEAT) 362 self.label = CaptionLabel(parent=self, align=uiconst.CENTER, text=localization.GetByLabel('UI/SkillTrading/Extract')) 363 self.spinner = LoadingWheel(parent=self, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=30, height=30, opacity=0.0) 364 365 @property 366 def isReady(self): 367 return self.controller.progress >= 1.0 and not self.controller.isCompleted 368 369 @property 370 def isProcessing(self): 371 return self._processing 372 373 def Update(self): 374 if self.controller.progress < 1.0: 375 self.AnimHide() 376 else: 377 self.AnimShow() 378 379 def AnimShow(self): 380 self.Enable() 381 animations.FadeIn(self, timeOffset=0.3, duration=0.5) 382 383 def AnimHide(self): 384 self.Disable() 385 animations.FadeOut(self, duration=0.2) 386 387 def OnClick(self, *args): 388 uthread.new(self._ProcessOnClick) 389 390 def _ProcessOnClick(self): 391 if not self.isReady or self.isProcessing: 392 return 393 with self.processing(): 394 self.controller.Commit() 395 sm.GetService('audio').SendUIEvent('st_confirmed_extraction_play') 396 397 @contextlib.contextmanager 398 def processing(self): 399 try: 400 self._processing = True 401 self._AnimShowProcessing() 402 yield 403 finally: 404 self._processing = False 405 self._AnimHideProcessing() 406 407 def _AnimShowProcessing(self): 408 animations.FadeOut(self.label) 409 animations.FadeIn(self.spinner) 410 411 def _AnimHideProcessing(self): 412 animations.FadeIn(self.label) 413 animations.FadeOut(self.spinner) 414 415 416 class ExtractorBarAmountLabel(Container): 417 default_height = 17 418 419 def ApplyAttributes(self, attributes): 420 super(ExtractorBarAmountLabel, self).ApplyAttributes(attributes) 421 self.goal = attributes.goal 422 self._amount = attributes.get('amount', 0) 423 self.Layout() 424 425 def Layout(self): 426 Frame(parent=self, opacity=0.1) 427 Sprite(bgParent=self, align=uiconst.TOALL, padding=(4, 4, 4, 4), texturePath='res:/UI/Texture/classes/skilltrading/checker_bg.png', opacity=0.4, tileX=True, tileY=True) 428 self.label = EveLabelSmall(parent=self, align=uiconst.CENTER) 429 self.UpdateLabel() 430 431 @property 432 def amount(self): 433 return self._amount 434 435 @amount.setter 436 def amount(self, value): 437 self._amount = int(value) 438 self.UpdateLabel() 439 440 def AnimSetAmount(self, amount): 441 animations.MorphScalar(self, 'amount', startVal=self.amount, endVal=amount, callback=lambda : setattr(self, 'amount', amount)) 442 443 def UpdateLabel(self): 444 text = localization.GetByLabel('UI/SkillTrading/ExtractionProgressAndTotal', current=self.amount, total=self.goal) 445 self.label.SetText(text) 446 447 448 class SkillEntryData(TreeData): 449 450 def __init__(self, skill, controller, filterSettings, **kw): 451 super(SkillEntryData, self).__init__(**kw) 452 self.skill = skill 453 self.controller = controller 454 self.filterSettings = filterSettings 455 456 @property 457 def typeID(self): 458 return self.skill.typeID 459 460 @property 461 def name(self): 462 return evetypes.GetName(self.typeID) 463 464 @property 465 def isFiltered(self): 466 showExtracted = self.filterSettings.IsShowExtractedSkills() 467 hideLocked = self.filterSettings.IsHideLockedSkills() 468 hideEmpty = self.filterSettings.IsHideEmptySkills() 469 nameFilter = self.filterSettings.GetNameFilter() 470 if showExtracted and self.skill.points == self.skill.unmodifiedPoints: 471 return True 472 if nameFilter not in self.name.lower(): 473 return True 474 if hideLocked and self.isLocked: 475 return True 476 if hideEmpty and self.skill.unmodifiedPoints == 0: 477 return True 478 return False 479 480 @property 481 def isLocked(self): 482 return self.skill.IsLocked() 483 484 @property 485 def isExtracted(self): 486 return self.skill.points != self.skill.unmodifiedPoints 487 488 def ExtractSkill(self): 489 if not self.isLocked: 490 self.controller.ExtractSkill(self.typeID) 491 492 def Undo(self): 493 self.controller.Revert(self.typeID) 494 495 def GetLabel(self): 496 color = (1.0, 1.0, 1.0, 0.8) 497 if self.skill.points != self.skill.unmodifiedPoints: 498 color = COLOR_EXTRACTED 499 text = localization.GetByLabel('UI/SkillTrading/SkillEntryLabel', typeID=self.typeID, level=self.skill.level, rank=self.skill.rank, points=self.skill.points, color=Color.RGBtoHex(*color)) 500 return text 501 502 def GetHint(self): 503 if self.skill.isRestricted: 504 return self._GetRestrictedSkillHint() 505 if self.skill.isRequired: 506 return self._GetRequiredSkillHint() 507 if self.skill.isQueued: 508 return self._GetQueuedSkillHint() 509 510 def _GetRestrictedSkillHint(self): 511 if self.skill.restrictionReason is None: 512 return 513 LABEL_BY_RESTRICTION = {RESTRICTION_IMPLANT: 'UI/SkillTrading/SkillRestrictionImplants', 514 RESTRICTION_CLONE_STATE: 'UI/SkillTrading/SkillRestrictionCloneState', 515 RESTRICTION_JUMP_CLONE: 'UI/SkillTrading/SkillRestrictionJumpClones', 516 RESTRICTION_PI_COLONY: 'UI/SkillTrading/SkillRestrictionInterplanetaryConsolidation', 517 RESTRICTION_PI_UPGRADE: 'UI/SkillTrading/SkillRestrictionCommandCenterUpgrade', 518 RESTRICTION_OVER_TRAINED: 'UI/SkillTrading/SkillRestrictionOverTrained'} 519 label = LABEL_BY_RESTRICTION[self.skill.restrictionReason] 520 return localization.GetByLabel(label) 521 522 def _GetRequiredSkillHint(self): 523 hint = localization.GetByLabel('UI/SkillTrading/RequiredSkillHintBody') 524 dependentSkills = self._GetDependentSkills() 525 skillNames = sorted((evetypes.GetName(s) for s in dependentSkills)) 526 showCount = 5 527 rest = len(skillNames) - showCount 528 if rest == 1: 529 showCount += 1 530 for skill in skillNames[:showCount]: 531 hint += '<br>%s' % skill 532 533 if rest > 1: 534 hint += localization.GetByLabel('UI/SkillTrading/RequiredSkillHintTrail', rest=rest) 535 return hint 536 537 def _GetDependentSkills(self): 538 mySkills = [ s.typeID for s in self.controller.skills ] 539 dependentSkills = [] 540 for dependentSkillID, requiredLevel in self.skill.dependencies.iteritems(): 541 if dependentSkillID not in mySkills: 542 continue 543 dependentSkill = self.controller.skillsByID[dependentSkillID] 544 if dependentSkill.points == 0: 545 continue 546 if self.skill.level <= requiredLevel: 547 dependentSkills.append(dependentSkillID) 548 549 return dependentSkills 550 551 def _GetQueuedSkillHint(self): 552 return localization.GetByLabel('UI/SkillTrading/QueuedSkillHint') 553 554 def GetDragData(self): 555 if self.isLocked: 556 return None 557 return [SkillDragData(self.typeID)] 558 559 560 class SkillGroupData(TreeData): 561 562 def __init__(self, groupID, filterSettings, **kw): 563 label = kw.pop('label', evetypes.GetGroupNameByGroup(groupID)) 564 super(SkillGroupData, self).__init__(label=label, **kw) 565 self.filterSettings = filterSettings 566 self.onChildUpdated = signals.Signal(signalName='onChildUpdated') 567 for child in self.GetChildren(): 568 child.skill.onUpdate.connect(self.onChildUpdated) 569 570 @property 571 def isFiltered(self): 572 return all((child.isFiltered for child in self.GetChildren())) 573 574 def GetChildren(self): 575 children = super(SkillGroupData, self).GetChildren() 576 return sorted(children, key=lambda x: evetypes.GetName(x.typeID).lower()) 577 578 579 class SkillTreeEntry(TreeViewEntry): 580 default_height = 26 581 tooltipPointer = uiconst.POINT_RIGHT_2 582 583 def ApplyAttributes(self, attributes): 584 super(SkillTreeEntry, self).ApplyAttributes(attributes) 585 self.modifiedBG = None 586 self.lockedIcon = None 587 self.undoButton = None 588 levelBarCont = Container(parent=self.topRightCont, align=uiconst.TOLEFT, width=70, left=18) 589 self.levelBar = SkillBar(parent=levelBarCont, align=uiconst.CENTER, state=uiconst.UI_DISABLED, controller=ExtractorSkillBarController(self.data.skill), levelClass=ExtractorSkillLevel) 590 self.OnSkillUpdated() 591 self.data.filterSettings.onUpdate.connect(self.UpdateVisibility) 592 self.data.skill.onUpdate.connect(self.OnSkillUpdated) 593 594 def UpdateAlignment(self, *args, **kwargs): 595 alignment = super(SkillTreeEntry, self).UpdateAlignment(*args, **kwargs) 596 self.UpdateLabelRightFade() 597 return alignment 598 599 def UpdateLabelRightFade(self): 600 availableWidth = self.displayWidth - self.label.left 601 self.label.SetRightAlphaFade(fadeEnd=availableWidth, maxFadeWidth=20) 602 603 def UpdateBackground(self): 604 if self.data.skill.points != self.data.skill.unmodifiedPoints: 605 self.CheckConstructModifiedBG() 606 self.modifiedBG.opacity = 0.1 607 elif self.modifiedBG: 608 self.modifiedBG.opacity = 0.0 609 610 def CheckConstructModifiedBG(self): 611 if self.modifiedBG is None: 612 self.modifiedBG = Fill(bgParent=self, color=COLOR_UNALLOCATED_1, opacity=0.0, padBottom=1) 613 614 def Extract(self): 615 if self.data.isLocked: 616 return 617 try: 618 self.data.ExtractSkill() 619 sm.GetService('audio').SendUIEvent('st_select_skills_play') 620 except SkillExtractorError: 621 pass 622 623 def UpdateUndoButton(self): 624 self.CheckConstructUndoButton() 625 if self.data.isExtracted: 626 self.undoButton.Enable() 627 animations.MorphScalar(self.undoButton, 'left', startVal=self.undoButton.left, endVal=4, duration=0.3) 628 else: 629 self.undoButton.Disable() 630 animations.MorphScalar(self.undoButton, 'left', startVal=self.undoButton.left, endVal=-self.undoButton.width, duration=0.3) 631 632 def CheckConstructUndoButton(self): 633 if self.undoButton is None: 634 self.undoButton = ButtonIcon(parent=self.topRightCont, align=uiconst.CENTERRIGHT, left=0, width=17, height=17, iconSize=17, texturePath='res:/UI/Texture/classes/skilltrading/undo.png', func=self.Undo, hint=localization.GetByLabel('UI/SkillTrading/Undo')) 635 636 def Undo(self): 637 self.data.Undo() 638 639 def GetDragData(self): 640 return self.data.GetDragData() 641 642 def GetSpacerContWidth(self): 643 return 88 644 645 def GetMenu(self): 646 m = GetMenuService().GetMenuFromItemIDTypeID(None, self.data.typeID) 647 extractText = localization.GetByLabel('UI/SkillTrading/ExtractSkill') 648 m.append((extractText, self.Extract, ())) 649 return m 650 651 def UpdateLabel(self): 652 super(SkillTreeEntry, self).UpdateLabel() 653 self.label.SetText(self.data.GetLabel()) 654 655 def UpdateVisibility(self): 656 self.display = not self.data.isFiltered 657 658 def UpdateLockedStatus(self): 659 opacity = 0.4 if self.data.isLocked else 1.0 660 animations.FadeTo(self.label, startVal=self.opacity, endVal=opacity) 661 if self.data.isLocked: 662 self.CheckConstructLockIcon() 663 animations.FadeIn(self.lockedIcon, duration=0.3) 664 elif self.lockedIcon is not None: 665 animations.FadeOut(self.lockedIcon, duration=0.3) 666 667 def CheckConstructLockIcon(self): 668 if self.lockedIcon is None: 669 self.lockedIcon = Sprite(parent=self.topRightCont, align=uiconst.CENTERLEFT, state=uiconst.UI_DISABLED, ignoreSize=True, texturePath='res:/UI/Texture/classes/skilltrading/lock.png', left=3, width=14, height=14, color=Color.GRAY3, opacity=0.0) 670 671 def OnDblClick(self): 672 self.Extract() 673 674 def OnSkillUpdated(self): 675 self.UpdateVisibility() 676 self.UpdateLabel() 677 self.UpdateBackground() 678 self.UpdateLockedStatus() 679 self.UpdateUndoButton() 680 681 682 class SkillGroupEntry(TreeViewEntry): 683 684 def ApplyAttributes(self, attributes): 685 super(SkillGroupEntry, self).ApplyAttributes(attributes) 686 self.UpdateVisibility() 687 self.data.filterSettings.onUpdate.connect(self.UpdateVisibility) 688 self.data.onChildUpdated.connect(self.UpdateVisibility) 689 690 def GetTreeViewEntryClassByTreeData(self, data): 691 return SkillTreeEntry 692 693 def ShowChild(self, child, show = True): 694 if show: 695 show = not child.data.isFiltered 696 super(SkillGroupEntry, self).ShowChild(child, show=show) 697 698 def UpdateVisibility(self): 699 self.display = not self.data.isFiltered 700 701 def OnClick(self, *args): 702 self.ToggleChildren() 703 704 705 class SkillDragData(object): 706 __guid__ = None 707 708 def __init__(self, typeID): 709 self.typeID = typeID 710 711 def get_link(self): 712 return evelink.type_link(self.typeID) 713 714 def LoadIcon(self, icon, parent, size): 715 icon.LoadIconByTypeID(self.typeID, size=size) 716 717 718 class ExtractorSkillBarController(SkillBarController): 719 720 def __init__(self, skill): 721 self._skill = skill 722 super(ExtractorSkillBarController, self).__init__(SkillController(skill.typeID)) 723 self._skill.onUpdate.connect(self.onUpdate) 724 725 @property 726 def isExtracted(self): 727 return self._skill.points != self._skill.unmodifiedPoints 728 729 def IsLevelExtracted(self, level): 730 return self._skill.level < level <= self._skill.unmodifiedLevel 731 732 def IsPartiallyTrained(self, level): 733 return self._skill.progress > 0 and self._skill.level + 1 == level 734 735 @property 736 def isRestricted(self): 737 return False 738 739 @property 740 def restrictedLevel(self): 741 return 5 742 743 744 class ExtractorSkillLevel(SkillLevel): 745 746 def Layout(self): 747 if self.isPartiallyTrained and self.skill.isExtracted: 748 self.AddBox(COLOR_UNTRAINED) 749 self.AddPartiallyTrained(COLOR_EXTRACTED) 750 elif self.skill.IsLevelExtracted(self.level): 751 self.AddBox(COLOR_EXTRACTED) 752 if self.skill.IsPartiallyTrained(self.level): 753 self.AddPartiallyTrained(COLOR_TRAINED) 754 else: 755 super(ExtractorSkillLevel, self).Layout() 756 757 758 def __SakeReloadHook(): 759 import log 760 try: 761 wnd = SkillExtractorWindow.GetIfOpen() 762 if wnd is not None: 763 itemID = wnd.controller.itemID 764 SkillExtractorWindow.CloseIfOpen() 765 SkillExtractorWindow.Open(itemID=itemID, useDefaultPos=True) 766 except Exception: 767 log.LogException()