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

ticker.py (12015B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\market\ticker.py
      2 import eveui
      3 from carbon.client.script.environment.AudioUtil import PlaySound
      4 from carbonui import uiconst
      5 from carbonui.graphs import axis
      6 from carbonui.graphs.graph import GraphArea
      7 from carbonui.graphs.linegraph import LineGraph
      8 from carbonui.primitives.container import Container
      9 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     10 from clientsettings.setting import UserSettingBool
     11 from carbonui.uianimations import animations
     12 from carbonui.util.color import Color
     13 from carbonui.control.buttonIcon import ButtonIcon
     14 from eve.client.script.ui import eveColor
     15 from eve.client.script.ui.control.eveIcon import Icon
     16 from eve.client.script.ui.control.eveLabel import EveHeaderLarge
     17 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel
     18 from eve.client.script.ui.control.glowSprite import GlowSprite
     19 from eve.client.script.ui.control.themeColored import FillThemeColored
     20 from eve.common.lib import appConst as const
     21 import evetypes
     22 import localization
     23 import signals
     24 import uthread
     25 from menu import MenuLabel
     26 from eveexceptions.exceptionEater import ExceptionEater
     27 from marketutil.quickbarUtil import GetTypIDsInFolder, TICKER_SETTING_NAME
     28 TICKER_ITEM_TYPES_ALWAYS_PRESENT = [const.typePlex,
     29  const.typeSkillInjector,
     30  const.typeSkillExtractor,
     31  const.typeSmallSkillInjector]
     32 TICKER_ITEM_TYPES = (const.typePlex,
     33  const.typeSkillInjector,
     34  const.typeRaffleToken,
     35  const.typeMexallon,
     36  9848,
     37  28606,
     38  21815,
     39  const.typeNaniteRepairPaste,
     40  25624,
     41  17888,
     42  16681,
     43  const.typeTritanium,
     44  17918,
     45  const.typeZydrine,
     46  33681,
     47  28366,
     48  const.typeSkillExtractor,
     49  25591,
     50  const.typeNocxium,
     51  17887,
     52  25619,
     53  const.typeMegacyte,
     54  16274,
     55  const.typeStrontiumClathrates,
     56  16272,
     57  const.typeSistersCoreProbeLauncher,
     58  16273,
     59  const.typePyerite,
     60  17889,
     61  21096,
     62  33475,
     63  const.typeSmallSkillInjector)
     64 TICKER_SCROLL_SPEED = 20.0
     65 
     66 class TickerContainer(Container):
     67     default_clipChildren = True
     68 
     69     def ApplyAttributes(self, attributes):
     70         super(TickerContainer, self).ApplyAttributes(attributes)
     71 
     72     def _VerifyNewChild(self, child):
     73         if child.align != uiconst.TOLEFT:
     74             raise ValueError('TOLEFT is the only acceptable alignment')
     75 
     76     def PauseAnimation(self):
     77         if not self.children:
     78             return
     79         animations.StopAnimation(self.children[0], 'left')
     80 
     81     def ResumeAnimation(self):
     82         if not self.children:
     83             return
     84         child = self.children[0]
     85         while not child.destroyed and child.width == 0:
     86             eveui.wait_for_next_frame()
     87 
     88         animations.MorphScalar(child, 'left', startVal=child.left, endVal=-child.width, duration=(child.width + child.left) / float(TICKER_SCROLL_SPEED), curveType=uiconst.ANIM_LINEAR, callback=self._OnTickerEntryReachedEnd)
     89 
     90     def _OnTickerEntryReachedEnd(self):
     91         child = self.children[0]
     92         child.left = 0
     93         child.SetOrder(-1)
     94         child.opacity = 0.0
     95         animations.FadeTo(child)
     96         self.ResumeAnimation()
     97 
     98 
     99 class TickerItem(object):
    100 
    101     def __init__(self, typeID):
    102         self.typeID = typeID
    103         self._loaded = False
    104         self._isAvailable = None
    105         self._averagePrice = None
    106         self._averagePriceDelta = None
    107         self._averagePriceHistory = None
    108 
    109     @property
    110     def isAvailable(self):
    111         self.LoadData()
    112         return self._isAvailable
    113 
    114     @property
    115     def averagePrice(self):
    116         self.LoadData()
    117         return self._averagePrice
    118 
    119     @property
    120     def averagePriceDelta(self):
    121         self.LoadData()
    122         return self._averagePriceDelta
    123 
    124     @property
    125     def name(self):
    126         return evetypes.GetName(self.typeID)
    127 
    128     def GetAveragePriceHistory(self):
    129         self.LoadData()
    130         return self._averagePriceHistory
    131 
    132     def LoadData(self):
    133         if self._loaded:
    134             return
    135         history = sm.GetService('marketQuote').GetPriceHistoryFromCache(self.typeID)
    136         if not history or len(history) < 7:
    137             self._isAvailable = False
    138             return
    139         self._averagePriceHistory = [ x.avgPrice for x in history[-7:] ]
    140         self._averagePrice = self._averagePriceHistory[-1]
    141         self._averagePriceDelta = self._averagePriceHistory[-1] / self._averagePriceHistory[-7] - 1.0
    142         if self._averagePrice <= 0 or all((x.volume == 0 for x in history)):
    143             self._isAvailable = False
    144         else:
    145             self._isAvailable = True
    146         self._loaded = True
    147 
    148 
    149 ticker_enabled_setting = UserSettingBool(settings_key='market_ticker_enabled', default_value=True)
    150 
    151 class TickerController(object):
    152 
    153     def __init__(self):
    154         self.items = self.GetItems()
    155         self._loaded = False
    156 
    157     def ResetController(self):
    158         self._loaded = False
    159         self.items = self.GetItems()
    160 
    161     def GetItems(self):
    162         itemTypes = self.GetItemsFromQuickbar()
    163         if itemTypes:
    164             itemTypes.update(TICKER_ITEM_TYPES_ALWAYS_PRESENT)
    165         else:
    166             itemTypes = TICKER_ITEM_TYPES
    167         return [ TickerItem(typeID) for typeID in itemTypes ]
    168 
    169     def GetItemsFromQuickbar(self):
    170         itemTypes = set()
    171         quickbar = settings.user.ui.Get('quickbar', {})
    172         marketTickerFolderID = settings.char.ui.Get(TICKER_SETTING_NAME, None)
    173         if marketTickerFolderID is not None:
    174             myTypeIDs = GetTypIDsInFolder(marketTickerFolderID, quickbar)
    175             for typeID in myTypeIDs:
    176                 with ExceptionEater('TickerController:GetItems'):
    177                     if evetypes.GetMarketGroupID(typeID):
    178                         itemTypes.add(typeID)
    179                 if len(itemTypes) >= 50:
    180                     return itemTypes
    181 
    182         return itemTypes
    183 
    184     @property
    185     def enabled(self):
    186         return ticker_enabled_setting.is_enabled()
    187 
    188     @enabled.setter
    189     def enabled(self, value):
    190         value = bool(value)
    191         ticker_enabled_setting.set(value)
    192 
    193     @property
    194     def on_enabled_changed(self):
    195         return ticker_enabled_setting.on_change
    196 
    197     def LoadData(self):
    198         if self._loaded:
    199             return
    200         for item in self.items:
    201             item.LoadData()
    202 
    203         self._loaded = True
    204 
    205     def ToggleEnabled(self):
    206         self.enabled = not self.enabled
    207 
    208 
    209 class TickerToggleButton(GlowSprite):
    210     default_texturePath = 'res:/UI/Texture/Shared/triangleDown.png'
    211     default_width = 10
    212     default_height = 10
    213     default_hint = localization.GetByLabel('UI/Market/TickerToggleHint')
    214 
    215     def ApplyAttributes(self, attributes):
    216         super(TickerToggleButton, self).ApplyAttributes(attributes)
    217         self.controller = attributes.controller
    218         self.UpdateTexture()
    219 
    220     def OnClick(self, *args):
    221         PlaySound(uiconst.SOUND_BUTTON_CLICK)
    222         self.controller.ToggleEnabled()
    223         self.UpdateTexture()
    224 
    225     def OnMouseEnter(self, *args):
    226         super(TickerToggleButton, self).OnMouseEnter(*args)
    227         PlaySound(uiconst.SOUND_BUTTON_HOVER)
    228 
    229     def UpdateTexture(self):
    230         if self.controller.enabled:
    231             self.SetTexturePath('res:/UI/Texture/Shared/triangleDown.png')
    232         else:
    233             self.SetTexturePath('res:/UI/Texture/Shared/triangleUp.png')
    234 
    235 
    236 class MarketTicker(Container):
    237     default_state = uiconst.UI_NORMAL
    238     __notifyevents__ = ['OnMarketTickerSettingsUpdated']
    239 
    240     def ApplyAttributes(self, attributes):
    241         super(MarketTicker, self).ApplyAttributes(attributes)
    242         self.ticker = None
    243         self.controller = TickerController()
    244         ticker_enabled_setting.on_change.connect(self.OnEnabledChanged)
    245         self.BuildMarketTicker()
    246         sm.RegisterNotify(self)
    247 
    248     def BuildMarketTicker(self):
    249         self.Layout()
    250         uthread.new(self.Load)
    251 
    252     def Layout(self):
    253         self.display = self.controller.enabled
    254         self.ticker = TickerContainer(parent=self, align=uiconst.TOALL)
    255 
    256     def Load(self):
    257         loading_size = max(16, self.height - 8)
    258         loadingWheel = LoadingWheel(parent=self, align=uiconst.CENTER, width=loading_size, height=loading_size)
    259         typeIDsInTicker = {x.typeID for x in self.controller.items}
    260         sm.GetService('marketQuote').PopulatePriceHistoryCacheForManyTypeIDs(typeIDsInTicker)
    261         for item in self.controller.items:
    262             if not item.isAvailable:
    263                 continue
    264             TickerEntry(parent=self.ticker, align=uiconst.TOLEFT, item=item, onMouseEnter=self.ticker.PauseAnimation, onMouseExit=self.ticker.ResumeAnimation, opacity=0.0)
    265             if len(self.ticker.children) > 15:
    266                 break
    267 
    268         animations.FadeOut(loadingWheel, duration=0.5)
    269         if not self.ticker.children:
    270             self.DisplayNoItemsMessage()
    271         for i, child in enumerate(self.ticker.children):
    272             animations.FadeTo(child, duration=0.3, timeOffset=i * 0.1)
    273 
    274         if self.IsVisible():
    275             self.ticker.ResumeAnimation()
    276 
    277     def DisplayNoItemsMessage(self):
    278         EveHeaderLarge(parent=self, align=uiconst.CENTER, text=localization.GetByLabel('UI/Market/TickerNoItems'), opacity=0.6)
    279 
    280     def OnEnabledChanged(self, enabled):
    281         self.display = enabled
    282 
    283     def RefreshMarketTicker(self):
    284         self.Flush()
    285         self.BuildMarketTicker()
    286 
    287     def GetMenu(self):
    288         return GetTickerMenu()
    289 
    290     def OnMarketTickerSettingsUpdated(self, newTickerFolderID):
    291         self.controller.ResetController()
    292         self.RefreshMarketTicker()
    293 
    294 
    295 class TickerEntry(ContainerAutoSize):
    296     default_state = uiconst.UI_NORMAL
    297     default_name = 'tickerEntry'
    298 
    299     def ApplyAttributes(self, attributes):
    300         super(TickerEntry, self).ApplyAttributes(attributes)
    301         self.item = attributes.item
    302         self.onMouseEnter = signals.Signal(signalName='onMouseEnter')
    303         if callable(attributes.onMouseEnter):
    304             self.onMouseEnter.connect(attributes.onMouseEnter)
    305         self.onMouseExit = signals.Signal(signalName='onMouseExit')
    306         if callable(attributes.onMouseExit):
    307             self.onMouseExit.connect(attributes.onMouseExit)
    308         self.Layout()
    309 
    310     def Layout(self):
    311         self._hover = FillThemeColored(bgParent=self, colorType=uiconst.COLORTYPE_UIHILIGHT, opacity=0.0)
    312         iconCont = Container(parent=self, align=uiconst.TOLEFT, left=8, width=24)
    313         Icon(parent=iconCont, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=24, height=24, typeID=self.item.typeID, isCopy=False)
    314         if self.item.averagePriceDelta >= 0.0:
    315             changeColor = Color(eveColor.SUCCESS_GREEN)
    316         else:
    317             changeColor = Color(eveColor.DANGER_RED)
    318         EveHeaderLarge(parent=ContainerAutoSize(parent=self, align=uiconst.TOLEFT, padding=(4, 0, 4, 0)), align=uiconst.CENTERLEFT, text=localization.GetByLabel('UI/Market/TickerItemLabel', typeID=self.item.typeID, averagePrice=self.item.averagePrice, averagePriceDelta=self.item.averagePriceDelta, color=changeColor.GetHex()))
    319         values = self.item.GetAveragePriceHistory()
    320         vAxis = axis.BaseAxis((min(values), max(values)))
    321         hAxis = axis.CategoryAxis(range(len(values)))
    322         graph = GraphArea(parent=self, align=uiconst.TOLEFT, state=uiconst.UI_DISABLED, padding=(0, 8, 8, 8), width=36, verticalAxis=[(vAxis, 1.0, 0.0)], horizontalAxis=[(hAxis, 0.0, 1.0)])
    323         LineGraph(parent=graph, categoryAxis=hAxis, valueAxis=vAxis, values=values, lineWidth=0.8)
    324 
    325     def OnClick(self, *args):
    326         sm.StartService('marketutils').ShowMarketDetails(self.item.typeID)
    327 
    328     def OnMouseEnter(self, *args):
    329         animations.FadeTo(self._hover, endVal=0.2, duration=0.15)
    330         self.onMouseEnter()
    331 
    332     def OnMouseExit(self, *args):
    333         animations.FadeOut(self._hover, duration=0.2)
    334         self.onMouseExit()
    335 
    336     def GetMenu(self):
    337         return GetTickerMenu()
    338 
    339 
    340 def GetTickerMenu():
    341     return [(MenuLabel('UI/Market/TickerReset'), ResetMarketTicker, ())]
    342 
    343 
    344 def ResetMarketTicker():
    345     settings.char.ui.Set(TICKER_SETTING_NAME, None)
    346     sm.ScatterEvent('OnMarketTickerSettingsUpdated', None)