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)