calculator.py (15821B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\neocom\calculator.py 2 import sys 3 import eveLocalization 4 import localization 5 from carbonui import fontconst, uiconst 6 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText 7 from carbonui.primitives.container import Container 8 from carbonui.primitives.containerAutoSize import ContainerAutoSize 9 from carbonui.primitives.layoutGrid import LayoutGrid 10 from carbonui.uicore import uicore 11 from carbonui.control.window import Window 12 from eve.client.script.ui.util import uix 13 from carbonui.control.buttonIcon import ButtonIcon 14 from carbonui.control.button import Button 15 from menu import MenuLabel 16 from player_messaging.client.ui_message import prompt_player_fsd_dialog 17 from decimal import Decimal 18 MINWIDTH = 240 19 20 class Calculator(Window): 21 __guid__ = 'form.Calculator' 22 default_windowID = 'calculator' 23 default_width = MINWIDTH 24 default_height = 160 25 default_left = 0 26 default_top = 0 27 default_captionLabelPath = 'UI/Accessories/Calculator/Calculator' 28 default_iconNum = 'res:/ui/Texture/WindowIcons/calculator.png' 29 default_scope = uiconst.SCOPE_INGAME 30 31 def ApplyAttributes(self, attributes): 32 self.mainCont = None 33 self.buttonGrid = None 34 self.decimalSign = eveLocalization.GetDecimalSeparator(localization.SYSTEM_LANGUAGE) 35 self.digitSign = eveLocalization.GetThousandSeparator(localization.SYSTEM_LANGUAGE) 36 self.PopulateKnownKeys() 37 super(Calculator, self).ApplyAttributes(attributes) 38 self.MakeUnResizeable() 39 self.Layout() 40 41 def Layout(self): 42 self.mainCont = ContainerAutoSize(parent=self.GetMainArea(), align=uiconst.TOTOP, callback=self._OnMainContResized) 43 self.sr.inpt = inpt = SingleLineEditText(name='inpt', parent=ContainerAutoSize(parent=self.mainCont, align=uiconst.TOTOP, top=8, padLeft=8, padRight=8), align=uiconst.TOTOP, top=0, height=32, fontsize=fontconst.EVE_LARGE_FONTSIZE) 44 inpt.SetHistoryVisibility(0) 45 inpt.OnChar = self.OnCharInput 46 inpt.Paste = self.OnInputPasted 47 self.SetInpt('0') 48 hist = settings.public.ui.Get('CalculatorHistory', []) 49 if hist: 50 ops = [ (v, v) for v in hist ] 51 else: 52 ops = [('0', '0')] 53 inpt.LoadCombo('urlcombo', ops, self.OnComboChange) 54 memoryCont = LayoutGrid(parent=ContainerAutoSize(parent=self.mainCont, align=uiconst.TOTOP, top=8, left=8, padRight=8), align=uiconst.CENTER, columns=3, cellSpacing=(8, 8)) 55 self.buttonGrid = grid = LayoutGrid(parent=ContainerAutoSize(parent=self.mainCont, align=uiconst.TOTOP, top=8, left=8, padRight=8, padBottom=8), align=uiconst.TOPLEFT, columns=7, cellSpacing=(4, 4)) 56 buttons = [('7', localization.formatters.FormatNumeric(7)), 57 ('8', localization.formatters.FormatNumeric(8)), 58 ('9', localization.formatters.FormatNumeric(9)), 59 ('divide', localization.GetByLabel('UI/Accessories/Calculator/DivideSymbol')), 60 ('clear', localization.GetByLabel('UI/Accessories/Calculator/ClearSymbol')), 61 ('clearall', localization.GetByLabel('UI/Accessories/Calculator/ClearAllSymbol')), 62 ('4', localization.formatters.FormatNumeric(4)), 63 ('5', localization.formatters.FormatNumeric(5)), 64 ('6', localization.formatters.FormatNumeric(6)), 65 ('times', localization.GetByLabel('UI/Accessories/Calculator/MultiplySymbol')), 66 ('bo', localization.GetByLabel('UI/Accessories/Calculator/BracketOpenSymbol')), 67 ('bc', localization.GetByLabel('UI/Accessories/Calculator/BracketCloseSymbol')), 68 ('1', localization.formatters.FormatNumeric(1)), 69 ('2', localization.formatters.FormatNumeric(2)), 70 ('3', localization.formatters.FormatNumeric(3)), 71 ('minus', localization.GetByLabel('UI/Accessories/Calculator/SubtractSymbol')), 72 ('percent', localization.GetByLabel('UI/Accessories/Calculator/PercentSymbol')), 73 ('square', localization.GetByLabel('UI/Accessories/Calculator/SquareSymbol')), 74 ('0', localization.formatters.FormatNumeric(0)), 75 ('dot', self.decimalSign), 76 ('equal', localization.GetByLabel('UI/Accessories/Calculator/EqualsSymbol')), 77 ('plus', localization.GetByLabel('UI/Accessories/Calculator/AddSymbol')), 78 ('plusminus', localization.GetByLabel('UI/Accessories/Calculator/PlusMinusSymbol')), 79 ('root', u'\u221a\xaf')] 80 for i, (arg, label) in enumerate(buttons): 81 if i % 6 == 4: 82 Container(parent=grid, align=uiconst.TOPLEFT, width=8) 83 Button(name=arg, parent=grid, align=uiconst.TOPLEFT, fixedwidth=32, fixedheight=32, label=label, fontsize=fontconst.EVE_MEDIUM_FONTSIZE, func=lambda c = arg: self.OnBtnClick(c), args=()) 84 85 for i in xrange(1, 7): 86 MemorySlot(parent=memoryCont, align=uiconst.TOPLEFT, index=i, getInputValue=self._GetInputValue, setInputValue=lambda value: self.SetInpt(str(value).replace('.', self.decimalSign), False)) 87 88 self.newNumber = True 89 self._stack = [] 90 self.opStack = [] 91 self.lastOp = 0 92 uicore.registry.SetFocus(self.sr.inpt) 93 94 def _GetInputValue(self): 95 if self.sr.inpt.GetValue() == '': 96 self.SetInpt('0') 97 text = self.sr.inpt.GetValue() 98 return Decimal(text.replace(self.decimalSign, '.')) 99 100 def _OnMainContResized(self): 101 if self.buttonGrid is None: 102 return 103 windowMain = self.GetMainArea() 104 totalHeight = self.header_height + windowMain.padTop + windowMain.padBottom + self.mainCont.height 105 totalWidth = windowMain.padLeft + windowMain.padRight + self.buttonGrid.width + self.buttonGrid.parent.left + self.buttonGrid.parent.padRight 106 self.SetMinSize(size=(totalWidth, totalHeight), refresh=True) 107 108 def SetInpt(self, value, new = True): 109 self.sr.inpt.SetValue(value) 110 self.newNumber = new 111 112 def OnComboChange(self, combo, header, value, *args): 113 self.SetInpt(value, False) 114 115 def OnInputPasted(self, paste, deleteStart = None, deleteEnd = None, forceFocus = False): 116 self.SetInpt('0') 117 for char in paste: 118 self.OnCharInput(ord(char)) 119 120 def OnCharInput(self, char, flag = None): 121 _char = char 122 text = self.sr.inpt.GetText() 123 try: 124 _char = unichr(char) 125 if len(_char) == 1 and _char in '0123456789': 126 if text == '0' or self.newNumber: 127 self.sr.inpt.text = '' 128 self.sr.inpt.Insert(char) 129 self.newNumber = False 130 return True 131 except: 132 sys.exc_clear() 133 134 if _char in self.knownkeys.keys(): 135 _char = self.knownkeys[_char] 136 if _char == 'dot' or type(char) == int and unichr(char) == u'.': 137 if self.decimalSign not in text or self.newNumber: 138 if text == '' or self.newNumber: 139 self.sr.inpt.text = '0' 140 self.sr.inpt.Insert(ord(self.decimalSign)) 141 self.newNumber = False 142 return True 143 self.sr.inpt.Insert(ord(self.decimalSign)) 144 self.newNumber = False 145 else: 146 return True 147 if _char == 'plusminus': 148 if text.startswith('-'): 149 text = text[1:] 150 else: 151 text = '-' + text 152 self.SetInpt(text, False) 153 return True 154 if _char == 'clearall': 155 self._stack = [] 156 self.opStack = [] 157 if _char in ('\x08', 'clear', 'clearall'): 158 self.SetInpt('0') 159 return True 160 if _char in self.prio.keys(): 161 self.Operator(_char, text) 162 if _char == 'equal': 163 hist = settings.public.ui.Get('CalculatorHistory', []) 164 hist.insert(0, text) 165 if len(hist) > 20: 166 hist.pop() 167 settings.public.ui.Set('CalculatorHistory', hist) 168 ops = [ (v, v) for v in hist ] 169 self.sr.inpt.LoadCombo('urlcombo', ops, self.OnComboChange, setvalue=text) 170 return True 171 172 def OnBtnClick(self, arg): 173 try: 174 arg = ord(arg) 175 except Exception: 176 sys.exc_clear() 177 178 self.OnCharInput(arg) 179 180 def CheckOperator(self): 181 if str(self._stack[-1]) in self.prio.keys(): 182 return True 183 return False 184 185 def Calc(self): 186 if len(self._stack) > 0 and self.CheckOperator(): 187 op = self._stack.pop() 188 if op in ('equal', 'bo', 'bc'): 189 self.Calc() 190 if op == 'plus': 191 self.Calc() 192 op1 = self._stack.pop() 193 self.Calc() 194 op2 = self._stack.pop() 195 self._stack.append(op2 + op1) 196 if op == 'minus': 197 self.Calc() 198 op1 = self._stack.pop() 199 self.Calc() 200 op2 = self._stack.pop() 201 self._stack.append(op2 - op1) 202 if op == 'times': 203 self.Calc() 204 op1 = self._stack.pop() 205 self.Calc() 206 op2 = self._stack.pop() 207 self._stack.append(op2 * op1) 208 if op == 'divide': 209 self.Calc() 210 op1 = self._stack.pop() 211 self.Calc() 212 op2 = self._stack.pop() 213 try: 214 self._stack.append(op2 / op1) 215 except ZeroDivisionError as e: 216 sys.exc_clear() 217 prompt_player_fsd_dialog('uiwarning03') 218 self._stack.append(Decimal(0.0)) 219 220 if op == 'square': 221 self.Calc() 222 op1 = self._stack.pop() 223 try: 224 self._stack.append(op1 ** Decimal(2.0)) 225 except OverflowError as e: 226 sys.exc_clear() 227 prompt_player_fsd_dialog('uiwarning03') 228 self._stack.append(Decimal(0.0)) 229 230 if op == 'root': 231 self.Calc() 232 op1 = self._stack.pop() 233 try: 234 self._stack.append(op1 ** Decimal(0.5)) 235 except ValueError as e: 236 sys.exc_clear() 237 prompt_player_fsd_dialog('uiwarning03') 238 self._stack.append(Decimal(0.0)) 239 240 if op == 'percent': 241 self.Calc() 242 op1 = self._stack.pop() 243 if len(self._stack): 244 op2 = self._stack[-1] 245 else: 246 prompt_player_fsd_dialog('uiwarning03') 247 op2 = 0.0 248 self._stack.append(op2 * op1 / Decimal(100.0)) 249 250 def Operator(self, op, number): 251 if not self.newNumber or len(self._stack) == 0: 252 self._stack.append(Decimal(number.replace(self.decimalSign, '.'))) 253 elif self.lastOp in ('plus', 'minus', 'times', 'divide') and op != 'bo': 254 self.opStack[-1] = op 255 return 256 if op != 'bo' and (len(self.opStack) == 0 or self.prio[op] <= self.prio[self.opStack[-1]]): 257 while len(self.opStack) > 0 and self.prio[op] <= self.prio[self.opStack[-1]]: 258 self._stack.append(self.opStack.pop()) 259 260 if op == 'bc': 261 while len(self.opStack) > 0 and self.opStack[-1] != 'bo': 262 self._stack.append(self.opStack.pop()) 263 264 if len(self.opStack): 265 self.opStack.pop() 266 if op in ('percent', 'root', 'square', 'equal', 'bc'): 267 self._stack.append(op) 268 else: 269 self.opStack.append(op) 270 self.Calc() 271 number = str(round(self._stack[-1], 14)) 272 self.SetInpt(number.replace('.', self.decimalSign)) 273 self.lastOp = op 274 275 def PopulateKnownKeys(self): 276 self.knownkeys = {'+': 'plus', 277 '-': 'minus', 278 '*': 'times', 279 '/': 'divide', 280 self.decimalSign: 'dot', 281 '=': 'equal', 282 'r': 'root', 283 's': 'square', 284 'p': 'percent', 285 'm': 'plusminus', 286 '(': 'bo', 287 ')': 'bc', 288 'C': 'clearall', 289 '\r': 'equal'} 290 291 prio = {'plus': 3, 292 'minus': 3, 293 'times': 4, 294 'divide': 4, 295 'square': 5, 296 'root': 5, 297 'bo': 1, 298 'bc': 6, 299 'percent': 4, 300 'equal': 0} 301 302 303 class MemorySlot(LayoutGrid): 304 305 def __init__(self, index, getInputValue, setInputValue, parent = None, align = uiconst.TOPLEFT): 306 self._index = index 307 self._getInputValue = getInputValue 308 self._setInputValue = setInputValue 309 super(MemorySlot, self).__init__(parent=parent, align=align, colums=2) 310 button = Button(parent=self, align=uiconst.TOPLEFT, label=localization.GetByLabel('UI/Accessories/Calculator/AbbreviatedMemory', index=self._index), func=self._OnButtonClick, args=()) 311 button.GetHint = self._GetButtonHint 312 menu = ButtonIcon(parent=self, align=uiconst.CENTER, width=16, height=16, texturePath='res:/UI/Texture/Icons/38_16_190.png', iconSize=16) 313 menu.expandOnLeft = True 314 menu.GetMenu = self._GetMenuOptions 315 316 def _OnButtonClick(self): 317 stored = GetMemoryAt(self._index) 318 if stored is not None: 319 self._setInputValue(stored) 320 321 def _GetButtonHint(self): 322 stored = GetMemoryAt(self._index) 323 if stored is not None: 324 return '%s:<br>%.14G' % (GetMemoryNameAt(self._index), stored) 325 else: 326 return localization.GetByLabel('UI/Accessories/Calculator/EmptyBank', label=GetMemoryNameAt(self._index), empty=localization.GetByLabel('UI/Accessories/Calculator/Empty')) 327 328 def _GetMenuOptions(self): 329 m = [] 330 m.append((MenuLabel('UI/Accessories/Calculator/Set'), self._Set)) 331 stored = GetMemoryAt(self._index) 332 if stored is not None: 333 m.extend([(MenuLabel('UI/Accessories/Calculator/Add'), self._Add), (MenuLabel('UI/Accessories/Calculator/Subtract'), self._Sub), (MenuLabel('UI/Accessories/Calculator/Clear'), self._Clear)]) 334 m.append((MenuLabel('UI/Accessories/Calculator/Annotate'), self._Rename)) 335 return m 336 337 def _Set(self): 338 SetMemoryAt(self._index, self._getInputValue()) 339 340 def _Add(self): 341 current = GetMemoryAt(self._index, default=0.0) 342 value = self._getInputValue() 343 SetMemoryAt(self._index, current + value) 344 345 def _Sub(self): 346 current = GetMemoryAt(self._index, default=0.0) 347 value = self._getInputValue() 348 SetMemoryAt(self._index, current - value) 349 350 def _Clear(self): 351 SetMemoryAt(self._index, None) 352 353 def _Rename(self): 354 retval = uix.HybridWnd(caption=localization.GetByLabel('UI/Accessories/Calculator/Annotate'), windowID='calculatorAnnotate', icon=uiconst.QUESTION, minW=300, minH=100, format=[{'type': 'edit', 355 'setvalue': GetMemoryNameAt(self._index), 356 'labelwidth': 48, 357 'label': localization.GetByLabel('UI/Accessories/Calculator/Name'), 358 'key': 'name', 359 'maxlength': 16, 360 'setfocus': 1}]) 361 if retval: 362 SetMemoryNameAt(self._index, retval['name']) 363 364 365 def GetMemoryAt(index, default = None): 366 return settings.public.ui.Get('CalculatorMem%s' % index, default) 367 368 369 def SetMemoryAt(index, value): 370 settings.public.ui.Set('CalculatorMem%s' % index, value) 371 372 373 def GetMemoryNameAt(index): 374 return settings.public.ui.Get('CalculatorMem%sName' % index, localization.GetByLabel('UI/Accessories/Calculator/Memory', index=index)) 375 376 377 def SetMemoryNameAt(index, name): 378 settings.public.ui.Set('CalculatorMem%sName' % index, name)