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

sequencingJobCard.py (14829B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\cosmetics\ship\pages\cards\sequencingJobCard.py
      2 import blue
      3 import carbonui
      4 import eveicon
      5 import evetypes
      6 import uthread2
      7 from carbon.common.script.sys.serviceConst import ROLE_QA
      8 from carbonui import Align, TextAlign, TextColor, uiconst, PickState, SpriteEffect, ButtonStyle
      9 from carbonui.text.styles import TextBody
     10 from carbonui.control.button import Button
     11 from carbonui.control.contextMenu.menuData import MenuData
     12 from carbonui.primitives.container import Container
     13 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     14 from carbonui.primitives.layoutGrid import LayoutGrid
     15 from carbonui.primitives.sprite import Sprite
     16 from carbonui.primitives.stretchspritehorizontal import StretchSpriteHorizontal
     17 from carbonui.uianimations import animations
     18 from cosmetics.client.liveIconRenderer import LiveIconSprite
     19 from cosmetics.client.shipSkinSequencingSvc import get_ship_skin_sequencing_svc
     20 from cosmetics.client.ships.skins.errors import NoTimeRemainingException, get_sequencing_error_text
     21 from cosmetics.client.ships.skins.live_data.sequencing_job import SequencingJobState
     22 from eve.client.script.ui import eveColor, eveThemeColor
     23 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel
     24 from eve.client.script.ui.control.gauge import Gauge
     25 from eve.client.script.ui.cosmetics.ship.pages.cards.cardConst import CardBackground, CardMask
     26 from eve.client.script.ui.shared.neocom.wallet.buyMissingPlexDialog import BuyMissingPlexDialog
     27 from eveservices.menu import GetMenuService
     28 from localization import GetByLabel
     29 from player_messaging.client.ui_message import message_player, prompt_player_fsd_dialog
     30 from plex.client.api.repository import get_repository
     31 from plex.client.api.signal import on_balance_changed
     32 from publicGateway.grpc.exceptions import GenericException
     33 from signals import Signal
     34 from stackless_response_router.exceptions import TimeoutException
     35 ANIM_DURATION = 0.2
     36 ICON_SIZE = 128
     37 
     38 class SequencingJobCard(ContainerAutoSize):
     39     default_state = uiconst.UI_NORMAL
     40     default_width = 168
     41     default_height = 308
     42 
     43     def __init__(self, sequencing_job, skin_design, *args, **kwargs):
     44         super(SequencingJobCard, self).__init__(*args, **kwargs)
     45         self.sequencing_job = sequencing_job
     46         self.skin_design = skin_design
     47         self._is_selected = False
     48         self.is_hovered = False
     49         self.live_icon = None
     50         self.finished_loading = False
     51         self.time_label_update_thread = None
     52         self.expedite_sequencing_thread = None
     53         self.update_complete_now_thread = None
     54         self.on_click = Signal('on_click')
     55         self.construct_layout()
     56         self.update_live_icon()
     57         self.update()
     58         self.connect_signals()
     59         self.time_label_update_thread = uthread2.start_tasklet(self.update_time_left_label)
     60 
     61     def Close(self):
     62         try:
     63             self.kill_threads()
     64             self.disconnect_signals()
     65         finally:
     66             super(SequencingJobCard, self).Close()
     67 
     68     def kill_threads(self):
     69         self.kill_time_label_update_thread()
     70         self.kill_expedite_sequencing_thread()
     71         self.kill_update_complete_now_thread()
     72 
     73     def kill_time_label_update_thread(self):
     74         if self.time_label_update_thread is not None:
     75             self.time_label_update_thread.kill()
     76             self.time_label_update_thread = None
     77 
     78     def kill_expedite_sequencing_thread(self):
     79         if self.expedite_sequencing_thread is not None:
     80             self.expedite_sequencing_thread.kill()
     81             self.expedite_sequencing_thread = None
     82 
     83     def kill_update_complete_now_thread(self):
     84         if self.update_complete_now_thread is not None:
     85             self.update_complete_now_thread.kill()
     86             self.update_complete_now_thread = None
     87 
     88     def connect_signals(self):
     89         on_balance_changed.connect(self.on_plex_amount_changed)
     90 
     91     def disconnect_signals(self):
     92         on_balance_changed.disconnect(self.on_plex_amount_changed)
     93 
     94     def construct_layout(self):
     95         self.construct_top_cont()
     96         self.construct_tier_indicator()
     97         self.construct_num_runs_label()
     98         self.construct_label_and_gauge()
     99         self.construct_complete_now()
    100         self.construct_ship_icon()
    101 
    102     def on_plex_amount_changed(self, *args):
    103         self.update()
    104 
    105     def on_live_rendered_icon_ready(self):
    106         self.finished_loading = True
    107         self.loading_sprite.display = False
    108         animations.FadeIn(self.live_icon)
    109         self.live_icon.on_rendered.disconnect(self.on_live_rendered_icon_ready)
    110 
    111     def on_loading_sprite_fadeout_complete(self):
    112         self.loading_sprite.display = False
    113 
    114     def construct_label_and_gauge(self):
    115         self.name_label = TextBody(name='name_label', parent=self, align=Align.TOTOP, color=TextColor.HIGHLIGHT, padTop=8, bold=True)
    116         self.gauge = Gauge(parent=self, align=Align.TOTOP, gaugeHeight=4, padTop=4, color=eveThemeColor.THEME_FOCUS)
    117         self.time_left_label = TextBody(name='time_left_label', parent=self, align=Align.TOTOP, padTop=2)
    118 
    119     def construct_complete_now(self):
    120         self.complete_now_cont = Container(name='complete_cont', parent=self, align=Align.TOTOP, height=Button.default_height, padBottom=16, padTop=8)
    121         self.complete_now_label_cont = LayoutGrid(name='complete_now_label_cont', parent=self.complete_now_cont, align=Align.CENTER, cellSpacing=(4, 0))
    122         Sprite(parent=self.complete_now_label_cont, align=Align.CENTERLEFT, texturePath=eveicon.plex, color=eveColor.PLEX_YELLOW, pos=(0, 0, 16, 16))
    123         self.complete_now_label = TextBody(parent=self.complete_now_label_cont, align=Align.CENTERLEFT)
    124         self.complete_button = Button(name='complete_button', parent=self.complete_now_cont, align=Align.TOTOP, display=False, pickState=PickState.ON, label=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/CompleteJobNow'), func=self.on_complete_button)
    125         self.complete_button.LoadTooltipPanel = self.LoadCompleteButtonTooltipPanel
    126 
    127     def LoadCompleteButtonTooltipPanel(self, tooltipPanel, *args):
    128         if self.has_enough_plex:
    129             return
    130         tooltipPanel.columns = 1
    131         tooltipPanel.LoadStandardSpacing()
    132         tooltipPanel.pickState = PickState.ON
    133         TextBody(parent=tooltipPanel, align=Align.CENTERLEFT, text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/InsufficientPLEX', plex_balance=int(get_repository().get_balance()) or 0), color=TextColor.DANGER, maxWidth=220)
    134         Button(parent=tooltipPanel, label=GetByLabel('UI/Wallet/BuyMore'), func=self.on_buy_more_plex_button, style=ButtonStyle.MONETIZATION, align=Align.TOTOP)
    135 
    136     def on_buy_more_plex_button(self, *args):
    137         buy_missing_plex_dialog = BuyMissingPlexDialog(required_amount=self.get_early_completion_plex_cost())
    138         buy_missing_plex_dialog.ShowModal()
    139 
    140     def on_complete_button(self, *args):
    141         if not self.has_enough_plex:
    142             return
    143         try:
    144             self.complete_button.busy = True
    145             self.complete_button.disable()
    146             self.show_expedite_sequencing_popup_async()
    147         except (GenericException, TimeoutException):
    148             message_player(GetByLabel('UI/Common/CannotConnectToServer'))
    149         finally:
    150             self.complete_button.busy = False
    151             self.complete_button.enable()
    152 
    153     def show_expedite_sequencing_popup_async(self):
    154         self.kill_expedite_sequencing_thread()
    155         self.expedite_sequencing_thread = uthread2.start_tasklet(self.show_expedite_sequencing_popup)
    156 
    157     def show_expedite_sequencing_popup(self):
    158         params = {'plex_cost': self.get_early_completion_plex_cost()}
    159         icon = 'res:/UI/texture/Icons/SKINR.png'
    160         if prompt_player_fsd_dialog('CompleteSequencingNow', params, uiconst.YESNO, icon) == uiconst.ID_YES:
    161             error = get_ship_skin_sequencing_svc().expedite_sequencing(self.sequencing_job.job_id)
    162             if error is not None:
    163                 message_player(GetByLabel(get_sequencing_error_text(error)))
    164 
    165     def construct_top_cont(self):
    166         self.top_cont = Container(name='top_cont', parent=self, align=Align.TOTOP, height=198)
    167 
    168     def construct_num_runs_label(self):
    169         num_runs_cont = ContainerAutoSize(name='num_runs_Cont', parent=self.top_cont, align=Align.BOTTOMRIGHT, height=46)
    170         StretchSpriteHorizontal(bgParent=num_runs_cont, texturePath='res:/UI/Texture/classes/Cosmetics/Ship/cards/sequencing/numruns_background.png', leftEdgeSize=34, rightEdgeSize=30)
    171         label_cont = ContainerAutoSize(name='label_cont', parent=num_runs_cont, align=Align.TOLEFT, padLeft=40, padRight=26)
    172         self.num_runs_label = TextBody(align=Align.CENTERLEFT, top=4, parent=label_cont)
    173 
    174     def construct_ship_icon(self):
    175         icon_cont = Container(name='icon_cont', parent=self.top_cont, align=Align.CENTER, pos=(0, 0, 168, 198), state=uiconst.UI_DISABLED)
    176         self.live_icon = LiveIconSprite(name='live_icon', parent=icon_cont, opacity=0.0, viewportWidth=168, viewportHeight=198, bg_texture_path=CardBackground.SEQUENCING, mask_texture_path=CardMask.SEQUENCING)
    177         self.fallback_background = Sprite(name='fallback_background', bgParent=icon_cont, spriteEffect=SpriteEffect.MASK, textureSecondaryPath=CardMask.SEQUENCING, texturePath=CardBackground.SEQUENCING)
    178         self.finished_loading = False
    179         self.live_icon.on_rendered.connect(self.on_live_rendered_icon_ready)
    180         self.loading_sprite = LoadingWheel(parent=self.top_cont, align=uiconst.CENTER)
    181 
    182     def construct_tier_indicator(self):
    183         if not self.show_tier_indicator():
    184             return
    185         self.tier_label = TextBody(name='tier_label', parent=self.top_cont, align=Align.TOTOP_NOPUSH, textAlign=TextAlign.CENTER, text='{tier}'.format(tier=self.get_skin_tier_level()), color=eveColor.BLACK, shadowOffset=(0, 0), bold=True, padTop=6)
    186         self.tier_container = Container(name='tier_container', parent=self.top_cont, align=Align.TOTOP_NOPUSH, opacity=0.5, width=self.default_width, height=29)
    187         self.tier_background = Sprite(name='tier_background', parent=self.tier_container, align=Align.TOTOP_NOPUSH, state=uiconst.UI_DISABLED, texturePath=self.get_skin_tier_texture(), outputMode=uiconst.OutputMode.COLOR_AND_GLOW, glowBrightness=0.4, width=self.default_width, height=29)
    188 
    189     def show_tier_indicator(self):
    190         return True
    191 
    192     def get_skin_tier_level(self):
    193         return self.skin_design.tier_level
    194 
    195     def get_skin_tier_texture(self):
    196         tier_level = self.get_skin_tier_level()
    197         return 'res:/UI/Texture/classes/Cosmetics/Ship/cards/tier/tier_{level}.png'.format(level=tier_level)
    198 
    199     def update(self):
    200         self.num_runs_label.text = str(self.sequencing_job.nb_runs)
    201         self.name_label.text = self.skin_design.name or ''
    202         self.update_tier_indicator()
    203         self.fallback_background.texturePath = CardBackground.SEQUENCING_SELECTED if self.is_selected else CardBackground.SEQUENCING
    204         self.update_complete_now_async()
    205 
    206     def update_complete_now_async(self):
    207         self.kill_update_complete_now_thread()
    208         self.update_complete_now_thread = uthread2.start_tasklet(self.update_complete_now)
    209 
    210     def update_complete_now(self):
    211         if not self.complete_now_cont or self.complete_now_cont.destroyed:
    212             return
    213         try:
    214             plex_cost = self.get_early_completion_plex_cost()
    215             if plex_cost:
    216                 self.complete_now_cont.display = True
    217                 self.complete_now_label.text = plex_cost
    218                 has_enough_plex = get_repository().get_balance() >= plex_cost
    219                 self.complete_button.style = ButtonStyle.NORMAL if has_enough_plex else ButtonStyle.DANGER
    220             else:
    221                 self.complete_now_cont.display = False
    222         except NoTimeRemainingException:
    223             self.complete_now_cont.display = False
    224 
    225     def get_early_completion_plex_cost(self):
    226         try:
    227             plex_cost = get_ship_skin_sequencing_svc().get_early_completion_cost(self.sequencing_job.job_id)
    228         except (GenericException, TimeoutException):
    229             message_player(GetByLabel('UI/Common/CannotConnectToServer'))
    230             return None
    231 
    232         return plex_cost
    233 
    234     def update_time_left_label(self):
    235         while not self.destroyed:
    236             job_state = self.sequencing_job.state
    237             if job_state == SequencingJobState.STARTED:
    238                 self.time_left_label.text = self.sequencing_job.get_time_remaining_text()
    239                 self.gauge.SetValue(self.sequencing_job.get_completed_ratio(), animate=False)
    240             elif job_state == SequencingJobState.COMPLETED:
    241                 self.time_left_label.text = GetByLabel('UI/Generic/Completed')
    242                 self.gauge.SetValue(1.0)
    243             uthread2.Sleep(0.5)
    244 
    245     def update_tier_indicator(self):
    246         if not self.show_tier_indicator():
    247             return
    248         animations.FadeTo(self.tier_container, self.tier_container.opacity, 0.5, ANIM_DURATION)
    249 
    250     def update_live_icon(self, skip_queue = False):
    251         bg_texture_path = CardBackground.SEQUENCING_SELECTED if self.is_selected else CardBackground.SEQUENCING
    252         self.live_icon.apply_skin_design(self.skin_design, bg_texture_path, skip_queue)
    253 
    254     @property
    255     def has_enough_plex(self):
    256         return get_repository().get_balance() >= self.get_early_completion_plex_cost()
    257 
    258     @property
    259     def is_selected(self):
    260         return self._is_selected
    261 
    262     @is_selected.setter
    263     def is_selected(self, value):
    264         should_update = value != self._is_selected
    265         self._is_selected = value
    266         if should_update:
    267             self.update()
    268             self.update_live_icon(skip_queue=True)
    269 
    270     def OnMouseEnter(self, *args):
    271         self.is_hovered = True
    272         if self.complete_now_cont.display:
    273             self.complete_button.display = True
    274             self.complete_now_label_cont.display = False
    275         self.update()
    276 
    277     def OnMouseExit(self, *args):
    278         self.is_hovered = False
    279         if self.complete_now_cont.display:
    280             self.complete_button.display = False
    281             self.complete_now_label_cont.display = True
    282         self.update()
    283 
    284     def OnClick(self, *args):
    285         self.is_selected = not self.is_selected
    286         self.on_click(self)
    287 
    288     def GetMenu(self):
    289         menu = MenuData()
    290         type_id = self.skin_design.ship_type_id
    291         menu.AddEntry(text=evetypes.GetName(type_id), subMenuData=lambda : GetMenuService().GetMenuFromItemIDTypeID(itemID=None, typeID=type_id, includeMarketDetails=True))
    292         if session.role & ROLE_QA:
    293             menu.AddEntry(text='Job UUID: {job_uuid}'.format(job_uuid=self.sequencing_job.job_id), func=lambda *args: blue.pyos.SetClipboardData(str(self.sequencing_job.job_id)))
    294         return menu