Move store plugins to use a GUI wrapper plugin to keep the plugin system happy. Fix segfault with cover download thread pool.

This commit is contained in:
John Schember 2011-03-01 07:48:33 -05:00
parent 06e3186391
commit 84d8dc78c1
12 changed files with 219 additions and 203 deletions

View File

@ -581,60 +581,41 @@ class PreferencesPlugin(Plugin): # {{{
# }}} # }}}
class StoreBase(Plugin): # {{{
class StorePlugin(Plugin): # {{{
supported_platforms = ['windows', 'osx', 'linux'] supported_platforms = ['windows', 'osx', 'linux']
author = 'John Schember' author = 'John Schember'
type = _('Store') type = _('Store')
# This needs to be changed to (0, 8, 0) # This needs to be changed to (0, 8, 0)
minimum_calibre_version = (0, 4, 118) minimum_calibre_version = (0, 4, 118)
def open(self, gui, parent=None, detail_item=None, external=False): actual_plugin = None
actual_plugin_object = None
def load_actual_plugin(self, gui):
''' '''
Open the store. This method must return the actual interface action plugin object.
:param gui: The main GUI. This will be used to have the job
system start downloading an item from the store.
:param parent: The parent of the store dialog. This is used
to create modal dialogs.
:param detail_item: A plugin specific reference to an item
in the store that the user should be shown.
:param external: When False open an internal dialog with the
store. When True open the users default browser to the store's
web site. :param:`detail_item` should still be respected when external
is True.
''' '''
raise NotImplementedError() mod, cls = self.actual_plugin.split(':')
self.actual_plugin_object = getattr(__import__(mod, fromlist=['1'], level=0), cls)(gui, self.name)
def search(self, query, max_results=10, timeout=60): return self.actual_plugin_object
'''
Searches the store for items matching query. This should def customization_help(self, gui=False):
return items as a generator. if self.actual_plugin_object:
return self.actual_plugin_object.customization_help(gui)
else:
raise NotImplementedError()
:param query: The string query search with. def config_widget(self):
:param max_results: The maximum number of results to return. if self.actual_plugin_object:
:param timeout: The maximum amount of time in seconds to spend download the search results. return self.actual_plugin_object.config_widget()
else:
:return: :class:`calibre.gui2.store.search_result.SearchResult` objects raise NotImplementedError()
item_data is plugin specific and is used in :meth:`open` to open to a specifc place in the store.
''' def save_settings(self, config_widget):
raise NotImplementedError() if self.actual_plugin_object:
return self.actual_plugin_object.save_settings(config_widget)
def get_settings(self): else:
''' raise NotImplementedError()
This is only useful for plugins that implement
:attr:`config_widget` that is the only way to save
settings. This is used by plugins to get the saved
settings and apply when necessary.
:return: A dictionary filled with the settings used
by this plugin.
'''
raise NotImplementedError()
# }}} # }}}

View File

@ -5,7 +5,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import textwrap, os, glob, functools, re import textwrap, os, glob, functools, re
from calibre import guess_type from calibre import guess_type
from calibre.customize import FileTypePlugin, MetadataReaderPlugin, \ from calibre.customize import FileTypePlugin, MetadataReaderPlugin, \
MetadataWriterPlugin, PreferencesPlugin, InterfaceActionBase MetadataWriterPlugin, PreferencesPlugin, InterfaceActionBase, StoreBase
from calibre.constants import numeric_version from calibre.constants import numeric_version
from calibre.ebooks.metadata.archive import ArchiveExtract, get_cbz_metadata from calibre.ebooks.metadata.archive import ArchiveExtract, get_cbz_metadata
from calibre.ebooks.metadata.opf2 import metadata_to_opf from calibre.ebooks.metadata.opf2 import metadata_to_opf
@ -1042,12 +1042,32 @@ plugins += [GoogleBooks]
# }}} # }}}
# Store plugins {{{ # Store plugins {{{
from calibre.gui2.store.amazon_plugin import AmazonKindleStore class StoreAmazonKindleStore(StoreBase):
from calibre.gui2.store.gutenberg_plugin import GutenbergStore name = 'Amazon Kindle'
from calibre.gui2.store.feedbooks_plugin import FeedbooksStore description = _('Kindle books from Amazon')
from calibre.gui2.store.manybooks_plugin import ManyBooksStore actual_plugin = 'calibre.gui2.store.amazon_plugin:AmazonKindleStore'
from calibre.gui2.store.smashwords_plugin import SmashwordsStore
plugins += [AmazonKindleStore, GutenbergStore, FeedbooksStore, ManyBooksStore, SmashwordsStore]
class StoreGutenbergStore(StoreBase):
name = 'Project Gutenberg'
description = _('The first producer of free ebooks.')
actual_plugin = 'calibre.gui2.store.gutenberg_plugin:GutenbergStore'
class StoreFeedbooksStore(StoreBase):
name = 'Feedbooks'
description = _('Read anywhere.')
actual_plugin = 'calibre.gui2.store.feedbooks_plugin:FeedbooksStore'
class StoreManyBooksStore(StoreBase):
name = 'ManyBooks'
description = _('The best ebooks at the best price: free!')
actual_plugin = 'calibre.gui2.store.manybooks_plugin:ManyBooksStore'
class StoreSmashwordsStore(StoreBase):
name = 'Smashwords'
description = _('Your ebook. Your way.')
actual_plugin = 'calibre.gui2.store.smashwords_plugin:SmashwordsStore'
plugins += [StoreAmazonKindleStore, StoreGutenbergStore, StoreFeedbooksStore, StoreManyBooksStore, StoreSmashwordsStore]
# }}} # }}}

View File

@ -8,7 +8,7 @@ from contextlib import closing
from calibre.customize import Plugin, CatalogPlugin, FileTypePlugin, \ from calibre.customize import Plugin, CatalogPlugin, FileTypePlugin, \
MetadataReaderPlugin, MetadataWriterPlugin, \ MetadataReaderPlugin, MetadataWriterPlugin, \
InterfaceActionBase as InterfaceAction, \ InterfaceActionBase as InterfaceAction, \
PreferencesPlugin, StorePlugin PreferencesPlugin, StoreBase
from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin
from calibre.customize.profiles import InputProfile, OutputProfile from calibre.customize.profiles import InputProfile, OutputProfile
from calibre.customize.builtins import plugins as builtin_plugins from calibre.customize.builtins import plugins as builtin_plugins
@ -283,7 +283,7 @@ def preferences_plugins():
def store_plugins(): def store_plugins():
customization = config['plugin_customization'] customization = config['plugin_customization']
for plugin in _initialized_plugins: for plugin in _initialized_plugins:
if isinstance(plugin, StorePlugin): if isinstance(plugin, StoreBase):
if not is_disabled(plugin): if not is_disabled(plugin):
plugin.site_customization = customization.get(plugin.name, '') plugin.site_customization = customization.get(plugin.name, '')
yield plugin yield plugin

View File

@ -8,7 +8,6 @@ from functools import partial
from PyQt4.Qt import Qt, QMenu, QToolButton, QDialog, QVBoxLayout from PyQt4.Qt import Qt, QMenu, QToolButton, QDialog, QVBoxLayout
from calibre.customize.ui import store_plugins
from calibre.gui2.actions import InterfaceAction from calibre.gui2.actions import InterfaceAction
class StoreAction(InterfaceAction): class StoreAction(InterfaceAction):
@ -21,14 +20,14 @@ class StoreAction(InterfaceAction):
self.store_menu = QMenu() self.store_menu = QMenu()
self.store_menu.addAction(_('Search'), self.search) self.store_menu.addAction(_('Search'), self.search)
self.store_menu.addSeparator() self.store_menu.addSeparator()
for x in store_plugins(): for n, p in self.gui.istores.items():
self.store_menu.addAction(x.name, partial(self.open_store, x)) self.store_menu.addAction(n, partial(self.open_store, p))
self.qaction.setMenu(self.store_menu) self.qaction.setMenu(self.store_menu)
def search(self): def search(self):
from calibre.gui2.store.search import SearchDialog from calibre.gui2.store.search import SearchDialog
sd = SearchDialog(self.gui, self.gui) sd = SearchDialog(self.gui.istores, self.gui)
sd.exec_() sd.exec_()
def open_store(self, store_plugin): def open_store(self, store_plugin):
store_plugin.open(self.gui, self.gui) store_plugin.open(self.gui)

View File

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en'
class StorePlugin(object): # {{{
def __init__(self, gui, name):
self.gui = gui
self.name = name
self.base_plugin = None
def open(self, gui, parent=None, detail_item=None, external=False):
'''
Open the store.
:param gui: The main GUI. This will be used to have the job
system start downloading an item from the store.
:param parent: The parent of the store dialog. This is used
to create modal dialogs.
:param detail_item: A plugin specific reference to an item
in the store that the user should be shown.
:param external: When False open an internal dialog with the
store. When True open the users default browser to the store's
web site. :param:`detail_item` should still be respected when external
is True.
'''
raise NotImplementedError()
def search(self, query, max_results=10, timeout=60):
'''
Searches the store for items matching query. This should
return items as a generator.
:param query: The string query search with.
:param max_results: The maximum number of results to return.
:param timeout: The maximum amount of time in seconds to spend download the search results.
:return: :class:`calibre.gui2.store.search_result.SearchResult` objects
item_data is plugin specific and is used in :meth:`open` to open to a specifc place in the store.
'''
raise NotImplementedError()
def get_settings(self):
'''
This is only useful for plugins that implement
:attr:`config_widget` that is the only way to save
settings. This is used by plugins to get the saved
settings and apply when necessary.
:return: A dictionary filled with the settings used
by this plugin.
'''
raise NotImplementedError()
def do_genesis(self):
self.genesis()
def genesis(self):
pass
def config_widget(self):
raise NotImplementedError()
def save_settings(self, config_widget):
raise NotImplementedError()
def customization_help(self, gui=False):
raise NotImplementedError()
# }}}

View File

@ -14,15 +14,13 @@ from lxml import html
from PyQt4.Qt import QUrl from PyQt4.Qt import QUrl
from calibre import browser from calibre import browser
from calibre.customize import StorePlugin from calibre.gui2 import open_url
from calibre.gui2.store import StorePlugin
from calibre.gui2.store.search_result import SearchResult from calibre.gui2.store.search_result import SearchResult
class AmazonKindleStore(StorePlugin): class AmazonKindleStore(StorePlugin):
name = 'Amazon Kindle' def open(self, parent=None, detail_item=None, external=False):
description = _('Kindle books from Amazon')
def open(self, gui, parent=None, detail_item=None):
''' '''
Amazon comes with a number of difficulties. Amazon comes with a number of difficulties.
@ -106,7 +104,6 @@ class AmazonKindleStore(StorePlugin):
The best (I use the term lightly here) solution is to open Amazon.com The best (I use the term lightly here) solution is to open Amazon.com
in the users default browser and set the affiliate id as part of the url. in the users default browser and set the affiliate id as part of the url.
''' '''
from calibre.gui2 import open_url
aff_id = {'tag': 'josbl0e-cpb-20'} aff_id = {'tag': 'josbl0e-cpb-20'}
# Use Kovid's affiliate id 30% of the time. # Use Kovid's affiliate id 30% of the time.
if random.randint(1, 10) in (1, 2, 3): if random.randint(1, 10) in (1, 2, 3):

View File

@ -10,18 +10,14 @@ from contextlib import closing
from lxml import html from lxml import html
from calibre import browser from calibre import browser
from calibre.customize import StorePlugin from calibre.gui2.store import StorePlugin
from calibre.gui2.store.search_result import SearchResult from calibre.gui2.store.basic_config import BasicStoreConfig
from calibre.gui2.store.web_store_dialog import WebStoreDialog
class FeedbooksStore(StorePlugin): class FeedbooksStore(BasicStoreConfig, StorePlugin):
name = 'Feedbooks' def open(self, parent=None, detail_item=None, external=False):
description = _('Read anywhere.') d = WebStoreDialog(self.gui, 'http://m.feedbooks.com/', parent, detail_item)
def open(self, gui, parent=None, detail_item=None):
from calibre.gui2.store.web_store_dialog import WebStoreDialog
d = WebStoreDialog(gui, 'http://m.feedbooks.com/', parent, detail_item)
d.setWindowTitle(self.name) d.setWindowTitle(self.name)
d.set_tags(self.name + ',' + _('store')) d.set_tags(self.name + ',' + _('store'))
d = d.exec_() d = d.exec_()
@ -76,22 +72,3 @@ class FeedbooksStore(StorePlugin):
s.detail_item = id.strip() s.detail_item = id.strip()
yield s yield s
def customization_help(self, gui=False):
return 'Customize the behavior of this store.'
def config_widget(self):
from calibre.gui2.store.basic_config_widget import BasicStoreConfigWidget
return BasicStoreConfigWidget(self)
def save_settings(self, config_widget):
from calibre.gui2.store.basic_config_widget import save_settings
save_settings(config_widget)
def get_settings(self):
from calibre.gui2 import gprefs
settings = {}
settings[self.name + '_tags'] = gprefs.get(self.name + '_tags', self.name + ', store, download')
return settings

View File

@ -10,18 +10,16 @@ from contextlib import closing
from lxml import html from lxml import html
from calibre import browser from calibre import browser
from calibre.customize import StorePlugin from calibre.gui2.store import StorePlugin
from calibre.gui2.store.basic_config import BasicStoreConfig
from calibre.gui2.store.search_result import SearchResult from calibre.gui2.store.search_result import SearchResult
from calibre.gui2.store.web_store_dialog import WebStoreDialog
class GutenbergStore(StorePlugin): class GutenbergStore(BasicStoreConfig, StorePlugin):
name = 'Project Gutenberg'
description = _('The first producer of free ebooks.')
def open(self, gui, parent=None, detail_item=None): def open(self, parent=None, detail_item=None, external=False):
settings = self.get_settings() settings = self.get_settings()
from calibre.gui2.store.web_store_dialog import WebStoreDialog d = WebStoreDialog(self.gui, 'http://m.gutenberg.org/', parent, detail_item)
d = WebStoreDialog(gui, 'http://m.gutenberg.org/', parent, detail_item)
d.setWindowTitle(self.name) d.setWindowTitle(self.name)
d.set_tags(settings.get(self.name + '_tags', '')) d.set_tags(settings.get(self.name + '_tags', ''))
d = d.exec_() d = d.exec_()
@ -51,8 +49,9 @@ class GutenbergStore(StorePlugin):
continue continue
id = url.split('/')[-1] id = url.split('/')[-1]
heading = ''.join(url_a.xpath('text()')) url_a = html.fromstring(html.tostring(url_a))
title, _, author = heading.partition('by ') heading = ''.join(url_a.xpath('//text()'))
title, _, author = heading.rpartition('by ')
author = author.split('-')[0] author = author.split('-')[0]
price = '$0.00' price = '$0.00'
@ -66,22 +65,3 @@ class GutenbergStore(StorePlugin):
s.detail_item = '/ebooks/' + id.strip() s.detail_item = '/ebooks/' + id.strip()
yield s yield s
def customization_help(self, gui=False):
return 'Customize the behavior of this store.'
def config_widget(self):
from calibre.gui2.store.basic_config_widget import BasicStoreConfigWidget
return BasicStoreConfigWidget(self)
def save_settings(self, config_widget):
from calibre.gui2.store.basic_config_widget import save_settings
save_settings(config_widget)
def get_settings(self):
from calibre.gui2 import gprefs
settings = {}
settings[self.name + '_tags'] = gprefs.get(self.name + '_tags', self.name + ', store, download')
return settings

View File

@ -11,18 +11,15 @@ from contextlib import closing
from lxml import html from lxml import html
from calibre import browser from calibre import browser
from calibre.customize import StorePlugin from calibre.gui2.store import StorePlugin
from calibre.gui2.store.basic_config import BasicStoreConfig
from calibre.gui2.store.search_result import SearchResult from calibre.gui2.store.search_result import SearchResult
from calibre.gui2.store.web_store_dialog import WebStoreDialog
class ManyBooksStore(StorePlugin):
name = 'ManyBooks'
description = _('The best ebooks at the best price: free!')
def open(self, gui, parent=None, detail_item=None): class ManyBooksStore(BasicStoreConfig, StorePlugin):
from calibre.gui2.store.web_store_dialog import WebStoreDialog
d = WebStoreDialog(gui, 'http://manybooks.net/', parent, detail_item) def open(self, parent=None, detail_item=None, external=False):
d = WebStoreDialog(self.gui, 'http://manybooks.net/', parent, detail_item)
d.setWindowTitle(self.name) d.setWindowTitle(self.name)
d.set_tags(self.name + ',' + _('store')) d.set_tags(self.name + ',' + _('store'))
d = d.exec_() d = d.exec_()
@ -55,8 +52,9 @@ class ManyBooksStore(StorePlugin):
id = url.split('/')[-1] id = url.split('/')[-1]
id = id.strip() id = id.strip()
heading = ''.join(url_a.xpath('text()')) url_a = html.fromstring(html.tostring(url_a))
title, _, author = heading.partition('by ') heading = ''.join(url_a.xpath('//text()'))
title, _, author = heading.rpartition('by ')
author = author.split('-')[0] author = author.split('-')[0]
price = '$0.00' price = '$0.00'
@ -78,22 +76,3 @@ class ManyBooksStore(StorePlugin):
s.detail_item = '/titles/' + id s.detail_item = '/titles/' + id
yield s yield s
def customization_help(self, gui=False):
return 'Customize the behavior of this store.'
def config_widget(self):
from calibre.gui2.store.basic_config_widget import BasicStoreConfigWidget
return BasicStoreConfigWidget(self)
def save_settings(self, config_widget):
from calibre.gui2.store.basic_config_widget import save_settings
save_settings(config_widget)
def get_settings(self):
from calibre.gui2 import gprefs
settings = {}
settings[self.name + '_tags'] = gprefs.get(self.name + '_tags', self.name + ', store, download')
return settings

View File

@ -16,7 +16,6 @@ from PyQt4.Qt import Qt, QAbstractItemModel, QDialog, QTimer, QVariant, \
QPushButton QPushButton
from calibre import browser from calibre import browser
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
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
@ -29,17 +28,12 @@ COVER_DOWNLOAD_THREAD_TOTAL = 2
class SearchDialog(QDialog, Ui_Dialog): class SearchDialog(QDialog, Ui_Dialog):
def __init__(self, gui, *args): def __init__(self, istores, *args):
QDialog.__init__(self, *args) QDialog.__init__(self, *args)
self.setupUi(self) self.setupUi(self)
# We pass this on to the store plugins so they can
# tell the gui's job system to start downloading an
# item.
self.gui = gui
# We keep a cache of store plugins and reference them by name. # We keep a cache of store plugins and reference them by name.
self.store_plugins = {} self.store_plugins = istores
self.search_pool = SearchThreadPool(SearchThread, SEARCH_THREAD_TOTAL) self.search_pool = SearchThreadPool(SearchThread, SEARCH_THREAD_TOTAL)
# Check for results and hung threads. # Check for results and hung threads.
self.checker = QTimer() self.checker = QTimer()
@ -53,12 +47,11 @@ class SearchDialog(QDialog, Ui_Dialog):
# per search basis. # per search basis.
stores_group_layout = QVBoxLayout() stores_group_layout = QVBoxLayout()
self.stores_group.setLayout(stores_group_layout) self.stores_group.setLayout(stores_group_layout)
for x in store_plugins(): for x in self.store_plugins:
self.store_plugins[x.name] = x cbox = QCheckBox(x)
cbox = QCheckBox(x.name)
cbox.setChecked(True) cbox.setChecked(True)
stores_group_layout.addWidget(cbox) stores_group_layout.addWidget(cbox)
setattr(self, 'store_check_' + x.name, cbox) setattr(self, 'store_check_' + x, cbox)
stores_group_layout.addStretch() stores_group_layout.addStretch()
self.search.clicked.connect(self.do_search) self.search.clicked.connect(self.do_search)
@ -122,7 +115,7 @@ class SearchDialog(QDialog, Ui_Dialog):
self.checker.stop() self.checker.stop()
else: else:
# Stop the checker if not threads are running. # Stop the checker if not threads are running.
if not self.search_pool.threads_running(): if not self.search_pool.threads_running() and not self.search_pool.has_tasks():
self.checker.stop() self.checker.stop()
while self.search_pool.has_results(): while self.search_pool.has_results():
@ -132,7 +125,7 @@ class SearchDialog(QDialog, Ui_Dialog):
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_name].open(self.gui, self, result.detail_item) self.store_plugins[result.store_name].open(self, result.detail_item)
def get_store_checks(self): def get_store_checks(self):
''' '''
@ -156,6 +149,10 @@ class SearchDialog(QDialog, Ui_Dialog):
def stores_select_none(self): def stores_select_none(self):
for check in self.get_store_checks(): for check in self.get_store_checks():
check.setChecked(False) check.setChecked(False)
def closeEvent(self, e):
self.model.closing()
QDialog.closeEvent(self, e)
class GenericDownloadThreadPool(object): class GenericDownloadThreadPool(object):
@ -305,6 +302,9 @@ class Matches(QAbstractItemModel):
self.matches = [] self.matches = []
self.cover_pool = CoverThreadPool(CoverThread, 2) self.cover_pool = CoverThreadPool(CoverThread, 2)
self.cover_pool.start_threads() self.cover_pool.start_threads()
def closing(self):
self.cover_pool.abort()
def clear_results(self): def clear_results(self):
self.matches = [] self.matches = []
@ -315,7 +315,6 @@ class Matches(QAbstractItemModel):
def add_result(self, result): def add_result(self, result):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self.matches.append(result) self.matches.append(result)
self.cover_pool.add_task(result, self.update_result) self.cover_pool.add_task(result, self.update_result)
self.layoutChanged.emit() self.layoutChanged.emit()

View File

@ -12,22 +12,19 @@ from contextlib import closing
from lxml import html from lxml import html
from calibre import browser from calibre import browser
from calibre.customize import StorePlugin from calibre.gui2.store import StorePlugin
from calibre.gui2.store.basic_config import BasicStoreConfig
from calibre.gui2.store.search_result import SearchResult from calibre.gui2.store.search_result import SearchResult
from calibre.gui2.store.web_store_dialog import WebStoreDialog
class SmashwordsStore(StorePlugin): class SmashwordsStore(BasicStoreConfig, StorePlugin):
name = 'Smashwords' def open(self, parent=None, detail_item=None, external=False):
description = _('Your ebook. Your way.')
def open(self, gui, parent=None, detail_item=None):
from calibre.gui2.store.web_store_dialog import WebStoreDialog
aff_id = 'usernone' aff_id = 'usernone'
# Use Kovid's affiliate id 30% of the time. # Use Kovid's affiliate id 30% of the time.
if random.randint(1, 10) in (1, 2, 3): if random.randint(1, 10) in (1, 2, 3):
aff_id = 'kovidgoyal' aff_id = 'kovidgoyal'
d = WebStoreDialog(gui, 'http://www.smashwords.com/?ref=%s' % aff_id, parent, detail_item) d = WebStoreDialog(self.gui, 'http://www.smashwords.com/?ref=%s' % aff_id, parent, detail_item)
d.setWindowTitle(self.name) d.setWindowTitle(self.name)
d.set_tags(self.name + ',' + _('store')) d.set_tags(self.name + ',' + _('store'))
d = d.exec_() d = d.exec_()
@ -78,22 +75,3 @@ class SmashwordsStore(StorePlugin):
s.detail_item = '/books/view/' + id.strip() s.detail_item = '/books/view/' + id.strip()
yield s yield s
def customization_help(self, gui=False):
return 'Customize the behavior of this store.'
def config_widget(self):
from calibre.gui2.store.basic_config_widget import BasicStoreConfigWidget
return BasicStoreConfigWidget(self)
def save_settings(self, config_widget):
from calibre.gui2.store.basic_config_widget import save_settings
save_settings(config_widget)
def get_settings(self):
from calibre.gui2 import gprefs
settings = {}
settings[self.name + '_tags'] = gprefs.get(self.name + '_tags', self.name + ', store, download')
return settings

View File

@ -22,7 +22,7 @@ from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.config import prefs, dynamic from calibre.utils.config import prefs, dynamic
from calibre.utils.ipc.server import Server from calibre.utils.ipc.server import Server
from calibre.library.database2 import LibraryDatabase2 from calibre.library.database2 import LibraryDatabase2
from calibre.customize.ui import interface_actions from calibre.customize.ui import interface_actions, store_plugins
from calibre.gui2 import error_dialog, GetMetadata, open_local_file, \ from calibre.gui2 import error_dialog, GetMetadata, open_local_file, \
gprefs, max_available_height, config, info_dialog, Dispatcher, \ gprefs, max_available_height, config, info_dialog, Dispatcher, \
question_dialog question_dialog
@ -102,6 +102,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
self.device_connected = None self.device_connected = None
self.gui_debug = gui_debug self.gui_debug = gui_debug
self.iactions = OrderedDict() self.iactions = OrderedDict()
# Actions
for action in interface_actions(): for action in interface_actions():
if opts.ignore_plugins and action.plugin_path is not None: if opts.ignore_plugins and action.plugin_path is not None:
continue continue
@ -114,11 +115,24 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
if action.plugin_path is None: if action.plugin_path is None:
raise raise
continue continue
ac.plugin_path = action.plugin_path ac.plugin_path = action.plugin_path
ac.interface_action_base_plugin = action ac.interface_action_base_plugin = action
self.add_iaction(ac) self.add_iaction(ac)
# Stores
self.istores = OrderedDict()
for store in store_plugins():
if opts.ignore_plugins and store.plugin_path is not None:
continue
try:
st = self.init_istore(store)
self.add_istore(st)
except:
# Ignore errors in loading user supplied plugins
import traceback
traceback.print_exc()
if store.plugin_path is None:
raise
continue
def init_iaction(self, action): def init_iaction(self, action):
ac = action.load_actual_plugin(self) ac = action.load_actual_plugin(self)
@ -126,6 +140,13 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
ac.interface_action_base_plugin = action ac.interface_action_base_plugin = action
action.actual_iaction_plugin_loaded = True action.actual_iaction_plugin_loaded = True
return ac return ac
def init_istore(self, store):
st = store.load_actual_plugin(self)
st.plugin_path = store.plugin_path
st.base_plugin = store
store.actual_istore_plugin_loaded = True
return st
def add_iaction(self, ac): def add_iaction(self, ac):
acmap = self.iactions acmap = self.iactions
@ -134,6 +155,14 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
acmap[ac.name] = ac acmap[ac.name] = ac
else: else:
acmap[ac.name] = ac acmap[ac.name] = ac
def add_istore(self, st):
stmap = self.istores
if st.name in stmap:
if st.priority >= stmap[st.name].priority:
stmap[st.name] = st
else:
stmap[st.name] = st
def initialize(self, library_path, db, listener, actions, show_gui=True): def initialize(self, library_path, db, listener, actions, show_gui=True):
@ -155,6 +184,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
for ac in self.iactions.values(): for ac in self.iactions.values():
ac.do_genesis() ac.do_genesis()
for st in self.istores.values():
st.do_genesis()
MainWindowMixin.__init__(self, db) MainWindowMixin.__init__(self, db)
# Jobs Button {{{ # Jobs Button {{{