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

donutChart.py (5574B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\control\donutChart.py
      2 import math
      3 import logging
      4 from carbonui import uiconst
      5 from carbonui.primitives.container import Container
      6 from carbonui.uianimations import animations
      7 from carbonui.util.color import Color
      8 from eve.client.script.ui.control.donutSegment import DonutSegment
      9 from signals import Signal
     10 logger = logging.getLogger(__name__)
     11 
     12 class SegmentData:
     13 
     14     def __init__(self, value, segmentID = None, hint = None):
     15         self.value = value
     16         self.ratio = 0.0
     17         self.segmentID = segmentID
     18         self.hint = hint
     19 
     20 
     21 class DonutChart(Container):
     22     default_name = 'DonutChart'
     23     default_radius = 50
     24     default_startAngle = -math.pi / 2
     25     default_isClockwise = True
     26     default_lineWidth = 10.0
     27     default_gapSize = 0.005
     28     default_minSegmentSize = 0.02
     29     default_color = (0.5, 0.65, 0.7, 1.0)
     30     default_outputMode = uiconst.OUTPUT_COLOR
     31 
     32     def ApplyAttributes(self, attributes):
     33         Container.ApplyAttributes(self, attributes)
     34         self.radius = attributes.get('radius', self.default_radius)
     35         self.startAngle = attributes.get('startAngle', self.default_startAngle)
     36         self.isClockwise = attributes.get('isClockwise', self.default_isClockwise)
     37         self.gapSize = attributes.get('gapSize', self.default_gapSize)
     38         self.minSegmentSize = attributes.get('minSegmentSize', self.default_minSegmentSize)
     39         self.color = attributes.get('color', self.default_color)
     40         self.lineWidth = attributes.get('lineWidth', self.default_lineWidth)
     41         self.outputMode = attributes.get('outputMode', self.default_outputMode)
     42         self.segmentDataByID = {}
     43         self.segmentsByID = []
     44         self.width = self.height = self.radius * 2
     45         self.on_segment_clicked = Signal(signalName='on_segment_clicked')
     46         self.on_segment_mouse_enter = Signal(signalName='on_segment_mouse_enter')
     47         self.on_segment_mouse_exit = Signal(signalName='on_segment_mouse_exit')
     48 
     49     def AddSegment(self, value, segmentID, hint = None):
     50         self.segmentDataByID[segmentID] = SegmentData(value, segmentID, hint)
     51 
     52     def Reset(self):
     53         self.segmentDataByID = {}
     54         self.segmentsByID = {}
     55 
     56     def SetValues(self, values, segmentIDs = None, animate = False, animDuration = 1.0):
     57         self.Construct(animate, animDuration)
     58 
     59     def Construct(self, animate = True, animDuration = 1.0):
     60         self.Flush()
     61         values = [ data.value for data in self.segmentDataByID.values() ]
     62         if not sum(values) >= 0.9999:
     63             logger.warning('not rendering donut chart because sum of values is too small: %s' % sum(values))
     64             return
     65         self._UpdateSegmentRatios(values)
     66         self.segmentsByID = self.ConstructSegments()
     67         if animate:
     68             self.AnimEntry(animDuration)
     69 
     70     def AnimEntry(self, animDuration):
     71         segments = sorted(self.segmentsByID.values(), key=lambda x: x.angle, reverse=True)
     72         for i, segment in enumerate(segments):
     73             self._AnimEntrySegment(animDuration, segment, i)
     74 
     75     def ConstructSegments(self):
     76         angle = 0.0
     77         segments = {}
     78         for data in self.GetSegmentData():
     79             color = self.GetSegmentColor(data.segmentID, data.value)
     80             segment = DonutSegment(parent=self, segmentID=data.segmentID, radius=self.radius, startAngle=angle, isClockwise=self.isClockwise, angle=2 * math.pi * (data.ratio - self.gapSize), colorStart=color, colorEnd=color, lineWidth=self.lineWidth, hint=data.hint, outputMode=self.outputMode)
     81             segment.on_clicked.connect(self.on_segment_clicked)
     82             segment.on_mouse_enter.connect(self.on_segment_mouse_enter)
     83             segment.on_mouse_exit.connect(self.on_segment_mouse_exit)
     84             segments[data.segmentID] = segment
     85             angle += data.ratio * 2 * math.pi
     86 
     87         return segments
     88 
     89     def _AnimEntrySegment(self, duration, segment, i):
     90         timeOffset = i * 0.025
     91         animations.FadeTo(segment, 0.0, 1.0, duration=duration, timeOffset=timeOffset)
     92         if segment.angle < 0.1 * math.pi:
     93             self._AnimEntrySmallSegment(duration * 0.5, segment, timeOffset)
     94         else:
     95             animations.MorphScalar(segment, 'end', 0.0, 1.0, duration=duration, timeOffset=timeOffset)
     96 
     97     def _AnimEntrySmallSegment(self, duration, segment, timeOffset):
     98         timeOffset += 0.3 * (1.0 - segment.angle / (0.5 * math.pi))
     99         angle = segment.startAngle + segment.angle / 2.0
    100         r = 0.15 * segment.radius
    101         x = r * math.cos(angle)
    102         y = r * math.sin(angle)
    103         animations.MorphScalar(segment, 'left', x, segment.left, duration=duration, timeOffset=timeOffset)
    104         animations.MorphScalar(segment, 'top', y, segment.top, duration=duration, timeOffset=timeOffset)
    105 
    106     def _UpdateSegmentRatios(self, values):
    107         sumTotal = float(sum([ data.value for data in self.segmentDataByID.values() ]))
    108         sumRatio = 0
    109         for data in self.segmentDataByID.values():
    110             data.ratio = max(data.value / sumTotal, self.minSegmentSize)
    111             sumRatio += data.ratio
    112 
    113         for data in self.segmentDataByID.values():
    114             data.ratio /= sumRatio
    115 
    116     def GetSegmentColor(self, segmentID, value):
    117         data = self.segmentDataByID[segmentID]
    118         x = 0.3 + 0.7 * data.ratio
    119         return Color(*self.color).SetBrightness(x).GetRGBA()
    120 
    121     def GetSegmentData(self):
    122         segmentData = self.segmentDataByID.values()
    123         return sorted(segmentData, key=lambda data: data.value)