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

marketGraph.py (18760B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\market\marketGraph.py
      2 from carbon.common.script.util.format import FmtDate, FmtAmt
      3 import carbonui.const as uiconst
      4 from carbonui import ButtonVariant, Density
      5 from carbonui.primitives.container import Container
      6 from carbonui.control.button import Button
      7 from carbonui.graphs.minimap import MiniMap
      8 from carbonui.graphs.pointgraph import PointGraph
      9 from carbonui.graphs.donchianchannel import DonchianChannel
     10 from carbonui.graphs import axis, axislabels, graph
     11 from carbonui.graphs.lowhighvaluegraph import LowHighValueGraph
     12 from carbonui.graphs.graphsutil import MovingAvg
     13 from carbonui.graphs.linegraph import LineGraph
     14 from carbonui.graphs.bargraph import BarGraph, DynamicHint
     15 from carbonui.graphs.areagraph import AreaGraph
     16 from carbonui.graphs.grid import Grid
     17 from carbonui.graphs.legend import Legend
     18 from carbonui.graphs import animations
     19 import localization
     20 from eve.client.script.ui.control.utilMenu import UtilMenu
     21 _cachedRange = ()
     22 graphColors = ((0.0, 0.247, 0.31, 1.0),
     23  (0.098, 0.4, 0.479, 1.0),
     24  (0.082, 0.663, 0.812, 1.0),
     25  (0.569, 0.275, 0.0, 1.0),
     26  (0.812, 0.435, 0.082, 1.0))
     27 TOP_AXIS_HEIGHT = 30
     28 LEFT_AXIS_WIDTH = 70
     29 RIGHT_AXIS_WIDTH = 50
     30 
     31 def _GetSetting(name, default):
     32     return settings.user.ui.Get('market_setting_%s' % name, default)
     33 
     34 
     35 def _SetSetting(name, value):
     36     settings.user.ui.Set('market_setting_%s' % name, value)
     37 
     38 
     39 def _OnAnimationCheckbox():
     40     curr = _GetSetting('animations', 1)
     41     _SetSetting('animations', not curr)
     42 
     43 
     44 def _FormatAmount(x):
     45     if abs(x) < 100:
     46         return FmtAmt(x, 'ln', 1)
     47     try:
     48         return FmtAmt(x, 'sn')
     49     except:
     50         return ''
     51 
     52 
     53 class MarketGraph(Container):
     54     default_state = uiconst.UI_ACTIVE
     55     default_zoom = 30
     56 
     57     def ApplyAttributes(self, attributes):
     58         global _cachedRange
     59         Container.ApplyAttributes(self, attributes)
     60         self.typeID = attributes.get('typeID', None)
     61         self.data = attributes.get('data', None)
     62         count = len(self.data)
     63         self.zoom = attributes.get('zoom', None)
     64         if self.zoom is None:
     65             self.zoom = _GetSetting('dateRange', self.default_zoom)
     66         self.zoom = min(self.zoom, count)
     67         if _cachedRange:
     68             timeRange = _cachedRange
     69             if timeRange[1] > count:
     70                 timeRange = (count - self.zoom - 0.5, count - 0.5)
     71         else:
     72             timeRange = (count - self.zoom - 0.5, count - 0.5)
     73         self.resetZoomBtn = Button(name='resetZoom', label=localization.GetByLabel('UI/Market/PriceHistory/ResetAxis'), parent=self, align=uiconst.CENTERTOP, func=self._ResetAutoZoom, density=Density.COMPACT, variant=ButtonVariant.GHOST)
     74         self.topAxisContainer = Container(parent=self, name='TopAxisContainer', align=uiconst.TOTOP, height=TOP_AXIS_HEIGHT)
     75         self.miniMapContainer = Container(parent=self, name='MinimapContainer', align=uiconst.TOBOTTOM, height=60)
     76         self.leftAxisContainer = Container(parent=self, name='LeftAxisContainer', align=uiconst.TOLEFT, width=LEFT_AXIS_WIDTH)
     77         self.rightAxisContainer = Container(parent=self, name='RightAxisContainer', align=uiconst.TORIGHT, width=RIGHT_AXIS_WIDTH)
     78         self.leftAxis = axis.AutoTicksAxis(self._GetLeftAxisData(), tickCount=10, margins=(0.4, 0.15), tickFilter=lambda t: t >= 0, labelFormat=_FormatAmount)
     79         self.rightAxis = axis.AutoTicksAxis(self._GetRightAxisData(), behavior=axis.AXIS_FROM_ZERO, tickCount=4, labelFormat=_FormatAmount)
     80         self.topAxis = axis.MonthlyTimeAxis([ x[0] for x in self.data ], visibleRange=timeRange, behavior=axis.AXIS_CUSTOM, interactionLimit=axis.InteractionLimit.DATA_RANGE)
     81         self.graphArea = graph.GraphArea(parent=self, name='GraphArea')
     82         self.graphArea.AddAxis(axis.AxisOrientation.HORIZONTAL, self.topAxis)
     83         self.graphArea.AddAxis(axis.AxisOrientation.VERTICAL, self.leftAxis, 1.0, 0.0)
     84         self.graphArea.AddAxis(axis.AxisOrientation.VERTICAL, self.rightAxis, 1.0, 0.7)
     85         self.graphArea.AddZoomAxis(self.leftAxis)
     86         self.graphArea.AddPanAxis(self.leftAxis)
     87         self.graphArea.AddPanAxis(self.topAxis)
     88         if _GetSetting('scale_to_data', 1) and self.leftAxis.GetBehavior() != axis.AXIS_CUSTOM:
     89             self.leftAxis.ZoomOn(self._GetLeftAxisData(True))
     90             self.rightAxis.ZoomOn(self._GetRightAxisData(True))
     91         self.median = None
     92         self.donchian = None
     93         self.lowHigh = None
     94         self.movingAvg5 = None
     95         self.movingAvg20 = None
     96         self.barGraph = None
     97         self._isPanning = False
     98         self._prevPanPosition = (0, 0)
     99         self._ajustZoomToVisible = _GetSetting('scale_to_data', 1)
    100         if len(self.data) < 1:
    101             return
    102         self.Build(_GetSetting('animations', 1))
    103         self.leftAxis.onChange.connect(self._LeftAxisChanged)
    104         self.topAxis.onChange.connect(self._TopAxisChanged)
    105 
    106     def _OnScaleToDataCheckbox(self):
    107         curr = _GetSetting('scale_to_data', 1)
    108         _SetSetting('scale_to_data', not curr)
    109         if _GetSetting('scale_to_data', 1):
    110             if self.leftAxis.GetBehavior() != axis.AXIS_CUSTOM:
    111                 self.leftAxis.ZoomOn(self._GetLeftAxisData(True))
    112             self.rightAxis.ZoomOn(self._GetRightAxisData(True))
    113         else:
    114             self.leftAxis.ZoomOn(None)
    115             self.rightAxis.ZoomOn(None)
    116 
    117     def _OnLegendCheckbox(self):
    118         curr = _GetSetting('legend', 1)
    119         _SetSetting('legend', not curr)
    120         if _GetSetting('legend', 1):
    121             if not self.legend:
    122                 self.legend = Legend(parent=self, align=uiconst.TOTOP, autoHeight=True, centerContent=True, contentSpacing=(6, 6))
    123             self._RebuildLegend()
    124         elif self.legend:
    125             self.legend.Close()
    126             self.legend = None
    127 
    128     def _OnGraphCheckbox(self, name):
    129         curr = _GetSetting(name, 1)
    130         _SetSetting(name, not curr)
    131         self.leftAxis.SetDataRange(self._GetLeftAxisData(False))
    132         self.rightAxis.SetDataRange(self._GetRightAxisData(False))
    133         if name == 'movingavg5':
    134             self.AddMovingAverage5()
    135         elif name == 'movingavg20':
    136             self.AddMovingAverage20()
    137         elif name == 'donchian':
    138             self.AddDonchianChannel()
    139         elif name == 'mediandayprice':
    140             self.AddMedianPoints()
    141         elif name == 'minmax':
    142             self.AddMinMaxGraph()
    143         elif name == 'volume':
    144             self.AddBarGraph()
    145         self._RebuildLegend()
    146 
    147     def GetGraphMenu(self, menuParent):
    148         avg5 = localization.GetByLabel('/Carbon/UI/Common/WrittenDateTimeQuantityShort/Day', value=5)
    149         avg20 = localization.GetByLabel('/Carbon/UI/Common/WrittenDateTimeQuantityShort/Day', value=20)
    150         GetLabel = localization.GetByLabel
    151         PRICE_FILTERS = [('movingavg5', GetLabel('UI/Market/PriceHistory/ShowMovingAvg', numDays=avg5)),
    152          ('movingavg20', GetLabel('UI/Market/PriceHistory/ShowMovingAvg', numDays=avg20)),
    153          ('donchian', GetLabel('UI/Market/PriceHistory/ShowDonchianChannel')),
    154          ('mediandayprice', GetLabel('UI/Market/PriceHistory/ShowMedianDayPrice')),
    155          ('minmax', GetLabel('UI/Market/PriceHistory/ShowMinMax')),
    156          ('volume', GetLabel('UI/Market/PriceHistory/ShowVolume'))]
    157         menuParent.AddCheckBox(text=GetLabel('UI/Market/PriceHistory/ShowAnimations'), checked=_GetSetting('animations', 1), callback=_OnAnimationCheckbox)
    158         menuParent.AddCheckBox(text=GetLabel('UI/Market/PriceHistory/ScaleToData'), checked=_GetSetting('scale_to_data', 1), callback=self._OnScaleToDataCheckbox)
    159         menuParent.AddSpace()
    160         for settingName, label in PRICE_FILTERS:
    161             menuParent.AddCheckBox(text=label, checked=_GetSetting(settingName, 1), callback=(self._OnGraphCheckbox, settingName))
    162 
    163         menuParent.AddCheckBox(text=GetLabel('UI/Market/PriceHistory/ShowLegend'), checked=_GetSetting('legend', 1), callback=self._OnLegendCheckbox)
    164 
    165     def Build(self, animate = False):
    166         self.topAxisLabels = axislabels.AxisLabels(parent=self.topAxisContainer, align=uiconst.TOTOP, axis=self.topAxis, orientation=axis.AxisOrientation.HORIZONTAL, padding=(LEFT_AXIS_WIDTH,
    167          0,
    168          RIGHT_AXIS_WIDTH,
    169          0), height=TOP_AXIS_HEIGHT, textAlignment=uiconst.CENTERBOTTOM)
    170         self.leftAxisLabels = axislabels.AxisLabels(parent=self.leftAxisContainer, axis=self.leftAxis, orientation=axis.AxisOrientation.VERTICAL, align=uiconst.TOLEFT, width=LEFT_AXIS_WIDTH, minFactor=1.0, maxFactor=0.0, hints=True)
    171         self.rightAxisLabels = axislabels.AxisLabels(parent=self.rightAxisContainer, axis=self.rightAxis, orientation=axis.AxisOrientation.VERTICAL, textAlignment=uiconst.BOTTOMLEFT, align=uiconst.TOBOTTOM_PROP, height=0.3, width=self.rightAxisContainer.width, minFactor=1.0, maxFactor=0.0, hints=True)
    172         if _GetSetting('legend', 1):
    173             self.legend = Legend(parent=self, align=uiconst.TOTOP, autoHeight=True, centerContent=True, contentSpacing=(6, 6))
    174         else:
    175             self.legend = None
    176         self.AddMiniMap()
    177         self.AddMedianPoints(animate)
    178         self.AddMinMaxGraph()
    179         self.AddMovingAverage5()
    180         self.AddMovingAverage20()
    181         self.AddDonchianChannel()
    182         self.AddGrid()
    183         self.AddBarGraph(animate)
    184         if self.legend:
    185             self._RebuildLegend()
    186         self.utilMenu = UtilMenu(menuAlign=uiconst.TOPLEFT, parent=self, align=uiconst.TOPRIGHT, GetUtilMenu=self.GetGraphMenu, texturePath='res:/UI/Texture/SettingsCogwheel.png', top=4, width=18, height=18, iconSize=18)
    187         self.resetZoomBtn.display = self.leftAxis.GetBehavior() == axis.AXIS_CUSTOM
    188 
    189     def _ResetAutoZoom(self, *_):
    190         self.resetZoomBtn.display = False
    191         self.leftAxis.SetBehavior(axis.AXIS_TIGHT)
    192         if _GetSetting('scale_to_data', 1):
    193             self.leftAxis.ZoomOn(self._GetLeftAxisData(True))
    194 
    195     def _LeftAxisChanged(self, _):
    196         self.resetZoomBtn.display = self.leftAxis.GetBehavior() == axis.AXIS_CUSTOM
    197 
    198     def _TopAxisChanged(self, _):
    199         global _cachedRange
    200         _cachedRange = self.topAxis.GetVisibleRange()
    201         _SetSetting('dateRange', int(_cachedRange[1] - _cachedRange[0]))
    202         if _GetSetting('scale_to_data', 1):
    203             if self.leftAxis.GetBehavior() != axis.AXIS_CUSTOM:
    204                 self.leftAxis.ZoomOn(self._GetLeftAxisData(True))
    205             self.rightAxis.ZoomOn(self._GetRightAxisData(True))
    206 
    207     def _GetLeftAxisData(self, visibleRange = False):
    208         if visibleRange:
    209             inputData = self.data[max(0, int(self.topAxis.GetVisibleRange()[0])):int(self.topAxis.GetVisibleRange()[1]) + 1]
    210         else:
    211             inputData = self.data
    212         data = []
    213         if _GetSetting('mediandayprice', True):
    214             data.extend((x[3] for x in inputData))
    215         if _GetSetting('minmax', True):
    216             data.extend((x[1] for x in inputData))
    217             data.extend((x[2] for x in inputData))
    218         closing = [ x[3] for x in inputData ]
    219         if _GetSetting('movingavg5', True):
    220             data.extend(MovingAvg(closing, 5))
    221         if _GetSetting('movingavg20', True):
    222             data.extend(MovingAvg(closing, 20))
    223         if _GetSetting('donchian', True):
    224             data.extend((x[1] for x in inputData))
    225             data.extend((y[2] for y in inputData))
    226         if not data:
    227             return (0, 1)
    228         return (min(data), max(data))
    229 
    230     def _GetRightAxisData(self, visibleRange = False):
    231         data = []
    232         if _GetSetting('volume', True):
    233             if visibleRange:
    234                 inputData = self.data[max(0, int(self.topAxis.GetVisibleRange()[0])):int(self.topAxis.GetVisibleRange()[1]) + 1]
    235             else:
    236                 inputData = self.data
    237             data.extend((x[4] for x in inputData))
    238         return axis.GetRangeFromSequences([0], data)
    239 
    240     def AddGrid(self):
    241         Grid(parent=self.graphArea, axis=self.leftAxis, orientation=axis.AxisOrientation.VERTICAL, minFactor=1.0, maxFactor=0.0, color=(1.0, 1.0, 1.0, 0.1), zeroColor=(1, 1, 1, 1))
    242         Grid(parent=self.graphArea, axis=self.topAxis, orientation=axis.AxisOrientation.HORIZONTAL, color=(1.0, 1.0, 1.0, 0.1))
    243 
    244     def AddBarGraph(self, animate = False):
    245         if _GetSetting('volume', True):
    246             self.rightAxisLabels.display = True
    247             if _GetSetting('scale_to_data', 1):
    248                 self.rightAxis.ZoomOn(self._GetRightAxisData(True))
    249             if self.barGraph:
    250                 return
    251             data = [ x[4] for x in self.data ]
    252 
    253             def getHint(index):
    254                 point = self.data[index]
    255                 return localization.GetByLabel('UI/PriceHistory/VolumeHint', date=FmtDate(point[0], 'ln'), orders=FmtAmt(point[5], 'sn'), quantity=FmtAmt(point[4], 'sn'))
    256 
    257             self.barGraph = BarGraph(parent=self.graphArea, categoryAxis=self.topAxis, valueAxis=self.rightAxis, barSizeMinMax=(2, 20), values=data, hint=DynamicHint(getHint), color=graphColors[0], hoverColor=graphColors[0][0:3] + (2,))
    258             if animate:
    259                 self.barGraph.Animate(animations.AnimationType.FADE, animations.AnimationDynamics.SIMULTANEOUS, 1.0)
    260         else:
    261             if self.barGraph:
    262                 self.barGraph.Close()
    263                 self.barGraph = None
    264             self.rightAxisLabels.display = False
    265 
    266     def AddMovingAverage5(self):
    267         if _GetSetting('movingavg5', True):
    268             if self.movingAvg5:
    269                 return
    270             closing = [ x[3] for x in self.data ]
    271             avg5Data = MovingAvg(closing, 5)
    272             idx = 0
    273             if self.median:
    274                 idx += 1
    275             if self.lowHigh:
    276                 idx += 1
    277             self.movingAvg5 = LineGraph(parent=self.graphArea, categoryAxis=self.topAxis, valueAxis=self.leftAxis, values=avg5Data, lineColor=graphColors[2], idx=idx)
    278         elif self.movingAvg5:
    279             self.movingAvg5.Close()
    280             self.movingAvg5 = None
    281 
    282     def AddMovingAverage20(self):
    283         if _GetSetting('movingavg20', True):
    284             if self.movingAvg20:
    285                 return
    286             closing = [ x[3] for x in self.data ]
    287             avg20Data = MovingAvg(closing, 20)
    288             idx = 0
    289             if self.median:
    290                 idx += 1
    291             if self.lowHigh:
    292                 idx += 1
    293             if self.movingAvg5:
    294                 idx += 1
    295             self.movingAvg20 = LineGraph(parent=self.graphArea, categoryAxis=self.topAxis, valueAxis=self.leftAxis, values=avg20Data, lineColor=graphColors[4], idx=idx)
    296         elif self.movingAvg20:
    297             self.movingAvg20.Close()
    298             self.movingAvg20 = None
    299 
    300     def AddMinMaxGraph(self):
    301         if _GetSetting('minmax', True):
    302             if self.lowHigh:
    303                 return
    304             data = [ (x[1], x[2]) for x in self.data ]
    305             idx = 1 if self.median else 0
    306             self.lowHigh = LowHighValueGraph(parent=self.graphArea, categoryAxis=self.topAxis, valueAxis=self.leftAxis, values=data, idx=idx)
    307         elif self.lowHigh:
    308             self.lowHigh.Close()
    309             self.lowHigh = None
    310 
    311     def AddDonchianChannel(self):
    312         if _GetSetting('donchian', True):
    313             if self.donchian:
    314                 return
    315             data = ([ x[1] for x in self.data ], [ y[2] for y in self.data ])
    316             idx = 0
    317             if self.median:
    318                 idx += 1
    319             if self.lowHigh:
    320                 idx += 1
    321             if self.movingAvg5:
    322                 idx += 1
    323             if self.movingAvg20:
    324                 idx += 1
    325             self.donchian = DonchianChannel(parent=self.graphArea, categoryAxis=self.topAxis, valueAxis=self.leftAxis, values=data, graphColor=(0.8,
    326              0.8,
    327              1.0,
    328              0.12249999999999998), state=uiconst.UI_DISABLED, idx=idx)
    329         elif self.donchian:
    330             self.donchian.Close()
    331             self.donchian = None
    332 
    333     def AddMiniMap(self):
    334         data = [ x[3] for x in self.data ]
    335         categoryAxis = axis.MonthlyTimeAxis([ x[0] for x in self.data ])
    336         valueAxis = axis.AutoTicksAxis((min(data), max(data)), behavior=axis.AXIS_FROM_ZERO)
    337         miniMap = MiniMap(name='MiniMap', parent=self.miniMapContainer, align=uiconst.TOALL, controlAxis=self.topAxis, padTop=4)
    338         miniMap.AddAxis(axis.AxisOrientation.HORIZONTAL, categoryAxis)
    339         miniMap.AddAxis(axis.AxisOrientation.VERTICAL, valueAxis, 1.0, 0.0)
    340         Grid(parent=miniMap, axis=categoryAxis, orientation=axis.AxisOrientation.HORIZONTAL, color=(1.0, 1.0, 1.0, 0.1))
    341         AreaGraph(parent=miniMap, categoryAxis=categoryAxis, valueAxis=valueAxis, values=data, graphColor=(1.0,
    342          1.0,
    343          1.0,
    344          0.09), state=uiconst.UI_DISABLED)
    345 
    346     def AddMedianPoints(self, animate = False):
    347         if _GetSetting('mediandayprice', True):
    348             if self.median:
    349                 return
    350 
    351             def getHint(index):
    352                 point = self.data[index]
    353                 return '%s\n%s' % (FmtDate(point[0], 'ln'), FmtAmt(point[3], 'sn'))
    354 
    355             data = [ x[3] for x in self.data ]
    356             self.median = PointGraph(parent=self.graphArea, categoryAxis=self.topAxis, valueAxis=self.leftAxis, values=data, hint=DynamicHint(getHint), pointSizeMinMax=(5, 10), pointColor=graphColors[3], idx=0)
    357             if animate:
    358                 self.median.Animate(animations.AnimationType.GROW, animations.AnimationDynamics.ALONG_AXIS, 1.0)
    359         elif self.median:
    360             self.median.Close()
    361             self.median = None
    362 
    363     def _RebuildLegend(self):
    364         if self.legend is None:
    365             return
    366         self.legend.Flush()
    367         if self.median:
    368             self.legend.AddItem(self.median.GetLegendItem(), localization.GetByLabel('UI/PriceHistory/MedianDayPrice'))
    369         if self.lowHigh:
    370             self.legend.AddItem(self.lowHigh.GetLegendItem(), localization.GetByLabel('UI/PriceHistory/MinMax'))
    371         if self.movingAvg5:
    372             numDays = localization.GetByLabel('/Carbon/UI/Common/WrittenDateTimeQuantityShort/Day', value=5)
    373             self.legend.AddItem(self.movingAvg5.GetLegendItem(), localization.GetByLabel('UI/PriceHistory/MovingAvg', numDays=numDays))
    374         if self.movingAvg20:
    375             numDays = localization.GetByLabel('/Carbon/UI/Common/WrittenDateTimeQuantityShort/Day', value=20)
    376             self.legend.AddItem(self.movingAvg20.GetLegendItem(), localization.GetByLabel('UI/PriceHistory/MovingAvg', numDays=numDays))
    377         if self.donchian:
    378             self.legend.AddItem(self.donchian.GetLegendItem(), localization.GetByLabel('UI/PriceHistory/DonchianChannel'))
    379         if self.barGraph:
    380             self.legend.AddItem(self.barGraph.GetLegendItem(), localization.GetByLabel('UI/PriceHistory/Volume'))