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

buyMultiFromBase.py (28465B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\market\buyMultiFromBase.py
      2 from carbonui.control.buttonIcon import ButtonIcon
      3 from carbonui.control.combo import Combo
      4 from collections import Counter
      5 import blue
      6 import eveLocalization
      7 import evetypes
      8 import localization
      9 import locks
     10 import log
     11 import uthread
     12 from carbon.common.script.util.format import FmtAmt
     13 from carbon.common.script.util.timerstuff import AutoTimer
     14 from carbonui import uiconst
     15 from carbonui.control.combo import Combo
     16 from carbonui.control.singlelineedits.singleLineEditInteger import SingleLineEditInteger
     17 from carbonui.primitives.layoutGrid import LayoutGrid
     18 from carbonui.primitives.sprite import Sprite
     19 from carbonui.uicore import uicore
     20 from carbonui.util.various_unsorted import GetClipboardData
     21 from carbonui.util.various_unsorted import SortListOfTuples
     22 from collections import Counter
     23 from eve.client.script.ui.control.eveLabel import EveLabelLargeBold
     24 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel
     25 from eve.client.script.ui.control.infoIcon import MoreExtraInfoInTooltip
     26 from eve.client.script.ui.shared.fitting.fittingInfoTooltip import LoadFittingInfoTooltip
     27 from eve.client.script.ui.shared.market import GetTypeIDFromDragItem
     28 from eve.client.script.ui.shared.market.buySellMultiBase import SellBuyItemsWindow, COL_RED
     29 from eve.client.script.ui.shared.market.buytItemEntry import BuyItemContainer
     30 from eve.client.script.util.contractutils import FmtISKWithDescription
     31 from eve.common.lib import appConst as const
     32 from eve.common.script.util.eveFormat import FmtISK
     33 from eveexceptions.exceptionEater import ExceptionEater
     34 from inventorycommon.const import typePlex
     35 from inventorycommon.util import GetPackagedVolume
     36 from localization import GetByLabel
     37 from marketutil.const import MAX_ORDER_PRICE
     38 from marketutil.orderInfo import GetBuyItemInfo
     39 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     40 from textImporting import CleanText, GetNameFunc
     41 from textImporting.importMultibuy import ImportMultibuy
     42 from uthread2 import BufferedCall
     43 from utillib import KeyVal
     44 ACTION_ICON = 'res:/UI/Texture/classes/UtilMenu/BulletIcon.png'
     45 MILLION = 1000000
     46 
     47 class MultiBuy(SellBuyItemsWindow):
     48     __guid__ = 'form.BuyItems'
     49     default_iconNum = 'res:/UI/Texture/classes/MultiSell/multiBuy.png'
     50     default_windowID = 'MultiBuy'
     51     default_name = 'multiBuy'
     52     captionTextPath = 'UI/Inventory/ItemActions/MultiBuy'
     53     scrollId = 'MultiBuyScroll'
     54     tradeForCorpSettingConfig = 'buyUseCorp'
     55     tradeTextPath = 'UI/Market/MarketQuote/CommandBuy'
     56     orderCap = 'MultiBuyOrderCap'
     57     tradeOnConfirm = False
     58     dropLabelPath = 'UI/Market/Marketbase/DropItemsToAddToBuy'
     59     cannotBeTradeLabelPath = 'UI/Market/MarketQuote/CannotBeBought'
     60     badDeltaWarningPath = 'UI/Market/MarketQuote/MultiBuyTypesAboveAverage'
     61     importHintLabelPath = 'UI/Market/MarketQuote/ImportShoppingListHint'
     62     exportHintLabelPath = 'UI/Market/MarketQuote/ExportShoppingListHint'
     63     corpCheckboxTop = 0
     64     numbersGridTop = 1
     65     belowColor = '<color=0xff00ff00>'
     66     aboveColor = '<color=0xffff5050>'
     67     __notifyevents__ = ['OnOwnOrdersChanged', 'OnSessionChanged']
     68 
     69     def ApplyAttributes(self, attributes):
     70         self.blinkEditBG = None
     71         SellBuyItemsWindow.ApplyAttributes(self, attributes)
     72         self.AddBuyAndFitBtn()
     73         self.dropCont.OnDropData = self.DropItems
     74         self.infoCont.height = 44
     75         self.totalAmt.LoadTooltipPanel = self.LoadTotalTooltip
     76         self.itemsScroll.OnDropData = self.DropItems
     77         self.AddLoadingWheel()
     78         self.DrawStationWarning()
     79         self.AddToLocationCont()
     80         self.StartAddItemsThread()
     81 
     82     def AddBuyAndFitBtn(self):
     83         self.buyAndFitBtn = self.btnGroup.AddButton(GetByLabel('UI/Market/MarketQuote/BuyAndFit'), self.BuyAndFit)
     84         self.buyAndFitBtn.SetOrder(0)
     85         self.buyAndFitBtn.LoadTooltipPanel = lambda tooltipPanel, *args: LoadFittingInfoTooltip(tooltipPanel, self.fitting)
     86         if not self.fitting:
     87             self.buyAndFitBtn.display = False
     88         self.btnGroup.ResetLayout()
     89 
     90     def InitializeVariables(self, attributes):
     91         SellBuyItemsWindow.InitializeVariables(self, attributes)
     92         self.entriesInScrollByTypeID = {}
     93         self.orderMultiplier = 1
     94         self.activeOrders = set()
     95         self.orderDisabled = False
     96         self.preItems = self.GetKeyValsForWantedTypes(attributes.get('wantToBuy'))
     97         self.reloadingItemsCounter = 0
     98         self.verifyMultiplierTimer = None
     99         self.expiredOrders = {}
    100         self.fitting = attributes.fitting
    101         self.buyAndFitClicked = None
    102         self.fittingPending = False
    103 
    104     def DrawNumbers(self):
    105         self.numbersGrid = LayoutGrid(parent=self.bottomRight, columns=2, align=uiconst.TORIGHT, top=self.numbersGridTop, state=uiconst.UI_PICKCHILDREN, cellSpacing=(10, 0))
    106         self.totalAmt = EveLabelLargeBold(parent=self.numbersGrid, text='', align=uiconst.CENTERRIGHT, padLeft=4, state=uiconst.UI_NORMAL)
    107         self.moreInfoIcon = MoreExtraInfoInTooltip(parent=self.numbersGrid, align=uiconst.CENTERLEFT)
    108         self.moreInfoIcon.LoadTooltipPanel = self.LoadTotalTooltip
    109 
    110     def GetKeyValsForWantedTypes(self, wantToBuy):
    111         keyVals = []
    112         for typeID, qty in wantToBuy.iteritems():
    113             keyVals.append(KeyVal(typeID=typeID, qty=qty))
    114 
    115         return keyVals
    116 
    117     def AddToLocationCont(self):
    118         self.locationCont.padTop = 24
    119         self.locationCont.height = 32
    120         self.orderMultiplierEdit = SingleLineEditInteger(name='orderMultiplierEdit', parent=self.locationCont, align=uiconst.TOPRIGHT, label=GetByLabel('UI/Market/MarketQuote/NumberOfOrders'), adjustWidth=True, minValue=1, maxValue=1000, OnChange=self.OnMultiplierEditChange)
    121         self.stationCombo = Combo(parent=self.locationCont, callback=self.OnStationChanged, width=200, noChoiceLabel=GetByLabel('UI/Market/MarketQuote/NotStationsAvailable'))
    122         self.LoadStationOptions()
    123 
    124     def _AddImportButton(self):
    125         hint = '%s<br><br>%s' % (GetByLabel('UI/Market/MarketQuote/ImportShoppingListOption'), GetByLabel('UI/Market/MarketQuote/ImportShoppingListOptionHint', type1=const.typeVeldspar, type2=const.typeTritanium))
    126         ButtonIcon(name='importMenu', menuAlign=uiconst.TOPLEFT, parent=self.mainCont, align=uiconst.TOPLEFT, pos=(0, 0, 32, 32), func=self.ImportShoppingList, texturePath='res:/UI/Texture/Shared/pasteFrom.png', iconSize=28, hint=hint)
    127 
    128     def ImportShoppingList(self, *args):
    129         localizedDecimal = eveLocalization.GetDecimalSeparator(localization.SYSTEM_LANGUAGE)
    130         localizedSeparator = eveLocalization.GetThousandSeparator(localization.SYSTEM_LANGUAGE)
    131         marketImporter = sm.GetService('marketutils').GetMarketTypeIDFinder()
    132         multibuyImporter = ImportMultibuy(marketImporter, localizedDecimal, localizedSeparator)
    133         text = GetClipboardData()
    134         toAdd, failedLines = multibuyImporter.GetTypesAndQty(text)
    135         self.AddToOrder(toAdd)
    136         if failedLines:
    137             text = '%s<br>' % GetByLabel('UI/SkillQueue/CouldNotReadLines')
    138             text += '<br>'.join(failedLines)
    139             prompt_player_fsd_dialog('CustomInfo', {'info': text}, modal=False)
    140 
    141     def GetExportMenu(self, menuParent):
    142         menuParent.AddIconEntry(icon=ACTION_ICON, text=GetByLabel('UI/Market/MarketQuote/CopyOrderToClipboard'), callback=self.ExportShoppingListLocalized)
    143         if self.AreExtraExportOptionsAvailable():
    144             menuParent.AddIconEntry(icon=ACTION_ICON, text=GetByLabel('UI/Market/MarketQuote/CopyOrderToClipboardEnglish'), callback=self.ExportShoppingList)
    145         menuParent.AddIconEntry(icon=ACTION_ICON, text=GetByLabel('UI/Market/MarketQuote/AddOrderToQuickbar'), callback=self.AddToQuickbar)
    146 
    147     def ExportShoppingList(self, *args):
    148         self._ExportShoppingList(isLocalized=False)
    149 
    150     def ExportShoppingListLocalized(self, *args):
    151         self._ExportShoppingList()
    152 
    153     def _ExportShoppingList(self, isLocalized = True, *args):
    154         listOfLines = []
    155         nameFunc = GetNameFunc(isLocalized)
    156         for item in self.GetItemsIterator():
    157             line = self._GetExportLine(item.typeID, item.GetTotalQty(), item.GetPrice(), nameFunc)
    158             listOfLines.append(line)
    159 
    160         totalLine = self._GetTotalLine(isLocalized)
    161         listOfLines.append(totalLine)
    162         exportText = '\r\n'.join(listOfLines)
    163         blue.pyos.SetClipboardData(exportText)
    164 
    165     def _GetExportLine(self, typeID, qty, price, nameFunc):
    166         decimals = 2
    167         if not price:
    168             itemPriceText = '-'
    169             totalPriceText = '-'
    170         else:
    171             itemPrice = round(price, decimals)
    172             itemPriceText = FmtAmt(itemPrice, showFraction=decimals)
    173             totalPrice = qty * price
    174             totalPrice = round(totalPrice, decimals)
    175             totalPriceText = FmtAmt(totalPrice, showFraction=decimals)
    176         lineParts = [CleanText(nameFunc(typeID)),
    177          str(qty),
    178          itemPriceText,
    179          totalPriceText]
    180         line = '\t'.join(lineParts)
    181         return line
    182 
    183     def _GetTotalLine(self, isLocalized = True):
    184         totalSum = self.GetSum()
    185         totalSumText = FmtAmt(totalSum, showFraction=2)
    186         if isLocalized or not self.AreExtraExportOptionsAvailable():
    187             totalText = GetByLabel('UI/Market/MarketQuote/TotalPrice')
    188         else:
    189             totalText = GetByLabel('UI/Market/MarketQuote/TotalPrice', languageID='en')
    190         totalLine = '\t'.join([totalText,
    191          '',
    192          '',
    193          totalSumText])
    194         return totalLine
    195 
    196     def AddToQuickbar(self):
    197         newFolder = sm.GetService('marketutils').AddNewQuickbarFolder()
    198         if newFolder:
    199             for item in self.GetItemsIterator():
    200                 sm.GetService('marketutils').AddTypeToQuickBar(item.typeID, newFolder.id, fromMarket=False, extraText=item.GetTotalQty())
    201 
    202     def LoadStationOptions(self):
    203         currentSelection = self.stationCombo.GetValue()
    204         stations = self.GetStations()
    205         if currentSelection:
    206             select = currentSelection
    207         elif session.stationid:
    208             select = session.stationid
    209         elif stations:
    210             select = stations[0][1]
    211         else:
    212             select = None
    213         self.stationCombo.LoadOptions(stations, select)
    214         self.stationCombo.Confirm()
    215 
    216     def DrawStationWarning(self):
    217         self.stationWarning = EveLabelLargeBold(parent=self.infoCont, text='', align=uiconst.CENTERBOTTOM)
    218         self.stationWarning.SetRGB(*COL_RED)
    219         self.stationWarning.display = False
    220 
    221     def AddToOrder(self, wantToBuy, fitting = None):
    222         if fitting:
    223             self.fitting = fitting
    224             self.buyAndFitBtn.display = True
    225             self.btnGroup.ResetLayout()
    226         wantedTypes = self.GetKeyValsForWantedTypes(wantToBuy)
    227         self.AddItems(wantedTypes)
    228 
    229     def AddPreItems(self, preItems):
    230         self.AddItems(preItems)
    231 
    232     def AddItems(self, itemsToAdd):
    233         self.reloadingItemsCounter += 1
    234         items = self.CheckOrderAvailability(itemsToAdd)
    235         self.ClearErrorLists()
    236         self.loadingWheel.Show()
    237         for item in items:
    238             self.AddItem(item)
    239 
    240         self.DisplayErrorHints()
    241         self.reloadingItemsCounter -= 1
    242         if self.reloadingItemsCounter < 1:
    243             self.loadingWheel.Hide()
    244 
    245     def AddItem(self, itemKeyVal):
    246         if not self.IsBuyable(itemKeyVal):
    247             if itemKeyVal.typeID == typePlex:
    248                 prompt_player_fsd_dialog('CannotPutPlexInMarketMultiBuy')
    249             return
    250         existingEntry = self.entriesInScrollByTypeID.get(itemKeyVal.typeID, None)
    251         if existingEntry and not existingEntry.destroyed:
    252             existingEntry.AddQtyToEntry(itemKeyVal.qty)
    253         else:
    254             self.DoAddItem(itemKeyVal)
    255 
    256     def AddItemToCollection(self, itemKeyVal, itemEntry):
    257         self.entriesInScrollByTypeID[itemKeyVal.typeID] = itemEntry
    258 
    259     def GetItemEntry(self, itemKeyVal):
    260         itemEntry = BuyItemContainer(typeID=itemKeyVal.typeID, qty=int(itemKeyVal.qty), parentFunc=self.RemoveItem, editFunc=self.OnEntryEdit, stationID=self.baseStationID, orderMultiplier=self.orderMultiplier, dropParentFunc=self.DropItems)
    261         return itemEntry
    262 
    263     def BuyAndFit(self, *args):
    264         self.buyAndFitClicked = True
    265         return self._PerformTrade()
    266 
    267     def Cancel(self, *args):
    268         self.buyAndFitClicked = False
    269         SellBuyItemsWindow.Cancel(self, *args)
    270 
    271     def PerformTrade(self, *args):
    272         self.buyAndFitClicked = False
    273         return self._PerformTrade()
    274 
    275     def _PerformTrade(self):
    276         useCorp = self.TradingForCorp()
    277         buyItemList, failedItems = self.GetValidatedAndFailedTypes()
    278         if not len(buyItemList):
    279             prompt_player_fsd_dialog('uiwarning03')
    280             return
    281         if not self.ContinueAfterWarning(buyItemList):
    282             return
    283         with ExceptionEater():
    284             self.LogBuy(buyItemList)
    285         ordersCreated = sm.GetService('marketQuote').BuyMulti(self.baseStationID, buyItemList, useCorp)
    286         orderIDs = {order.orderID for order in ordersCreated}
    287         self.activeOrders.update(orderIDs)
    288         self.CreateNewBuyOrder(failedItems)
    289         self.VerifyExpiredOrders()
    290 
    291     def GetItemsWithBadDelta(self, buyItemList):
    292         highItems = []
    293         for item in buyItemList:
    294             if item.delta > 1.0:
    295                 highItems.append((item.delta, item))
    296 
    297         return highItems
    298 
    299     def GetOrderDeltaTextForWarning(self):
    300         orderPercentage = self.GetOrderDelta()
    301         orderText = ''
    302         if orderPercentage > 1.0:
    303             percText = GetByLabel('UI/Common/Percentage', percentage=FmtAmt(abs(orderPercentage * 100), showFraction=0))
    304             orderText = GetByLabel('UI/Market/MarketQuote/MultiBuyAboveAverage', percentage=percText)
    305         return orderText
    306 
    307     def GetValidatedAndFailedTypes(self):
    308         allItems = self.GetItems()
    309         failedItemsList = []
    310         buyItemsList = []
    311         for item in allItems:
    312             if item.isUpdating:
    313                 return ([], [])
    314             if item.newBestPrice:
    315                 validatedItem = self.GetValidatedItem(item)
    316                 if validatedItem:
    317                     buyItemsList.append(validatedItem)
    318                     continue
    319             failedBuyInfo = KeyVal(typeID=item.typeID, qty=item.GetTotalQty())
    320             failedItemsList.append(failedBuyInfo)
    321 
    322         return (buyItemsList, failedItemsList)
    323 
    324     def GetValidatedItem(self, item):
    325         if item.isUpdating:
    326             return
    327         price = item.GetPrice()
    328         if not price:
    329             return
    330         if price > MAX_ORDER_PRICE:
    331             return
    332         price = round(price, 2)
    333         qty = item.GetTotalQty()
    334         if qty < 1:
    335             return
    336         validatedItem = GetBuyItemInfo(stationID=item.stationID, typeID=item.typeID, price=price, quantity=qty, minVolume=1, delta=item.GetDelta())
    337         return validatedItem
    338 
    339     def CreateNewBuyOrder(self, failedItems):
    340         self.ChangeOrderUIState(disable=True)
    341         self.RemoveAllItem()
    342         if self.orderMultiplierEdit.GetValue() != 1:
    343             self.BlinkNumOrdersEdit()
    344         self.orderMultiplierEdit.SetValue(1)
    345         if failedItems:
    346             self.AddItems(failedItems)
    347 
    348     def ChangeOrderUIState(self, disable):
    349         if disable:
    350             opacity = 0.5
    351         else:
    352             opacity = 1.0
    353         mainArea = self.GetMainArea()
    354         mainArea.opacity = opacity
    355         self.orderDisabled = disable
    356 
    357     def GetStations(self):
    358         marketDockableLocationsInRegion = sm.GetService('structureDirectory').GetMarketDockableLocationsInRegion()
    359         solarsystemItems = sm.GetService('map').GetSolarsystemItems(session.solarsystemid2, True, False)
    360         marketDockableLocations = {i for i in solarsystemItems if i.itemID in marketDockableLocationsInRegion}
    361         currentStation = session.stationid or session.structureid
    362         stationList = []
    363         for eachStation in marketDockableLocations:
    364             if eachStation.itemID == currentStation:
    365                 continue
    366             sortValue = (eachStation.celestialIndex, eachStation.orbitIndex, eachStation.itemName)
    367             stationList.append((sortValue, (eachStation.itemName, eachStation.itemID)))
    368 
    369         stationList = SortListOfTuples(stationList)
    370         if currentStation:
    371             stationName = cfg.evelocations.Get(currentStation).name
    372             currentStationOption = (GetByLabel('UI/Market/MarketQuote/CurrentStation', stationName=stationName), currentStation)
    373             stationList = [currentStationOption] + stationList
    374         return stationList
    375 
    376     def RemoveItemFromCollection(self, itemEntry):
    377         self.entriesInScrollByTypeID.pop(itemEntry.typeID, None)
    378 
    379     def OnMultiplierEditChange(self, *args):
    380         self.UpdateOrderMultiplierInEntries()
    381         if self.verifyMultiplierTimer:
    382             self.verifyMultiplierTimer.KillTimer()
    383         self.verifyMultiplierTimer = AutoTimer(2000, self.VerifyOrderMultiplier_thread)
    384 
    385     def UpdateOrderMultiplierInEntries(self, force = True):
    386         self.orderMultiplier = self.orderMultiplierEdit.GetValue()
    387         for item in self.GetItemsIterator():
    388             if force or item.orderMultiplier != self.orderMultiplier:
    389                 item.UpdateOrderMultiplier(self.orderMultiplier)
    390 
    391     def VerifyOrderMultiplier_thread(self):
    392         self.verifyMultiplierTimer = None
    393         self.UpdateOrderMultiplierInEntries(force=False)
    394 
    395     def OnEntryEdit(self, *args):
    396         uthread.new(self.UpdateNumbers)
    397 
    398     def UpdateNumbers(self):
    399         totalShown = self.GetSum()
    400         if totalShown >= MILLION:
    401             totalAmtText = '%s (%s)' % (FmtISK(totalShown), FmtISKWithDescription(totalShown, justDesc=True))
    402         else:
    403             totalAmtText = FmtISK(totalShown)
    404         self.moreInfoIcon.display = bool(totalShown)
    405         self.totalAmt.text = totalAmtText
    406         self.totalAmt.SetRGB(*COL_RED)
    407         self.ShowOrHideStationWarning()
    408 
    409     def ShowOrHideStationWarning(self):
    410         currentStation = session.stationid or session.structureid
    411         if self.baseStationID is None:
    412             self.stationWarning.text = GetByLabel('UI/Market/MarketQuote/NoStationSelected')
    413             self.stationWarning.display = True
    414         elif self.baseStationID == currentStation:
    415             self.stationWarning.display = False
    416         else:
    417             self.stationWarning.text = GetByLabel('UI/Market/MarketQuote/StationWarning')
    418             self.stationWarning.display = True
    419 
    420     def GetSum(self):
    421         totalSum = 0
    422         for item in self.GetItemsIterator():
    423             totalSum += item.GetTotalSum()
    424 
    425         return totalSum
    426 
    427     def LoadTotalTooltip(self, tooltipPanel, *args):
    428         totalShown = self.GetSum()
    429         if not totalShown or not self.orderMultiplier:
    430             return
    431         numTypes, numAvailableTypes, volume = self.GetNumTypesAndNumAvailableTypesAndVolume()
    432         pricePerOrder = totalShown / float(self.orderMultiplier)
    433         tooltipPanel.LoadGeneric2ColumnTemplate()
    434         tooltipPanel.cellPadding = (4, 1, 4, 1)
    435         tooltipPanel.AddLabelMedium(text=GetByLabel('UI/Market/MarketQuote/NumOrders'))
    436         tooltipPanel.AddLabelMedium(text=FmtAmt(self.orderMultiplier), align=uiconst.CENTERRIGHT)
    437         tooltipPanel.AddLabelMedium(text=GetByLabel('UI/Market/MarketQuote/PricePerOrder'))
    438         tooltipPanel.AddLabelMedium(text=FmtISK(pricePerOrder), align=uiconst.CENTERRIGHT)
    439         if pricePerOrder >= MILLION:
    440             tooltipPanel.AddCell()
    441             tooltipPanel.AddLabelMedium(text='=%s' % FmtISKWithDescription(pricePerOrder, justDesc=True), align=uiconst.CENTERRIGHT)
    442         buyingText = GetByLabel('UI/Market/MarketQuote/TypesAvailableAndTotalInOrder', numAvailable=numAvailableTypes, numTypes=numTypes)
    443         buyingLabel = tooltipPanel.AddLabelSmall(text=buyingText, align=uiconst.CENTERRIGHT, colSpan=tooltipPanel.columns)
    444         buyingLabel.SetAlpha(0.6)
    445         tooltipPanel.AddSpacer(1, 8, colSpan=tooltipPanel.columns)
    446         deltaText = self.GetOrderDeltaText()
    447         if deltaText:
    448             tooltipPanel.AddLabelMedium(text=deltaText, colSpan=tooltipPanel.columns)
    449         if volume:
    450             tooltipPanel.AddSpacer(1, 8, colSpan=tooltipPanel.columns)
    451             tooltipPanel.AddLabelMedium(text=GetByLabel('UI/Common/Volume'))
    452             tooltipPanel.AddLabelMedium(text=GetByLabel('UI/Inventory/ContainerCapacity', capacity=volume), align=uiconst.CENTERRIGHT)
    453 
    454     def GetOrderDelta(self):
    455         totalPrice = sum((i.GetTotalSum() for i in self.GetItemsIterator(onlyValid=True)))
    456         change = 0
    457         for item in self.GetItemsIterator(onlyValid=True):
    458             price = item.GetTotalSum()
    459             qty = item.GetTotalQty()
    460             avgPriceForQty = qty * item.averagePrice
    461             change += (price - avgPriceForQty) / avgPriceForQty * price / totalPrice
    462 
    463         return change
    464 
    465     def GetOrderDeltaText(self):
    466         percentage = self.GetOrderDelta()
    467         if percentage == 0:
    468             return ''
    469         if percentage < 0:
    470             percColor = self.belowColor
    471             devLabelPath = 'UI/Market/MarketQuote/BelowRegionalAverageWithPercentage'
    472         else:
    473             percColor = self.aboveColor
    474             devLabelPath = 'UI/Market/MarketQuote/AboveRegionalAverageWithPercentage'
    475         percentage = abs(percentage)
    476         percentageText = FmtAmt(percentage * 100, showFraction=1)
    477         percText = '%s%s</color>' % (percColor, GetByLabel('UI/Common/Percentage', percentage=percentageText))
    478         return GetByLabel(devLabelPath, percentage=percText)
    479 
    480     def GetNumTypesAndNumAvailableTypesAndVolume(self):
    481         numTypes = 0
    482         numAvailableTypes = 0
    483         totalVolume = 0
    484         for item in self.GetItemsIterator():
    485             numTypes += 1
    486             if item.GetPrice():
    487                 numAvailableTypes += 1
    488                 vol = item.GetTotalQty() * GetPackagedVolume(item.typeID)
    489                 totalVolume += vol
    490 
    491         return (numTypes, numAvailableTypes, totalVolume)
    492 
    493     def DropItems(self, dragObj, nodes):
    494         if dragObj is None:
    495             return
    496         itemsToAdd = []
    497         for node in nodes:
    498             typeID = GetTypeIDFromDragItem(node)
    499             if typeID:
    500                 itemsToAdd.append(KeyVal(typeID=typeID, qty=1))
    501 
    502         self.AddItems(itemsToAdd)
    503 
    504     def GetTypeIDFromDragItem(self, node):
    505         return GetTypeIDFromDragItem(node)
    506 
    507     def OnStationChanged(self, combo, key, value):
    508         self.baseStationID = value
    509         self.UpdateStationID()
    510 
    511     def UpdateStationID(self):
    512         for item in self.GetItemsIterator():
    513             item.UpdateStationID(self.baseStationID)
    514 
    515         self.UpdateHeaderCount()
    516         self.UpdateNumbers()
    517 
    518     def OnOwnOrdersChanged(self, orders, reason, isCorp):
    519         if reason != 'Expiry':
    520             return
    521         orderIDs = set()
    522         for eachOrder in orders:
    523             self.expiredOrders[eachOrder.orderID] = eachOrder
    524             orderIDs.add(eachOrder.orderID)
    525 
    526         self._ProccessExpiredOrders(orderIDs)
    527 
    528     def _ProccessExpiredOrders(self, orderIDs):
    529         failedItems = []
    530         for eachOrderID in orderIDs:
    531             if eachOrderID not in self.activeOrders:
    532                 continue
    533             eachOrder = self.expiredOrders.get(eachOrderID, None)
    534             if eachOrder and eachOrder.volRemaining != 0:
    535                 failedBuyInfo = KeyVal(typeID=eachOrder.typeID, qty=eachOrder.volRemaining)
    536                 failedItems.append(failedBuyInfo)
    537             self.activeOrders.discard(eachOrderID)
    538             self.TryApplyFittingToBuffer()
    539 
    540         if failedItems:
    541             self.AddItems(failedItems)
    542         if not failedItems and not self.AreStillItemsInWindow():
    543             self.CloseWnd()
    544             return
    545         if self.orderDisabled and not self.activeOrders:
    546             self.ChangeOrderUIState(disable=False)
    547 
    548     def CloseWnd(self):
    549         if self.buyAndFitClicked and self.fitting:
    550             uthread.new(self.TryApplyFittingOnClosing)
    551         self.Close()
    552 
    553     def VerifyExpiredOrders(self):
    554         expiredOrders = set(self.expiredOrders.keys())
    555         expiredOrdersNotProcessed = self.activeOrders.intersection(expiredOrders)
    556         if expiredOrdersNotProcessed:
    557             self._ProccessExpiredOrders(expiredOrdersNotProcessed)
    558 
    559     def AreStillItemsInWindow(self):
    560         if self.activeOrders:
    561             return True
    562         if self.reloadingItemsCounter > 0:
    563             return True
    564         if self.itemList:
    565             return True
    566         return False
    567 
    568     def RemoveAllItem(self):
    569         self.itemsScroll.Flush()
    570         self.itemList = []
    571         self.entriesInScrollByTypeID = {}
    572         self.UpdateNumbers()
    573         self.UpdateHeaderCount()
    574         self.ClearErrorLists()
    575 
    576     def OnSessionChanged(self, isRemote, session, change):
    577         if 'solarsystemid2' in change or 'stationid' in change or 'structureid' in change:
    578             self.LoadStationOptions()
    579 
    580     def IsBuyable(self, itemKeyVal):
    581         buyable = True
    582         if evetypes.GetMarketGroupID(itemKeyVal.typeID) is None:
    583             self.cannotTradeItemList.append(itemKeyVal)
    584             buyable = False
    585         if itemKeyVal.typeID == typePlex:
    586             buyable = False
    587         return buyable
    588 
    589     def BlinkNumOrdersEdit(self):
    590         self.ConstructBlinkBG()
    591         uicore.animations.FadeTo(self.blinkEditBG, 0.0, 0.5, duration=0.5, curveType=uiconst.ANIM_WAVE, loops=2)
    592 
    593     def ConstructBlinkBG(self):
    594         if self.blinkEditBG is None:
    595             self.blinkEditBG = Sprite(name='blinkEditBG', bgParent=self.orderMultiplierEdit, align=uiconst.TOALL, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/classes/InvItem/bgSelected.png', opacity=0.0, idx=0)
    596 
    597     def LogBuy(self, buyItemList):
    598         totalDisplayed = self.totalAmt.text
    599         totalSum = self.GetSum()
    600         multiplier = self.orderMultiplierEdit.GetValue()
    601         buyTextList = []
    602         for eachItem in buyItemList:
    603             text = '[typeID=%s, price=%s, qty=%s]' % (eachItem.typeID, eachItem.price, eachItem.quantity)
    604             buyTextList.append(text)
    605 
    606         buyText = ','.join(buyTextList)
    607         logInfo = 'Multibuy: totalDisplayed=%s, totalSum=%s, multiplier=%s, buyText=%s' % (totalDisplayed,
    608          totalSum,
    609          multiplier,
    610          buyText)
    611         log.LogNotice(logInfo)
    612 
    613     def Close(self, *args, **kwds):
    614         self.verifyMultiplierTimer = None
    615         return SellBuyItemsWindow.Close(self, *args, **kwds)
    616 
    617     def DisplayErrorHints(self):
    618         hintTextList = self.GetErrorHints()
    619         if hintTextList:
    620             hintText = '<br>'.join(hintTextList)
    621             prompt_player_fsd_dialog('CustomInfo', {'info': hintText})
    622 
    623     def AddLoadingWheel(self):
    624         self.loadingWheel = LoadingWheel(parent=self.scrollCont, align=uiconst.CENTER, state=uiconst.UI_DISABLED)
    625         self.loadingWheel.Hide()
    626 
    627     def TryApplyFittingToBuffer(self):
    628         if not self.buyAndFitClicked or not self.fitting:
    629             return
    630         if self.fittingPending:
    631             return
    632         self.fittingPending = True
    633         sm.GetService('loading').ProgressWnd(GetByLabel('UI/Market/MarketQuote/AttemptingToFitShip'), '', 1, 2)
    634         self._TryApplyFittingBuffered()
    635 
    636     @BufferedCall(2000)
    637     def _TryApplyFittingBuffered(self):
    638         self.TryApplyFitting()
    639 
    640     def TryApplyFittingOnClosing(self):
    641         blue.pyos.synchro.SleepWallclock(500)
    642         if self.fittingPending:
    643             return
    644         self.TryApplyFitting()
    645 
    646     def TryApplyFitting(self):
    647         sm.GetService('loading').ProgressWnd(GetByLabel('UI/Market/MarketQuote/AttemptingToFitShip'), '', 1, 2)
    648         with locks.TempLock('FitShipFromBuyAll'):
    649             self.fittingPending = False
    650             self._TryApplyFitting()
    651 
    652     def _TryApplyFitting(self):
    653         failedToLoad = sm.GetService('fittingSvc').LoadFitting(self.fitting, getFailedDict=True)
    654         failedToLoadCounter = Counter({x[0]:x[1] for x in failedToLoad})
    655         if failedToLoadCounter:
    656             prompt_player_fsd_dialog('CustomNotify', {'notify': GetByLabel('UI/Market/MarketQuote/SomeItemsCouldNotBeFitted')})
    657         else:
    658             sm.GetService('ghostFittingSvc').TryExitSimulation()
    659         sm.GetService('loading').ProgressWnd(GetByLabel('UI/Market/MarketQuote/AttemptingToFitShip'), '', 2, 2)