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

startSequencingPanel.py (20841B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\cosmetics\ship\pages\sequence\startSequencingPanel.py
      2 import eveicon
      3 import uthread2
      4 from carbonui import Align, AxisAlignment, Density, PickState
      5 from carbonui import ButtonVariant, ButtonStyle
      6 from carbonui import TextColor
      7 from carbonui.text.styles import TextHeader, TextBody
      8 from carbonui.button.group import ButtonGroup, ButtonSizeMode
      9 from carbonui.control.button import Button
     10 from carbonui.control.scrollContainer import ScrollContainer
     11 from carbonui.control.singlelineedits.singleLineEditInteger import SingleLineEditInteger
     12 from carbonui.primitives.container import Container
     13 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     14 from carbonui.primitives.fill import Fill
     15 from carbonui.primitives.sprite import Sprite
     16 from carbonui.uianimations import animations
     17 from characterskills.client import skill_signals
     18 from cosmetics.client.shipSkinSequencingSvc import get_ship_skin_sequencing_svc
     19 from cosmetics.client.ships import ship_skin_signals
     20 from cosmetics.client.ships.qa.settings import is_sequencing_button_always_enabled
     21 from cosmetics.client.ships.skins.errors import SEQUENCING_ERROR_TEXT_BY_CODE
     22 from cosmetics.client.ships.skins.live_data import current_skin_design, current_skin_design_signals
     23 from cosmetics.common.cosmeticsConst import SEQUENCE_BINDER_TYPES
     24 from cosmetics.common.ships.skins import sequencing_util
     25 from cosmetics.common.ships.skins.live_data.component_license_type import ComponentLicenseType
     26 from cosmetics.common.ships.skins.util import Currency
     27 from eve.client.script.ui import eveColor, eveThemeColor
     28 from player_messaging.client.ui_message import message_player
     29 from eve.client.script.ui.cosmetics.ship.const import SkinrPage
     30 from eve.client.script.ui.cosmetics.ship.controls.costInfo import SequencingPanelCostInfo
     31 from eve.client.script.ui.cosmetics.ship.controls.decoratedLine import DecoratedLine
     32 from eve.client.script.ui.cosmetics.ship.controls.sequencingTime import SequencingTime
     33 from eve.client.script.ui.cosmetics.ship.pages import current_page
     34 from eve.client.script.ui.cosmetics.ship.pages.sequence import sequence_ui_signals
     35 from eve.client.script.ui.cosmetics.ship.pages.sequence.sequenceBinderEntries import SequenceBinderCostEntry, UnlimitedComponentLicenseEntry, LimitedComponentLicenseEntry
     36 from eve.client.script.ui.cosmetics.ship.pages.sequence.tooltipInfoIcons import NumRunsInfoIcon
     37 from localization import GetByLabel
     38 from plex.client.api.signal import on_balance_changed
     39 from publicGateway.grpc.exceptions import GenericException
     40 from skills.client.util import get_skill_service
     41 from stackless_response_router.exceptions import TimeoutException
     42 
     43 class StartSequencingPanel(Container):
     44     default_width = 350
     45 
     46     def __init__(self, **kw):
     47         super(StartSequencingPanel, self).__init__(**kw)
     48         self.unlimited_sequence_binder_entries = []
     49         self.limited_sequence_binder_entries = []
     50         self.sequencing_time = None
     51         self.cost_info = None
     52         self.num_skins_edit = None
     53         self.start_btn = None
     54         self.unlimited_use_entry_container = None
     55         self.limited_use_entry_container = None
     56         self._update_values_thread = None
     57         self._set_num_runs_thread = None
     58         self.construct_bottom_buttons()
     59         self.construct_cost_info_container()
     60         self.construct_sequencing_time_container()
     61         self.construct_content_containers()
     62         self.connnect_signals()
     63 
     64     def Close(self):
     65         try:
     66             self.kill_threads()
     67             self.disconnect_signals()
     68         finally:
     69             super(StartSequencingPanel, self).Close()
     70 
     71     def kill_threads(self):
     72         self.kill_update_values_thread()
     73         self.kill_set_num_runs_thread()
     74 
     75     def kill_update_values_thread(self):
     76         if self._update_values_thread:
     77             self._update_values_thread.kill()
     78             self._update_values_thread = None
     79 
     80     def kill_set_num_runs_thread(self):
     81         if self._set_num_runs_thread:
     82             self._set_num_runs_thread.kill()
     83             self._set_num_runs_thread = None
     84 
     85     def connnect_signals(self):
     86         on_balance_changed.connect(self._on_plex_balance_changed)
     87         skill_signals.on_skill_levels_trained.connect(self.on_skill_levels_trained)
     88         sequence_ui_signals.on_num_skins_changed.connect(self.on_num_skins_changed)
     89         sequence_ui_signals.on_sequence_binders_changed.connect(self.on_sequence_binders_changed)
     90         current_skin_design_signals.on_name_changed.connect(self.on_current_skin_design_name_changed)
     91         current_skin_design_signals.on_component_instance_license_to_use_changed.connect(self.on_component_instance_license_to_use_changed)
     92         ship_skin_signals.on_component_license_granted.connect(self.on_component_license_granted)
     93         ship_skin_signals.on_skin_sequencing_cache_invalidated.connect(self.on_skin_sequencing_cache_invalidated)
     94         is_sequencing_button_always_enabled.on_change.connect(self._on_mock_sequencing_button_changed)
     95         self.on_size_changed.connect(self.on_size_changed_signal)
     96 
     97     def disconnect_signals(self):
     98         on_balance_changed.disconnect(self._on_plex_balance_changed)
     99         skill_signals.on_skill_levels_trained.disconnect(self.on_skill_levels_trained)
    100         sequence_ui_signals.on_num_skins_changed.disconnect(self.on_num_skins_changed)
    101         sequence_ui_signals.on_sequence_binders_changed.disconnect(self.on_sequence_binders_changed)
    102         current_skin_design_signals.on_name_changed.disconnect(self.on_current_skin_design_name_changed)
    103         current_skin_design_signals.on_component_instance_license_to_use_changed.disconnect(self.on_component_instance_license_to_use_changed)
    104         ship_skin_signals.on_component_license_granted.disconnect(self.on_component_license_granted)
    105         ship_skin_signals.on_skin_sequencing_cache_invalidated.disconnect(self.on_skin_sequencing_cache_invalidated)
    106         is_sequencing_button_always_enabled.on_change.disconnect(self._on_mock_sequencing_button_changed)
    107         self.on_size_changed.disconnect(self.on_size_changed_signal)
    108 
    109     def load_panel(self):
    110         current_skin_design.get().clean_up_skin()
    111         self.reconstruct_layout()
    112         self.update_values()
    113         self.anim_entry()
    114 
    115     def anim_entry(self):
    116         self.content.opacity = 0.0
    117         animations.MorphScalar(self, 'left', -200, 0, duration=0.6)
    118         animations.FadeTo(self.content, 0.0, 1.0, duration=0.3, timeOffset=0.6)
    119 
    120     def reconstruct_layout(self):
    121         self.content.Flush()
    122         self.construct_layout()
    123         self.on_size_changed_signal()
    124 
    125     def construct_content_containers(self):
    126         outer_container = Container(name='outer_container', parent=self, align=Align.TOALL, padding=(0, 8, 0, 16))
    127         self.scroll_divider_top = DecoratedLine(name='scroll_divider_top', parent=outer_container, align=Align.TOTOP, color=eveThemeColor.THEME_ALERT)
    128         self.scroll_container = ScrollContainer(name='scroll_container', parent=outer_container, align=Align.TOALL, padding=(0, 16, 0, 16))
    129         self.content = ContainerAutoSize(name='content', parent=self.scroll_container, align=Align.CENTER, width=self.default_width - 16)
    130         self.scroll_divider_bottom = DecoratedLine(name='scroll_divider_bottom', parent=outer_container, align=Align.TOBOTTOM, color=eveThemeColor.THEME_ALERT)
    131 
    132     def construct_layout(self):
    133         self.construct_num_runs_input()
    134         self.construct_sections_header()
    135         self.construct_unlimited_use_section()
    136         self.construct_limited_use_section()
    137         self.construct_sequencing_time()
    138         self.construct_cost_info()
    139 
    140     def construct_num_runs_input(self):
    141         num_skins_cont = ContainerAutoSize(name='num_skins_cont', parent=self.content, align=Align.TOTOP)
    142         label_cont = ContainerAutoSize(name='label_cont', parent=num_skins_cont, align=Align.CENTERTOP, height=20)
    143         TextBody(parent=ContainerAutoSize(parent=label_cont, align=Align.TOLEFT), text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/SkinAmount'), align=Align.CENTERLEFT)
    144         NumRunsInfoIcon(parent=ContainerAutoSize(parent=label_cont, align=Align.TOLEFT, padLeft=4), align=Align.CENTERLEFT)
    145         self.num_skins_edit = SingleLineEditInteger(name='num_skins_edit', parent=num_skins_cont, align=Align.CENTERTOP, density=Density.EXPANDED, top=32, width=96, minValue=1, maxValue=sequencing_util.get_maximum_runs_per_job(get_skill_service().GetMyLevel), setvalue=get_ship_skin_sequencing_svc().get_num_runs(), OnChange=self.on_num_skins_edit)
    146 
    147     def construct_sections_header(self):
    148         sections_header_container = Container(name='sections_header_container', parent=self.content, align=Align.TOTOP, height=32, padTop=32)
    149         TextBody(parent=sections_header_container, align=Align.CENTERLEFT, color=TextColor.SECONDARY, text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/ConsumablesRequired'))
    150 
    151     def construct_unlimited_use_section(self):
    152         unlimited_use_section_container = ContainerAutoSize(name='unlimited_use_section_container', parent=self.content, align=Align.TOTOP, padTop=8)
    153         ComponentTypeHeader(name='unlimited_use_section_header', parent=unlimited_use_section_container, align=Align.TOTOP, text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/UnlimitedDesignElement'), icon=eveicon.infinity)
    154         self.unlimited_use_entry_container = ContainerAutoSize(name='unlimited_use_entry_container', parent=unlimited_use_section_container, align=Align.TOTOP)
    155         self.refresh_unlimited_use_entries()
    156 
    157     def refresh_unlimited_use_entries(self):
    158         if not self.unlimited_use_entry_container:
    159             return
    160         self.unlimited_use_entry_container.Flush()
    161         self.unlimited_sequence_binder_entries = []
    162         for type_id in SEQUENCE_BINDER_TYPES:
    163             entry = SequenceBinderCostEntry(parent=self.unlimited_use_entry_container, align=Align.TOTOP, type_id=type_id, pickState=PickState.ON, padTop=8)
    164             self.unlimited_sequence_binder_entries.append(entry)
    165             fitted_components = current_skin_design.get().get_fitted_components()
    166             for component in fitted_components:
    167                 if type_id != component.sequence_binder_type_id:
    168                     continue
    169                 component_license = component.get_component_license()
    170                 if component_license and component_license.license_type != ComponentLicenseType.UNLIMITED:
    171                     continue
    172                 if not component_license:
    173                     available_licenses = component.get_component_data().component_item_data_by_type_id.values()
    174                     limited_licenses = [ l for l in available_licenses if l.license_type == ComponentLicenseType.LIMITED ]
    175                     if len(limited_licenses) > 0:
    176                         continue
    177                 UnlimitedComponentLicenseEntry(parent=self.unlimited_use_entry_container, align=Align.TOTOP, type_id=type_id, component=component, padTop=8)
    178 
    179             if type_id != SEQUENCE_BINDER_TYPES[-1]:
    180                 DecoratedLine(parent=self.unlimited_use_entry_container, padTop=16, align=Align.TOTOP)
    181 
    182     def construct_limited_use_section(self):
    183         self.limited_use_section_container = ContainerAutoSize(name='limited_use_section_container', parent=self.content, align=Align.TOTOP, padTop=32)
    184         ComponentTypeHeader(name='limited_use_section_header', parent=self.limited_use_section_container, align=Align.TOTOP, text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/LimitedDesignElement'), icon=eveicon.limited)
    185         self.limited_use_entry_container = ContainerAutoSize(name='limited_use_entry_container', parent=self.limited_use_section_container, align=Align.TOTOP)
    186         self.refresh_limited_use_entries()
    187 
    188     def refresh_limited_use_entries(self):
    189         if not self.limited_use_entry_container:
    190             return
    191         self.limited_use_entry_container.Flush()
    192         self.limited_sequence_binder_entries = []
    193         fitted_components = current_skin_design.get().get_fitted_components()
    194         if len(fitted_components) > 0:
    195             self.limited_use_section_container.display = True
    196             for component in fitted_components:
    197                 component_license = component.get_component_license()
    198                 if component_license and component_license.license_type != ComponentLicenseType.LIMITED:
    199                     continue
    200                 if not component_license:
    201                     available_licenses = component.get_component_data().component_item_data_by_type_id.values()
    202                     limited_licenses = [ l for l in available_licenses if l.license_type == ComponentLicenseType.LIMITED ]
    203                     if len(limited_licenses) == 0:
    204                         continue
    205                 if any([ e.component.component_id == component.component_id for e in self.limited_sequence_binder_entries ]):
    206                     continue
    207                 entry = LimitedComponentLicenseEntry(parent=self.limited_use_entry_container, align=Align.TOTOP, component=component, padTop=8)
    208                 self.limited_sequence_binder_entries.append(entry)
    209 
    210         else:
    211             self.limited_use_section_container.display = False
    212 
    213     def get_components_by_sequence_binder_type(self, license_type):
    214         components_by_type = {}
    215         fitted_components = current_skin_design.get().get_fitted_components()
    216         for component in fitted_components:
    217             component_license = component.get_component_license()
    218             if component_license is None or component_license.license_type != license_type:
    219                 continue
    220             type_id = component.get_component_data().sequence_binder_type_id
    221             components_by_type[type_id] = components_by_type.get(type_id, component)
    222 
    223         return components_by_type
    224 
    225     def construct_sequencing_time_container(self):
    226         self.sequencing_time_container = Container(name='sequencing_time_container', parent=self, align=Align.TOBOTTOM, height=32, padBottom=4)
    227 
    228     def construct_sequencing_time(self):
    229         self.sequencing_time_container.Flush()
    230         self.sequencing_time = SequencingTime(name='sequencing_time', parent=self.sequencing_time_container, align=Align.TOTOP, minWidth=self.start_btn.width - 32)
    231 
    232     def construct_cost_info_container(self):
    233         self.cost_info_container = ContainerAutoSize(name='cost_info_container', parent=self, align=Align.TOBOTTOM, padBottom=4)
    234 
    235     def construct_cost_info(self):
    236         self.cost_info_container.Flush()
    237         self.cost_info = SequencingPanelCostInfo(name='cost_info', parent=self.cost_info_container, align=Align.TOBOTTOM, currency=Currency.PLEX)
    238 
    239     def construct_bottom_buttons(self):
    240         button_group = ButtonGroup(parent=self, align=Align.TOBOTTOM, button_alignment=AxisAlignment.CENTER, button_size_mode=ButtonSizeMode.STRETCH, density=Density.EXPANDED)
    241         self.start_btn = StartSequencingBtn(label=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/CreateSKIN'), func=self.on_start_sequencing_btn, variant=ButtonVariant.PRIMARY)
    242         button_group.add_button(self.start_btn)
    243 
    244     @uthread2.debounce(0.2)
    245     def update_values(self):
    246         self.kill_update_values_thread()
    247         self._update_values_thread = uthread2.start_tasklet(self._update_values_async)
    248 
    249     def _update_values_async(self):
    250         if self.destroyed:
    251             return
    252         svc = get_ship_skin_sequencing_svc()
    253         if self.sequencing_time:
    254             self.sequencing_time.update()
    255         if self.cost_info:
    256             try:
    257                 plex_cost, discount = svc.get_sequencing_plex_price(current_skin_design.get(), svc.get_num_runs())
    258             except (GenericException, TimeoutException):
    259                 message_player(GetByLabel('UI/Common/CannotConnectToServer'))
    260                 plex_cost = 0
    261 
    262             self.cost_info.set_cost(plex_cost, discount)
    263         if self.num_skins_edit:
    264             self.num_skins_edit.SetMaxValue(sequencing_util.get_maximum_runs_per_job(get_skill_service().GetMyLevel))
    265         self.refresh_unlimited_use_entries()
    266         self.refresh_limited_use_entries()
    267         if self.start_btn:
    268             self.start_btn.update()
    269 
    270     @uthread2.debounce(0.5)
    271     def on_num_skins_edit(self, *args):
    272         self.kill_set_num_runs_thread()
    273         self._set_num_runs_thread = uthread2.start_tasklet(get_ship_skin_sequencing_svc().set_num_runs, self.number_of_skins)
    274 
    275     def on_num_skins_changed(self, *args):
    276         self.update_values()
    277 
    278     def on_sequence_binders_changed(self, *args):
    279         self.update_values()
    280 
    281     def on_component_instance_license_to_use_changed(self, *args):
    282         self.update_values()
    283 
    284     def on_component_license_granted(self, *args):
    285         self.update_values()
    286 
    287     def on_skin_sequencing_cache_invalidated(self, *args):
    288         self.update_values()
    289 
    290     def on_current_skin_design_name_changed(self, *args):
    291         self.update_values()
    292 
    293     def on_skill_levels_trained(self, *args):
    294         self.update_values()
    295 
    296     def on_size_changed_signal(self, *args):
    297         if self.scroll_container:
    298             is_scrolling = self.scroll_container.IsVerticalScrollBarVisible()
    299             self.scroll_divider_top.display = is_scrolling
    300             self.scroll_divider_bottom.display = is_scrolling
    301 
    302     def _on_plex_balance_changed(self, *args):
    303         self.update_values()
    304 
    305     def on_start_sequencing_btn(self, *args):
    306         self.on_sequencing_attempt()
    307         try:
    308             error = get_ship_skin_sequencing_svc().sequence_design(current_skin_design.get())
    309             if not error:
    310                 current_skin_design.create_blank_design()
    311                 current_page.reset_history()
    312                 current_page.set_page_id(SkinrPage.STUDIO)
    313             else:
    314                 label = SEQUENCING_ERROR_TEXT_BY_CODE.get(error, None)
    315                 if label:
    316                     message_player(GetByLabel(label))
    317         finally:
    318             self.on_after_sequencing_attempt()
    319 
    320     def on_sequencing_attempt(self):
    321         if self.start_btn:
    322             self.start_btn.busy = True
    323             self.start_btn.disabled = True
    324         if self.num_skins_edit:
    325             self.num_skins_edit.Disable()
    326 
    327     def on_after_sequencing_attempt(self):
    328         if self.start_btn:
    329             self.start_btn.disabled = False
    330             self.start_btn.busy = False
    331             self.start_btn.update()
    332         if self.num_skins_edit:
    333             self.num_skins_edit.Enable()
    334 
    335     def _on_mock_sequencing_button_changed(self, *args):
    336         self.on_after_sequencing_attempt()
    337 
    338     @property
    339     def number_of_skins(self):
    340         return self.num_skins_edit.GetValue()
    341 
    342 
    343 class StartSequencingBtn(Button):
    344     __notifyevents__ = ['OnSubscriptionChanged']
    345     errors = None
    346 
    347     def __init__(self, **kwargs):
    348         super(StartSequencingBtn, self).__init__(**kwargs)
    349         sm.RegisterNotify(self)
    350 
    351     def update(self):
    352         if current_page.get_page_id() != SkinrPage.STUDIO_SEQUENCING:
    353             return
    354         if is_sequencing_button_always_enabled.is_enabled():
    355             self.errors = []
    356         else:
    357             self.errors = get_ship_skin_sequencing_svc().validate_design_for_sequencing(current_skin_design.get())
    358         if self.errors:
    359             self.style = ButtonStyle.DANGER
    360         else:
    361             self.style = ButtonStyle.NORMAL
    362 
    363     def OnSubscriptionChanged(self):
    364         self.update()
    365 
    366     def LoadTooltipPanel(self, tooltipPanel, *args):
    367         if not self.errors:
    368             return
    369         if self.busy:
    370             return
    371         tooltipPanel.LoadStandardSpacing()
    372         tooltipPanel.columns = 1
    373         tooltipPanel.AddMediumHeader(text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/UnableToStartSequencingJob'), wrapWidth=300)
    374         for error in self.errors:
    375             label = SEQUENCING_ERROR_TEXT_BY_CODE.get(error, None)
    376             if label:
    377                 tooltipPanel.AddLabelMedium(text=GetByLabel(label), wrapWidth=300, color=eveColor.DANGER_RED)
    378 
    379     def _CallFunction(self):
    380         if self.errors:
    381             return
    382         else:
    383             return super(StartSequencingBtn, self)._CallFunction()
    384 
    385 
    386 class ComponentTypeHeader(Container):
    387     default_height = 29
    388 
    389     def __init__(self, text, icon, *args, **kwargs):
    390         super(ComponentTypeHeader, self).__init__(*args, **kwargs)
    391         self.text = text
    392         self.icon = icon
    393         self.construct_layout()
    394 
    395     def construct_layout(self):
    396         Fill(bgParent=self, color=eveColor.TUNGSTEN_GREY, opacity=0.1)
    397         TextHeader(parent=self, align=Align.CENTERLEFT, text=self.text, padLeft=8)
    398         icon_container = Container(parent=self, align=Align.TORIGHT, width=16, padRight=8)
    399         Sprite(parent=icon_container, align=Align.CENTER, texturePath=self.icon, pos=(0, 0, 16, 16))