diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index ac26544207..8490630bb8 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -854,6 +854,17 @@ class ActionStore(InterfaceActionBase): name = 'Store' author = 'John Schember' actual_plugin = 'calibre.gui2.actions.store:StoreAction' + + def customization_help(self, gui=False): + return 'Customize the behavior of the store search.' + + def config_widget(self): + from calibre.gui2.store.config.store import config_widget as get_cw + return get_cw() + + def save_settings(self, config_widget): + from calibre.gui2.store.config.store import save_settings as save + save(config_widget) plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog, ActionConvert, ActionDelete, ActionEditMetadata, ActionView, diff --git a/src/calibre/gui2/store/search/download_thread.py b/src/calibre/gui2/store/search/download_thread.py index 97279d7773..1fc74a5748 100644 --- a/src/calibre/gui2/store/search/download_thread.py +++ b/src/calibre/gui2/store/search/download_thread.py @@ -22,7 +22,7 @@ class GenericDownloadThreadPool(object): at the end of the function. ''' - def __init__(self, thread_type, thread_count): + def __init__(self, thread_type, thread_count=1): self.thread_type = thread_type self.thread_count = thread_count @@ -30,6 +30,9 @@ class GenericDownloadThreadPool(object): self.results = Queue() self.threads = [] + def set_thread_count(self, thread_count): + self.thread_count = thread_count + def add_task(self): ''' This must be implemented in a sub class and this function @@ -92,8 +95,8 @@ class SearchThreadPool(GenericDownloadThreadPool): def __init__(self, thread_count): GenericDownloadThreadPool.__init__(self, SearchThread, thread_count) - def add_task(self, query, store_name, store_plugin, timeout): - self.tasks.put((query, store_name, store_plugin, timeout)) + def add_task(self, query, store_name, store_plugin, max_results, timeout): + self.tasks.put((query, store_name, store_plugin, max_results, timeout)) GenericDownloadThreadPool.add_task(self) @@ -112,8 +115,8 @@ class SearchThread(Thread): def run(self): while self._run and not self.tasks.empty(): try: - query, store_name, store_plugin, timeout = self.tasks.get() - for res in store_plugin.search(query, timeout=timeout): + query, store_name, store_plugin, max_results, timeout = self.tasks.get() + for res in store_plugin.search(query, max_results=max_results, timeout=timeout): if not self._run: return res.store_name = store_name diff --git a/src/calibre/gui2/store/search/models.py b/src/calibre/gui2/store/search/models.py index adc90e3b14..059c1d73ed 100644 --- a/src/calibre/gui2/store/search/models.py +++ b/src/calibre/gui2/store/search/models.py @@ -33,7 +33,7 @@ class Matches(QAbstractItemModel): HEADERS = [_('Cover'), _('Title'), _('Price'), _('DRM'), _('Store')] HTML_COLS = (1, 4) - def __init__(self): + def __init__(self, cover_thread_count=2, detail_thread_count=4): QAbstractItemModel.__init__(self) self.DRM_LOCKED_ICON = QPixmap(I('drm-locked.png')).scaledToHeight(64, @@ -51,8 +51,8 @@ class Matches(QAbstractItemModel): self.matches = [] self.query = '' self.search_filter = SearchFilter() - self.cover_pool = CoverThreadPool(2) - self.details_pool = DetailsThreadPool(4) + self.cover_pool = CoverThreadPool(cover_thread_count) + self.details_pool = DetailsThreadPool(detail_thread_count) self.sort_col = 2 self.sort_order = Qt.AscendingOrder diff --git a/src/calibre/gui2/store/search/search.py b/src/calibre/gui2/store/search/search.py index f9ac45e707..906ba0b4ff 100644 --- a/src/calibre/gui2/store/search/search.py +++ b/src/calibre/gui2/store/search/search.py @@ -18,9 +18,6 @@ from calibre.gui2.store.search.download_thread import SearchThreadPool, \ CacheUpdateThreadPool from calibre.gui2.store.search.search_ui import Ui_Dialog -HANG_TIME = 75000 # milliseconds seconds -TIMEOUT = 75 # seconds - class SearchDialog(QDialog, Ui_Dialog): def __init__(self, istores, parent=None, query=''): @@ -28,13 +25,22 @@ class SearchDialog(QDialog, Ui_Dialog): self.setupUi(self) self.config = JSONConfig('store/search') - self.search_edit.initialize('store_search_search') + + # Loads variables that store various settings. + # This needs to be called soon in __init__ because + # the variables it sets up are used later. + self.load_settings() # We keep a cache of store plugins and reference them by name. self.store_plugins = istores - self.search_pool = SearchThreadPool(4) - self.cache_pool = CacheUpdateThreadPool(2) + + # Setup our worker threads. + self.search_pool = SearchThreadPool(self.search_thread_count) + self.cache_pool = CacheUpdateThreadPool(self.cache_thread_count) + self.results_view.model().cover_pool.set_thread_count(self.cover_thread_count) + self.results_view.model().details_pool.set_thread_count(self.details_thread_count) + # Check for results and hung threads. self.checker = QTimer() self.progress_checker = QTimer() @@ -42,7 +48,7 @@ class SearchDialog(QDialog, Ui_Dialog): # Update store caches silently. for p in self.store_plugins.values(): - self.cache_pool.add_task(p, 30) + self.cache_pool.add_task(p, self.timeout) # Add check boxes for each store so the user # can disable searching specific stores on a @@ -128,7 +134,7 @@ class SearchDialog(QDialog, Ui_Dialog): # Add plugins that the user has checked to the search pool's work queue. for n in store_names: if getattr(self, 'store_check_' + n).isChecked(): - self.search_pool.add_task(query, n, self.store_plugins[n], TIMEOUT) + self.search_pool.add_task(query, n, self.store_plugins[n], self.max_results, self.timeout) self.hang_check = 0 self.checker.start(100) self.pi.startAnimation() @@ -202,11 +208,32 @@ class SearchDialog(QDialog, Ui_Dialog): self.results_view.model().sort_order = self.config.get('sort_order', Qt.AscendingOrder) self.results_view.header().setSortIndicator(self.results_view.model().sort_col, self.results_view.model().sort_order) + def load_settings(self): + # Seconds + self.timeout = self.config.get('timeout', 75) + # Milliseconds + self.hang_time = self.config.get('hang_time', 75) * 1000 + self.max_results = self.config.get('max_results', 10) + + # Number of threads to run for each type of operation + self.search_thread_count = self.config.get('search_thread_count', 4) + self.cache_thread_count = self.config.get('cache_thread_count', 2) + self.cover_thread_count = self.config.get('cover_thread_count', 2) + self.details_thread_count = self.config.get('details_thread_count', 4) + + def config_finished(self): + self.load_settings() + + self.search_pool.set_thread_count(self.search_thread_count) + self.cache_pool.set_thread_count(self.cache_thread_count) + self.results_view.model().cover_pool.set_thread_count(self.cover_thread_count) + self.results_view.model().details_pool.set_thread_count(self.details_thread_count) + def get_results(self): # We only want the search plugins to run # a maximum set amount of time before giving up. self.hang_check += 1 - if self.hang_check >= HANG_TIME: + if self.hang_check >= self.hang_time: self.search_pool.abort() self.checker.stop() else: