NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

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()