From 9e96aac04d437030bcbea40f6d52c50f264ac01c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 4 Jun 2015 14:10:27 -0500 Subject: [PATCH 1/4] Fix situation in which module prices are fetched individually (which the price column). Instead, have them wait in a queue that is processed when the entire fit is called and calculated (with the price pane). Also adds a little refresh icon to know that prices are updating and it's not just blank (might change) --- gui/builtinViewColumns/price.py | 9 ++++++--- gui/display.py | 6 +++--- icons/refresh_small.png | Bin 0 -> 506 bytes service/market.py | 28 ++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 icons/refresh_small.png diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py index 5fe6ef954..be387c8d5 100644 --- a/gui/builtinViewColumns/price.py +++ b/gui/builtinViewColumns/price.py @@ -50,12 +50,15 @@ class Price(ViewColumn): return formatAmount(price, 3, 3, 9, currency=True) def delayedText(self, mod, display, colItem): - def callback(requests): - price = requests[0].price + sMkt = service.Market.getInstance() + def callback(item): + price = sMkt.getPriceNow(item.ID).price colItem.SetText(formatAmount(price, 3, 3, 9, currency=True) if price else "") + colItem.SetImage(-1) display.SetItem(colItem) - service.Market.getInstance().getPrices([mod.item.ID], callback) + sMkt.waitForPrice(mod.item, callback) + return self.fittingView.imageList.GetImageIndex("refresh_small", "icons") def getImageId(self, mod): return -1 diff --git a/gui/display.py b/gui/display.py index 0cfc28854..cd08a8d1e 100644 --- a/gui/display.py +++ b/gui/display.py @@ -249,10 +249,10 @@ class Display(wx.ListCtrl): oldImageId = colItem.GetImage() newText = col.getText(st) if newText is False: - col.delayedText(st, self, colItem) + newImageId = col.delayedText(st, self, colItem) newText = "" - - newImageId = col.getImageId(st) + else: + newImageId = col.getImageId(st) colItem.SetText(newText) colItem.SetImage(newImageId) diff --git a/icons/refresh_small.png b/icons/refresh_small.png new file mode 100644 index 0000000000000000000000000000000000000000..d3087dfc920b1705cdebc5beeba8a62047c99b68 GIT binary patch literal 506 zcmV-LrYk6ae86R!cq$1)rW52v1)b7{)|O}AG6Uw?DO|Ft)k{$F)%(f{RF=l?I+ zlJnnhy4xL`1{54hojLt{-~Wv_SN(53T=hS3efa;Fl|lb2w&(vZ*_{2~XR7ONye`Pu zoA?0e-~T}W{*PZ9b_gaOFw5hT_Y~(tZoT$Qj_p>QLT-Dpnng!_d8sIiCa_~9WpM}{jZm=`Cltb^#NWN0R6i=Yh}{^)FrY1 zts6}Ln^hV9kC-0#zxPDb|Kx?y|5Z~IXW}#f=--za%M<=jKim6%%IU8E6Hm4O?>pJ@ zzvo2be~`FJvceg~cv%O$F0gC1*cmrB?0?kE;Q#uCTK~1P)&8reDgReaQaGRxhpHK8 wAOih+0O;RKWM?MbJPl^eOx0CX$&G|C05z+&w|oy)!1^@s6 literal 0 HcmV?d00001 diff --git a/service/market.py b/service/market.py index 0ea234aaf..951f31456 100644 --- a/service/market.py +++ b/service/market.py @@ -71,6 +71,7 @@ class ShipBrowserWorkerThread(threading.Thread): class PriceWorkerThread(threading.Thread): def run(self): self.queue = Queue.Queue() + self.wait = {} self.processUpdates() def processUpdates(self): @@ -86,9 +87,21 @@ class PriceWorkerThread(threading.Thread): wx.CallAfter(callback) queue.task_done() + # After we fetch prices, go through the list of waiting items and call their callbacks + for price in requests: + callbacks = self.wait.pop(price.typeID, None) + if callbacks: + for callback in callbacks: + wx.CallAfter(callback) + def trigger(self, prices, callbacks): self.queue.put((callbacks, prices)) + def setToWait(self, itemID, callback): + if itemID not in self.wait: + self.wait[itemID] = [] + self.wait[itemID].append(callback) + class SearchWorkerThread(threading.Thread): def run(self): self.cv = threading.Condition() @@ -719,6 +732,21 @@ class Market(): self.priceWorkerThread.trigger(requests, cb) + def waitForPrice(self, item, callback): + """ + Wait for prices to be fetched and callback when finished. This is used with the column prices for modules. + Instead of calling them individually, we set them to wait until the entire fit price is called and calculated + (see GH #290) + """ + + def cb(): + try: + callback(item) + except: + pass + + self.priceWorkerThread.setToWait(item.ID, cb) + def clearPriceCache(self): self.priceCache.clear() deleted_rows = eos.db.clearPrices() From 53c9169043e86b380946fcdce9f173f41575e03c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 5 Jun 2015 15:39:10 -0500 Subject: [PATCH 2/4] Simplified price pane. Will show pricing update label and will only clear it when prices are done. Removed all timer code as it makes it overly complicated and I suspect half of it didn't work as intended anyway --- gui/builtinStatsViews/priceViewFull.py | 56 ++++---------------------- 1 file changed, 7 insertions(+), 49 deletions(-) diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py index 2aef192c8..eba4c134b 100644 --- a/gui/builtinStatsViews/priceViewFull.py +++ b/gui/builtinStatsViews/priceViewFull.py @@ -30,37 +30,13 @@ class PriceViewFull(StatsView): def __init__(self, parent): StatsView.__init__(self) self.parent = parent - self._timerId = wx.NewId() - self._timer = None - self.parent.Bind(wx.EVT_TIMER, self.OnTimer) - self._timerRunsBeforeUpdate = 60 - self._timerRuns = 0 - self._timerIdUpdate = wx.NewId() - self._timerUpdate = None self._cachedShip = 0 self._cachedFittings = 0 self._cachedTotal = 0 - def OnTimer(self, event): - if self._timerId == event.GetId(): - if self._timerRuns >= self._timerRunsBeforeUpdate: - self._timerRuns = 0 - self._timer.Stop() - self.refreshPanel(self.fit) - else: - self.labelEMStatus.SetLabel("Prices update retry in: %d seconds" %(self._timerRunsBeforeUpdate - self._timerRuns)) - self._timerRuns += 1 - if self._timerIdUpdate == event.GetId(): - self._timerUpdate.Stop() - self.labelEMStatus.SetLabel("") - def getHeaderText(self, fit): return "Price" - def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent(text) - return width - def populatePanel(self, contentPanel, headerPanel): contentSizer = contentPanel.GetSizer() self.panel = contentPanel @@ -111,22 +87,11 @@ class PriceViewFull(StatsView): for cargo in fit.cargo: for _ in xrange(cargo.amount): typeIDs.append(cargo.itemID) - if self._timer: - if self._timer.IsRunning(): - self._timer.Stop() + sMkt = service.Market.getInstance() sMkt.getPrices(typeIDs, self.processPrices) self.labelEMStatus.SetLabel("Updating prices...") - if not self._timerUpdate: - self._timerUpdate = wx.Timer(self.parent, self._timerIdUpdate) - if self._timerUpdate: - if not self._timerUpdate.IsRunning(): - self._timerUpdate.Start(1000) - else: - if self._timer: - if self._timer.IsRunning(): - self._timer.Stop() self.labelEMStatus.SetLabel("") self.labelPriceShip.SetLabel("0.0 ISK") self.labelPriceFittings.SetLabel("0.0 ISK") @@ -136,20 +101,13 @@ class PriceViewFull(StatsView): def processPrices(self, prices): shipPrice = prices[0].price - if shipPrice == None: - if not self._timer: - self._timer = wx.Timer(self.parent, self._timerId) - self._timer.Start(1000) - self._timerRuns = 0 - else: - if self._timer: - self._timer.Stop() - - self.labelEMStatus.SetLabel("") - - if shipPrice == None: - shipPrice = 0 modPrice = sum(map(lambda p: p.price or 0, prices[1:])) + + if shipPrice is not None: + self.labelEMStatus.SetLabel("") + else: + shipPrice = 0 + if self._cachedShip != shipPrice: self.labelPriceShip.SetLabel("%s ISK" % formatAmount(shipPrice, 3, 3, 9, currency=True)) self.labelPriceShip.SetToolTip(wx.ToolTip(locale.format('%.2f', shipPrice, 1))) From 6cc6fd9468d30bf309ab38596b46cd39e9efa98c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 6 Jun 2015 22:42:42 -0500 Subject: [PATCH 3/4] Instead of icon, use unicode refresh. Minor issues with image and GUI flickering --- gui/builtinViewColumns/price.py | 4 +--- gui/display.py | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py index be387c8d5..df88d0893 100644 --- a/gui/builtinViewColumns/price.py +++ b/gui/builtinViewColumns/price.py @@ -54,11 +54,9 @@ class Price(ViewColumn): def callback(item): price = sMkt.getPriceNow(item.ID).price colItem.SetText(formatAmount(price, 3, 3, 9, currency=True) if price else "") - colItem.SetImage(-1) - display.SetItem(colItem) + display.SetItem(colItem) sMkt.waitForPrice(mod.item, callback) - return self.fittingView.imageList.GetImageIndex("refresh_small", "icons") def getImageId(self, mod): return -1 diff --git a/gui/display.py b/gui/display.py index cd08a8d1e..707623581 100644 --- a/gui/display.py +++ b/gui/display.py @@ -249,10 +249,10 @@ class Display(wx.ListCtrl): oldImageId = colItem.GetImage() newText = col.getText(st) if newText is False: - newImageId = col.delayedText(st, self, colItem) - newText = "" - else: - newImageId = col.getImageId(st) + col.delayedText(st, self, colItem) + newText = u"\u21bb" + + newImageId = col.getImageId(st) colItem.SetText(newText) colItem.SetImage(newImageId) From 874cf4ef0a30bd2a6d58fc1831bc918ffb5a373e Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 3 Jul 2015 02:37:52 -0400 Subject: [PATCH 4/4] Use old price information if update fails. Add "(!)" to show that price is out of date --- eos/saveddata/price.py | 1 + gui/builtinStatsViews/priceViewFull.py | 5 +---- gui/builtinViewColumns/price.py | 12 ++++++++---- service/market.py | 4 ---- service/price.py | 6 ++---- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/eos/saveddata/price.py b/eos/saveddata/price.py index 4c7de879c..ea9b0ac92 100644 --- a/eos/saveddata/price.py +++ b/eos/saveddata/price.py @@ -26,6 +26,7 @@ class Price(object): def __init__(self, typeID): self.typeID = typeID self.time = 0 + self.price = 0 self.failed = None self.__item = None diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py index eba4c134b..f1d3fbd1d 100644 --- a/gui/builtinStatsViews/priceViewFull.py +++ b/gui/builtinStatsViews/priceViewFull.py @@ -103,10 +103,7 @@ class PriceViewFull(StatsView): shipPrice = prices[0].price modPrice = sum(map(lambda p: p.price or 0, prices[1:])) - if shipPrice is not None: - self.labelEMStatus.SetLabel("") - else: - shipPrice = 0 + self.labelEMStatus.SetLabel("") if self._cachedShip != shipPrice: self.labelPriceShip.SetLabel("%s ISK" % formatAmount(shipPrice, 3, 3, 9, currency=True)) diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py index df88d0893..17c7ade3c 100644 --- a/gui/builtinViewColumns/price.py +++ b/gui/builtinViewColumns/price.py @@ -39,7 +39,7 @@ class Price(ViewColumn): sMkt = service.Market.getInstance() price = sMkt.getPriceNow(stuff.item.ID) - if not price or not price.price: + if not price or not price.price or not price.isValid: return False price = price.price # Set new price variable with what we need @@ -52,9 +52,13 @@ class Price(ViewColumn): def delayedText(self, mod, display, colItem): sMkt = service.Market.getInstance() def callback(item): - price = sMkt.getPriceNow(item.ID).price - colItem.SetText(formatAmount(price, 3, 3, 9, currency=True) if price else "") - display.SetItem(colItem) + price = sMkt.getPriceNow(item.ID) + text = formatAmount(price.price, 3, 3, 9, currency=True) if price.price else "" + if price.failed: text += " (!)" + colItem.SetText(text) + + display.SetItem(colItem) + sMkt.waitForPrice(mod.item, callback) diff --git a/service/market.py b/service/market.py index 951f31456..ef4cf387a 100644 --- a/service/market.py +++ b/service/market.py @@ -706,10 +706,6 @@ class Market(): self.priceCache[typeID] = price - if not price.isValid: - # if the price has expired - price.price = None - return price def getPricesNow(self, typeIDs): diff --git a/service/price.py b/service/price.py index aefd1b14f..78e3e289b 100644 --- a/service/price.py +++ b/service/price.py @@ -71,7 +71,6 @@ class Price(): # Attempt to send request and process it try: - len(priceMap) network = service.Network.getInstance() data = network.request(baseurl, network.PRICES, data) xml = minidom.parse(data) @@ -102,7 +101,7 @@ class Price(): for typeID in priceMap.keys(): priceobj = priceMap[typeID] priceobj.time = time.time() + TIMEOUT - priceobj.failed = None + priceobj.failed = True del priceMap[typeID] except: # all other errors will pass and continue onward to the REREQUEST delay @@ -111,6 +110,5 @@ class Price(): # if we get to this point, then we've got an error. Set to REREQUEST delay for typeID in priceMap.keys(): priceobj = priceMap[typeID] - priceobj.price = 0 priceobj.time = time.time() + REREQUEST - priceobj.failed = None + priceobj.failed = True