overviewPanel.py (14329B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\neocom\wallet\panels\personal\overviewPanel.py 2 import math 3 from carbonui.util.various_unsorted import GetWindowAbove 4 from carbon.common.script.util.format import FmtAmt 5 from carbonui import uiconst 6 from carbonui.control.radioButton import RadioButton 7 from carbonui.control.scrollContainer import ScrollContainer 8 from carbonui.primitives.container import Container 9 from carbonui.primitives.containerAutoSize import ContainerAutoSize 10 from carbonui.primitives.fill import Fill 11 from carbonui.primitives.transform import Transform 12 from carbonui.uianimations import animations 13 from carbonui.uicore import uicore 14 from carbonui.util.color import Color 15 from carbonui.control.button import Button 16 from eve.client.script.ui.control.donutChart import DonutChart 17 from eve.client.script.ui.control.eveLabel import Label, EveLabelMedium, EveCaptionLarge, EveLabelLarge, EveLabelMediumBold 18 from eve.client.script.ui.control.eveWindowUnderlay import ListEntryUnderlay 19 from eve.client.script.ui.shared.neocom.wallet.panels.personal import overviewPanelSignals 20 from eve.client.script.ui.shared.neocom.wallet.transactionOverviewController import TransactionOverviewController 21 from eve.common.lib import appConst 22 from eve.common.script.util.eveFormat import FmtISK 23 from localization import GetByLabel 24 MIN_SEGMENT_SIZE = 0.02 25 INCOME = 1 26 EXPENSES = 2 27 DONUT_RADIUS = 95 28 DONUT_LINEWIDTH = 15.0 29 DONUT_SHADOWWIDTH = 6 30 DONUT_SHADOWOPACITY = 0.3 31 COLOR_BY_REFGROUPID = {appConst.refGroupCorpAlliance: '#5c59d8', 32 appConst.refGroupAgentsAndMissions: '#e68348', 33 appConst.refGroupTrade: '#31c4a1', 34 appConst.refGroupBounty: '#e53a3a', 35 appConst.refGroupIndustry: '#3de53a', 36 appConst.refGroupTransfer: '#e6e048', 37 appConst.refGroupMisc: '#808080', 38 appConst.refGroupHypernetRelay: '#3aa8e5'} 39 40 class OverviewPanel(Container): 41 default_name = 'OverviewPanel' 42 isTabStop = True 43 44 def ApplyAttributes(self, attributes): 45 Container.ApplyAttributes(self, attributes) 46 self.isInitialized = False 47 self.selectedRefGroupID = None 48 overviewPanelSignals.on_entry_selected.connect(self.OnEntrySelected) 49 wnd = GetWindowAbove(self) 50 if wnd: 51 wnd.on_start_scale.connect(self.OnWindowStartScale) 52 wnd.on_end_scale.connect(self.OnWindowEndScale) 53 wnd.on_stacked_changed.connect(self.on_window_stack_changed) 54 wnd.on_display_changed.connect(self.on_window_display_changed) 55 56 def OnWindowEndScale(self, wnd): 57 if not self.display: 58 return 59 animations.FadeTo(self.leftCont, self.leftCont.opacity, 1.0) 60 self.ReconstructDonutChart() 61 self.Update() 62 63 def OnWindowStartScale(self, wnd): 64 if not self.display: 65 return 66 animations.FadeTo(self.leftCont, self.leftCont.opacity, 0.0, duration=0.1) 67 68 def OnEntrySelected(self, refGroupID): 69 self.Update(refGroupID) 70 71 def on_window_stack_changed(self, *args): 72 self.ReconstructDonutChart() 73 self.Update() 74 75 def on_window_display_changed(self, *args): 76 self.ReconstructDonutChart() 77 self.Update() 78 79 def OnTabSelect(self): 80 if not self.isInitialized: 81 self.ConstructLayout() 82 self.isInitialized = True 83 self.ReconstructDonutChart() 84 self.Update() 85 86 def ConstructLayout(self): 87 self.rightCont = Container(name='rightCont', parent=self, align=uiconst.TORIGHT, width=200, padding=(32, 16, 0, 0)) 88 self.leftCont = Container(name='leftCont', parent=self, padLeft=16) 89 self.backButton = Button(name='backButton', parent=self, func=self.OnBack, align=uiconst.BOTTOMRIGHT, hint=GetByLabel('UI/Commands/Back'), texturePath='res:/UI/Texture/Vgs/back.png', iconSize=16, iconPadding=8) 90 self.ConstructRadioButtons() 91 self.entriesCont = ScrollContainer(parent=self.rightCont, align=uiconst.TOALL, state=uiconst.UI_PICKCHILDREN, padTop=8) 92 93 def ReconstructDonutChart(self): 94 if self.destroyed or not self.isInitialized: 95 return 96 self.leftCont.Flush() 97 radius = self.chart_radius 98 self.donutTransform = Transform(name='donutTransform', parent=self.leftCont, align=uiconst.CENTER, pos=(0, 99 0, 100 radius * 2, 101 radius * 2), alignMode=uiconst.TOPLEFT, scalingCenter=(0.5, 0.5)) 102 self.donutChart = DonutChart(parent=self.donutTransform, radius=radius, lineWidth=DONUT_LINEWIDTH, align=uiconst.CENTER, minSegmentSize=MIN_SEGMENT_SIZE) 103 self.donutChart.on_segment_clicked.connect(self.OnSegmentClicked) 104 self.donutChart.on_segment_mouse_enter.connect(self.OnSegmentMouseEnter) 105 self.donutChart.on_segment_mouse_exit.connect(self.OnSegmentMouseExit) 106 self.donutChart.GetSegmentColor = self.GetDonutChartSegmentColor 107 radius = radius + DONUT_SHADOWWIDTH 108 self.donutShadowTransform = Transform(name='donutShadowTransform', parent=self.leftCont, align=uiconst.CENTER, pos=(0, 109 0, 110 2 * radius, 111 2 * radius), alignMode=uiconst.TOPLEFT, scalingCenter=(0.5, 0.5)) 112 self.donutChartShadow = DonutChart(parent=self.donutShadowTransform, radius=radius, lineWidth=DONUT_SHADOWWIDTH, align=uiconst.CENTER, state=uiconst.UI_DISABLED, minSegmentSize=MIN_SEGMENT_SIZE, opacity=0.0, outputMode=uiconst.OUTPUT_GLOW) 113 self.donutChartShadow.GetSegmentColor = self.GetDonutChartSegmentColor 114 self.ConstructDonutLabels() 115 116 @property 117 def chart_radius(self): 118 wnd_width, wnd_height = GetWindowAbove(self).GetAbsoluteSize() 119 w = wnd_width - self.rightCont.width 120 h = wnd_height - 249 121 radius = min(w, h) / 2.0 * 0.9 122 return radius 123 124 def ConstructDonutLabels(self): 125 width, _ = self.leftCont.GetAbsoluteSize() 126 self.donutLabelCont = ContainerAutoSize(name='donutLabelCont', parent=self.donutTransform, align=uiconst.CENTER, width=width - 120, opacity=0.0, idx=0) 127 self.donutCenterCaption = EveCaptionLarge(parent=self.donutLabelCont, align=uiconst.TOTOP, color=Color.WHITE, padBottom=6, bold=False) 128 self.donutCenterTitle = EveLabelMediumBold(parent=self.donutLabelCont, align=uiconst.TOTOP, padBottom=2) 129 self.donutCenterSubCaption = EveLabelMedium(parent=self.donutLabelCont, align=uiconst.TOTOP, opacity=0.5) 130 131 def OnSegmentMouseEnter(self, segmentID): 132 pass 133 134 def OnSegmentMouseExit(self, segmentID): 135 pass 136 137 def OnSegmentClicked(self, transactionGroup): 138 if not transactionGroup.refTypeID: 139 self.Update(transactionGroup.refGroupID) 140 141 def GetDonutChartSegmentColor(self, transactionGroup, value): 142 if not transactionGroup: 143 return (1, 1, 1, 0.1) 144 if transactionGroup.refGroupID in COLOR_BY_REFGROUPID: 145 color = Color.HextoRGBA(COLOR_BY_REFGROUPID[transactionGroup.refGroupID]) 146 if not transactionGroup.refTypeID: 147 return color 148 transactionGroups = [ data.segmentID for data in self.donutChart.GetSegmentData() ] 149 numSegments = len(transactionGroups) 150 if numSegments <= 1: 151 x = 1.0 152 else: 153 x = 0.3 + 0.7 * transactionGroups.index(transactionGroup) / float(numSegments - 1) 154 color = Color(*color) 155 brightness = color.GetBrightness() 156 return color.SetBrightness(x * brightness).GetRGBA() 157 158 def OnBack(self, *args): 159 if self.selectedRefGroupID: 160 self.Update() 161 162 def ConstructRadioButtons(self): 163 self.incomeRadioBtn = OverviewPanelRadioButton(name='incomeRadioBtn', parent=self.rightCont, text=GetByLabel('UI/Wallet/WalletWindow/Income'), groupname='incomeOrExpensesRadioGroup', retval=INCOME, callback=self.OnIncomeExpensesRadioButton, padBottom=6) 164 self.incomeRadioBtn.SetChecked(True, report=False) 165 self.expensesRadioBtn = OverviewPanelRadioButton(name='expensesRadioBtn', parent=self.rightCont, text=GetByLabel('UI/Wallet/WalletWindow/Expenses'), groupname='incomeOrExpensesRadioGroup', retval=EXPENSES, callback=self.OnIncomeExpensesRadioButton, padBottom=6) 166 167 def OnIncomeExpensesRadioButton(self, *args): 168 self.Update() 169 170 def Update(self, refGroupID = None, animate = True): 171 if self.destroyed or not self.isInitialized: 172 return 173 self.selectedRefGroupID = refGroupID 174 self.controller = TransactionOverviewController(self.selectedRefGroupID) 175 self.UpdateDonutChart() 176 self.ConstructEntries() 177 self.UpdateText() 178 if not refGroupID: 179 self.UpdateRadioButtons() 180 self.UpdateBackButtonVisibility() 181 uicore.registry.SetFocus(self) 182 self.AnimEntry() 183 184 def UpdateBackButtonVisibility(self): 185 if self.selectedRefGroupID is None: 186 self.backButton.Hide() 187 self.backButton.OnMouseExit() 188 else: 189 self.backButton.Show() 190 191 def AnimEntry(self): 192 duration = 0.6 193 if self.selectedRefGroupID: 194 startScale = (0.9, 0.9) 195 else: 196 startScale = (1.1, 1.1) 197 animations.Tr2DScaleTo(self.donutTransform, startScale, (1.0, 1.0), duration=duration) 198 animations.FadeTo(self.donutTransform, 0.0, 1.0, duration=duration) 199 animations.FadeTo(self.donutLabelCont, 0.0, 1.0, duration=duration, timeOffset=1.0) 200 offset = 0.2 201 animations.Tr2DScaleTo(self.donutShadowTransform, (0.9, 0.9), (1.0, 1.0), duration=duration - offset, timeOffset=duration + offset) 202 animations.FadeTo(self.donutChartShadow, 0.0, DONUT_SHADOWOPACITY, duration=duration - offset, timeOffset=duration + offset) 203 204 def UpdateText(self): 205 totalIncome = self.controller.GetTotalIncome() 206 totalExpenses = self.controller.GetTotalExpenses() 207 totalAmount = totalIncome if self.IsIncomeSelected() else totalExpenses 208 text = FmtAmt(totalAmount, fmt='sn') 209 self.donutCenterCaption.SetText('<center>%s</center>' % text) 210 if self.IsIncomeSelected(): 211 subCaption = GetByLabel('UI/Wallet/WalletWindow/Last30DaysIncome') 212 else: 213 subCaption = GetByLabel('UI/Wallet/WalletWindow/Last30DaysExpenses') 214 self.donutCenterSubCaption.SetText('<center>%s</center>' % subCaption) 215 title = self.controller.GetGroupName() 216 if title: 217 self.donutCenterTitle.Show() 218 self.donutCenterTitle.SetText('<center>%s</center>' % title) 219 else: 220 self.donutCenterTitle.Hide() 221 222 def UpdateRadioButtons(self): 223 totalIncome = self.controller.GetTotalIncome() 224 totalExpenses = self.controller.GetTotalExpenses() 225 self.incomeRadioBtn.SetISKAmount(totalIncome) 226 self.expensesRadioBtn.SetISKAmount(totalExpenses) 227 228 def ConstructEntries(self): 229 self.entriesCont.Flush() 230 if self.IsIncomeSelected(): 231 transactionGroups = self.controller.GetIncomeTransactionGroups() 232 total = self.controller.GetTotalIncome() 233 else: 234 transactionGroups = self.controller.GetExpenditureTransactionGroups() 235 total = self.controller.GetTotalExpenses() 236 if transactionGroups: 237 for transactionGroup in transactionGroups: 238 ratio = transactionGroup.amount / total if total else 0.0 239 Entry(parent=self.entriesCont, align=uiconst.TOTOP, ratio=ratio, transactionGroup=transactionGroup, color=self.GetDonutChartSegmentColor(transactionGroup, ratio), padBottom=4) 240 241 else: 242 EveLabelMedium(parent=self.entriesCont, align=uiconst.TOTOP, text=GetByLabel('UI/Wallet/WalletWindow/HintNoTransactionsFound'), padLeft=17, opacity=0.75) 243 244 def UpdateDonutChart(self): 245 if self.IsIncomeSelected(): 246 groups = self.controller.GetIncomeTransactionGroups() 247 else: 248 groups = self.controller.GetExpenditureTransactionGroups() 249 self.donutChart.Reset() 250 self.donutChartShadow.Reset() 251 if not groups: 252 self.donutChart.AddSegment(value=1.0, segmentID=None) 253 self.donutChart.Disable() 254 self.donutChartShadow.AddSegment(value=1.0, segmentID=None) 255 else: 256 self.donutChart.Enable() 257 for group in groups: 258 value = math.fabs(group.amount) 259 self.donutChart.AddSegment(value=value, segmentID=group, hint=group.GetHint()) 260 self.donutChartShadow.AddSegment(value=value, segmentID=group) 261 262 self.donutChart.Construct(animate=bool(groups)) 263 self.donutChartShadow.Construct(animate=False) 264 265 def IsIncomeSelected(self): 266 return self.expensesRadioBtn.GetGroupValue() == INCOME 267 268 269 class Entry(Container): 270 default_height = 18 271 default_state = uiconst.UI_NORMAL 272 273 def ApplyAttributes(self, attributes): 274 super(Entry, self).ApplyAttributes(attributes) 275 ratio = attributes.ratio 276 color = attributes.color 277 self.transactionGroup = attributes.transactionGroup 278 self.label = Label(parent=self, align=uiconst.CENTERLEFT, text='%.1f%% %s' % (100 * ratio, self.transactionGroup.name), state=uiconst.UI_DISABLED, color=(1, 1, 1, 1), left=18) 279 gauge = Fill(parent=self, align=uiconst.CENTERLEFT, state=uiconst.UI_DISABLED, color=color, pos=(2, 0, 10, 10)) 280 self.underlay = ListEntryUnderlay(bgParent=self) 281 282 def UpdateAlignment(self, *args, **kwargs): 283 alignment = super(Entry, self).UpdateAlignment(*args, **kwargs) 284 return alignment 285 286 def GetHint(self): 287 return FmtISK(self.transactionGroup.amount) 288 289 def OnClick(self, *args): 290 if not self.transactionGroup.refTypeID: 291 overviewPanelSignals.on_entry_selected(self.transactionGroup.refGroupID) 292 293 def OnMouseEnter(self, *args): 294 self.underlay.hovered = True 295 296 def OnMouseExit(self, *args): 297 self.underlay.hovered = False 298 299 300 class OverviewPanelRadioButton(RadioButton): 301 302 def ConstructLabel(self): 303 super(OverviewPanelRadioButton, self).ConstructLabel() 304 self.iskLabel = EveLabelLarge(parent=self, align=uiconst.TOPLEFT, left=self.label.left, top=20) 305 306 def SetISKAmount(self, amount): 307 text = FmtISK(amount) 308 if amount >= 0: 309 text = '+' + text 310 self.iskLabel.SetText(text)