mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Don't lose user selection when updating item. Download covers in a separate thread and update dynamically.
This commit is contained in:
parent
668ee348ce
commit
2ea80eed04
@ -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()
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user