Don't lose user selection when updating item. Download covers in a separate thread and update dynamically.

This commit is contained in:
John Schember 2011-02-26 19:08:37 -05:00
parent 668ee348ce
commit 2ea80eed04
3 changed files with 84 additions and 11 deletions

View File

@ -59,11 +59,16 @@ class FeedbooksStore(StorePlugin):
price = ''.join(data.xpath('//a[@class="buy"]/text()')) price = ''.join(data.xpath('//a[@class="buy"]/text()'))
if not price: if not price:
price = '$0.00' price = '$0.00'
cover_url = ''
cover_url_img = data.xpath('//img')
if cover_url_img:
cover_url = cover_url_img[0].get('src')
cover_url.split('?')[0]
counter -= 1 counter -= 1
s = SearchResult() s = SearchResult()
s.cover_url = '' s.cover_url = cover_url
s.title = title.strip() s.title = title.strip()
s.author = author.strip() s.author = author.strip()
s.price = price.replace(' ', '').strip() s.price = price.replace(' ', '').strip()

View File

@ -5,12 +5,15 @@ __copyright__ = '2011, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import re import re
import time
from contextlib import closing
from threading import Event, Thread from threading import Event, Thread
from Queue import Queue from Queue import Queue
from PyQt4.Qt import Qt, QAbstractItemModel, QDialog, QTimer, QVariant, \ from PyQt4.Qt import Qt, QAbstractItemModel, QDialog, QTimer, QVariant, \
QModelIndex QModelIndex, QPixmap, QSize
from calibre import browser
from calibre.customize.ui import store_plugins from calibre.customize.ui import store_plugins
from calibre.gui2 import NONE from calibre.gui2 import NONE
from calibre.gui2.store.search_ui import Ui_Dialog from calibre.gui2.store.search_ui import Ui_Dialog
@ -88,11 +91,12 @@ class SearchDialog(QDialog, Ui_Dialog):
if res: if res:
result = res[1] result = res[1]
result.store = res[0] result.store = res[0]
self.results_view.model().add_result(result) self.results_view.model().add_result(result)
def open_store(self, index): def open_store(self, index):
result = self.results_view.model().get_result(index) result = self.results_view.model().get_result(index)
self.store_plugins[result.store].open(self.gui, self, result.item_data) self.store_plugins[result.store].open(self.gui, self, result.detail_item)
class SearchThread(Thread): class SearchThread(Thread):
@ -106,12 +110,52 @@ class SearchThread(Thread):
self.results = results self.results = results
self.abort = abort self.abort = abort
self.timeout = timeout self.timeout = timeout
self.br = browser()
def run(self): def run(self):
for res in self.store_plugin.search(self.query, timeout=self.timeout): try:
if self.abort.is_set(): for res in self.store_plugin.search(self.query, timeout=self.timeout):
return if self.abort.is_set():
self.results.put((self.store_name, res)) return
#if res.cover_url:
# with closing(self.br.open(res.cover_url, timeout=15)) as f:
# res.cover_data = f.read()
self.results.put((self.store_name, res))
except:
pass
class CoverDownloadThread(Thread):
def __init__(self, items, update_callback, timeout=5):
Thread.__init__(self)
self.daemon = True
self.items = items
self.update_callback = update_callback
self.timeout = timeout
self.br = browser()
self._run = True
def abort(self):
self._run = False
def is_running(self):
return self._run
def run(self):
while self._run:
try:
time.sleep(.1)
if not self.items.empty():
item = self.items.get_nowait()
if item and item.cover_url:
with closing(self.br.open(item.cover_url, timeout=self.timeout)) as f:
item.cover_data = f.read()
self.items.task_done()
self.update_callback(item)
except:
continue
class Matches(QAbstractItemModel): class Matches(QAbstractItemModel):
@ -121,15 +165,20 @@ class Matches(QAbstractItemModel):
def __init__(self): def __init__(self):
QAbstractItemModel.__init__(self) QAbstractItemModel.__init__(self)
self.matches = [] self.matches = []
self.cover_download_queue = Queue()
self.cover_download_thread = CoverDownloadThread(self.cover_download_queue, self.update_result)
self.cover_download_thread.start()
def clear_results(self): def clear_results(self):
self.matches = [] self.matches = []
#self.cover_download_queue.queue.clear()
self.reset() self.reset()
def add_result(self, result): def add_result(self, result):
self.layoutAboutToBeChanged.emit()
self.matches.append(result) self.matches.append(result)
self.reset() self.cover_download_queue.put(result)
#self.dataChanged.emit(self.createIndex(self.rowCount() - 1, 0), self.createIndex(self.rowCount() - 1, self.columnCount())) self.layoutChanged.emit()
def get_result(self, index): def get_result(self, index):
row = index.row() row = index.row()
@ -138,6 +187,12 @@ class Matches(QAbstractItemModel):
else: else:
return None return None
def update_result(self, result):
if not result in self.matches:
return
self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit()
def index(self, row, column, parent=QModelIndex()): def index(self, row, column, parent=QModelIndex()):
return self.createIndex(row, column) return self.createIndex(row, column)
@ -150,7 +205,7 @@ class Matches(QAbstractItemModel):
return len(self.matches) return len(self.matches)
def columnCount(self, *args): def columnCount(self, *args):
return 5 return len(self.HEADERS)
def headerData(self, section, orientation, role): def headerData(self, section, orientation, role):
if role != Qt.DisplayRole: if role != Qt.DisplayRole:
@ -177,7 +232,10 @@ class Matches(QAbstractItemModel):
return QVariant(result.store) return QVariant(result.store)
return NONE return NONE
elif role == Qt.DecorationRole: elif role == Qt.DecorationRole:
pass if col == 0 and result.cover_data:
p = QPixmap()
p.loadFromData(result.cover_data)
return QVariant(p)
return NONE return NONE
def data_as_text(self, result, col): def data_as_text(self, result, col):
@ -205,3 +263,4 @@ class Matches(QAbstractItemModel):
descending) descending)
if reset: if reset:
self.reset() self.reset()

View File

@ -32,9 +32,18 @@
<property name="alternatingRowColors"> <property name="alternatingRowColors">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="rootIsDecorated"> <property name="rootIsDecorated">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="uniformRowHeights">
<bool>false</bool>
</property>
<property name="itemsExpandable"> <property name="itemsExpandable">
<bool>false</bool> <bool>false</bool>
</property> </property>