diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 28dcd5dc34..10bc6c464c 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -11,7 +11,6 @@ from PyQt4.Qt import (QCoreApplication, QIcon, QObject, QTimer, from calibre import prints, plugins, force_unicode from calibre.constants import (iswindows, __appname__, isosx, DEBUG, islinux, filesystem_encoding, get_portable_base) -from calibre.db.legacy import LibraryDatabase from calibre.utils.ipc import gui_socket_address, RC from calibre.gui2 import (ORG_NAME, APP_UID, initialize_file_icon_provider, Application, choose_dir, error_dialog, question_dialog, gprefs) @@ -83,7 +82,6 @@ def find_portable_library(): os.mkdir(lib) def init_qt(args): - from calibre.gui2.ui import Main parser = option_parser() opts, args = parser.parse_args(args) find_portable_library() @@ -99,9 +97,8 @@ def init_qt(args): override = 'calibre-gui' if islinux else None app = Application(args, override_program_name=override) app.file_event_hook = EventAccumulator() - actions = tuple(Main.create_application_menubar()) app.setWindowIcon(QIcon(I('lt.png'))) - return app, opts, args, actions + return app, opts, args def get_default_library_path(): @@ -208,6 +205,7 @@ class GuiRunner(QObject): raise SystemExit(1) def initialize_db_stage2(self, db, tb): + from calibre.db.legacy import LibraryDatabase if db is None and tb is not None: # DB Repair failed @@ -241,6 +239,7 @@ class GuiRunner(QObject): det_msg=traceback.format_exc(), show=True) def initialize_db(self): + from calibre.db.legacy import LibraryDatabase db = None try: db = LibraryDatabase(self.library_path) @@ -307,13 +306,15 @@ def run_in_debug_mode(logpath=None): stderr=subprocess.STDOUT, stdin=open(os.devnull, 'r'), creationflags=creationflags) -def run_gui(opts, args, actions, listener, app, gui_debug=None): +def run_gui(opts, args, listener, app, gui_debug=None): initialize_file_icon_provider() app.load_builtin_fonts(scan_for_fonts=True) if not dynamic.get('welcome_wizard_was_run', False): from calibre.gui2.wizard import wizard wizard().exec_() dynamic.set('welcome_wizard_was_run', True) + from calibre.gui2.ui import Main + actions = tuple(Main.create_application_menubar()) runner = GuiRunner(opts, args, actions, listener, app, gui_debug=gui_debug) ret = app.exec_() if getattr(runner.main, 'run_wizard_b4_shutdown', False): @@ -419,7 +420,7 @@ def main(args=sys.argv): args = ['calibre'] try: - app, opts, args, actions = init_qt(args) + app, opts, args = init_qt(args) except AbortInit: return 1 from calibre.utils.lock import singleinstance @@ -440,10 +441,10 @@ def main(args=sys.argv): except socket.error: cant_start() else: - return run_gui(opts, args, actions, listener, app, + return run_gui(opts, args, listener, app, gui_debug=gui_debug) else: - return run_gui(opts, args, actions, listener, app, + return run_gui(opts, args, listener, app, gui_debug=gui_debug) otherinstance = False try: @@ -454,7 +455,7 @@ def main(args=sys.argv): # On windows only singleinstance can be trusted otherinstance = True if iswindows else False if not otherinstance and not opts.shutdown_running_calibre: - return run_gui(opts, args, actions, listener, app, gui_debug=gui_debug) + return run_gui(opts, args, listener, app, gui_debug=gui_debug) communicate(opts, args) diff --git a/src/calibre/gui2/main_window.py b/src/calibre/gui2/main_window.py index 150c437148..d73cb8d565 100644 --- a/src/calibre/gui2/main_window.py +++ b/src/calibre/gui2/main_window.py @@ -78,15 +78,16 @@ class MainWindow(QMainWindow): @classmethod def create_application_menubar(cls): - mb = QMenuBar(None) - menu = QMenu() - for action in cls.get_menubar_actions(): - menu.addAction(action) - cls.__actions.append(action) - yield action - mb.addMenu(menu) - cls.___menu_bar = mb - cls.___menu = menu + if not cls.__actions: + mb = QMenuBar(None) + menu = QMenu() + for action in cls.get_menubar_actions(): + menu.addAction(action) + cls.__actions.append(action) + mb.addMenu(menu) + cls.___menu_bar = mb + cls.___menu = menu + return cls.__actions @classmethod def get_menubar_actions(cls): diff --git a/src/calibre/gui2/wizard/__init__.py b/src/calibre/gui2/wizard/__init__.py index 572e6a8bfe..519e38d499 100644 --- a/src/calibre/gui2/wizard/__init__.py +++ b/src/calibre/gui2/wizard/__init__.py @@ -14,7 +14,6 @@ from contextlib import closing from PyQt4.Qt import (QWizard, QWizardPage, QPixmap, Qt, QAbstractListModel, QVariant, QItemSelectionModel, SIGNAL, QObject, QTimer, pyqtSignal) from calibre import __appname__, patheq -from calibre.db.legacy import LibraryDatabase from calibre.library.move import MoveLibrary from calibre.constants import (filesystem_encoding, iswindows, plugins, isportable) @@ -29,7 +28,6 @@ from calibre.gui2 import min_available_height, available_width from calibre.utils.config import dynamic, prefs from calibre.gui2 import NONE, choose_dir, error_dialog from calibre.gui2.dialogs.progress import ProgressDialog -from calibre.customize.ui import device_plugins if iswindows: winutil = plugins['winutil'][0] @@ -272,6 +270,7 @@ class Android(Device): @classmethod def commit(cls): + from calibre.customize.ui import device_plugins super(Android, cls).commit() for plugin in device_plugins(include_disabled=True): if hasattr(plugin, 'configure_for_generic_epub_app'): @@ -292,6 +291,7 @@ class AndroidPhoneWithKindle(Android): @classmethod def commit(cls): + from calibre.customize.ui import device_plugins super(Android, cls).commit() for plugin in device_plugins(include_disabled=True): if hasattr(plugin, 'configure_for_kindle_app'): @@ -461,6 +461,11 @@ class KindlePage(QWizardPage, KindleUI): def nextId(self): return FinishPage.ID + def retranslateUi(self, widget): + KindleUI.retranslateUi(self, widget) + if hasattr(self, 'send_email_widget'): + self.send_email_widget.retranslateUi(self.send_email_widget) + class StanzaPage(QWizardPage, StanzaUI): ID = 5 @@ -617,6 +622,7 @@ class Callback(object): _mm = None def move_library(oldloc, newloc, parent, callback_on_complete): + from calibre.db.legacy import LibraryDatabase callback = Callback(callback_on_complete) try: if not os.path.exists(os.path.join(newloc, 'metadata.db')): @@ -710,10 +716,12 @@ class LibraryPage(QWizardPage, LibraryUI): __builtin__.__dict__['_'] = lambda(x): x from calibre.utils.localization import set_translators from calibre.gui2 import qt_app + from calibre.ebooks.metadata.book.base import reset_field_metadata set_translators() qt_app.load_translations() self.retranslate.emit() self.init_languages() + reset_field_metadata() try: lang = prefs['language'].lower()[:2] metadata_plugins = { @@ -728,6 +736,7 @@ class LibraryPage(QWizardPage, LibraryUI): pass def is_library_dir_suitable(self, x): + from calibre.db.legacy import LibraryDatabase try: return LibraryDatabase.exists_at(x) or not os.listdir(x) except: @@ -741,6 +750,7 @@ class LibraryPage(QWizardPage, LibraryUI): return True def change(self): + from calibre.db.legacy import LibraryDatabase x = choose_dir(self, 'database location dialog', _('Select location for books')) if x: diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index dcca9ef5e7..a76d78da0b 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -3,7 +3,7 @@ Created on 25 May 2010 @author: charles ''' -import copy, traceback +import traceback from collections import OrderedDict from calibre.utils.config_base import tweaks @@ -40,67 +40,13 @@ category_icon_map = { 'languages' : 'languages.png', } +# Builtin metadata {{{ -class FieldMetadata(dict): - ''' - key: the key to the dictionary is: - - for standard fields, the metadata field name. - - for custom fields, the metadata field name prefixed by '#' - This is done to create two 'namespaces' so the names don't clash - - label: the actual column label. No prefixing. - - datatype: the type of information in the field. Valid values are listed in - VALID_DATA_TYPES below. - is_multiple: valid for the text datatype. If {}, the field is to be - treated as a single term. If not None, it contains a dict of the form - {'cache_to_list': ',', - 'ui_to_list': ',', - 'list_to_ui': ', '} - where the cache_to_list contains the character used to split the value in - the meta2 table, ui_to_list contains the character used to create a list - from a value shown in the ui (each resulting value must be strip()ed and - empty values removed), and list_to_ui contains the string used in join() - to create a displayable string from the list. - - kind == field: is a db field. - kind == category: standard tag category that isn't a field. see news. - kind == user: user-defined tag category. - kind == search: saved-searches category. - - is_category: is a tag browser category. If true, then: - table: name of the db table used to construct item list - column: name of the column in the normalized table to join on - link_column: name of the column in the connection table to join on. This - key should not be present if there is no link table - category_sort: the field in the normalized table to sort on. This - key must be present if is_category is True - If these are None, then the category constructor must know how - to build the item list (e.g., formats, news). - The order below is the order that the categories will - appear in the tags pane. - - name: the text that is to be used when displaying the field. Column headings - in the GUI, etc. - - search_terms: the terms that can be used to identify the field when - searching. They can be thought of as aliases for metadata keys, but are only - valid when passed to search(). - - is_custom: the field has been added by the user. - - rec_index: the index of the field in the db metadata record. - - is_csp: field contains colon-separated pairs. Must also be text, is_multiple - - ''' - - VALID_DATA_TYPES = frozenset([None, 'rating', 'text', 'comments', 'datetime', - 'int', 'float', 'bool', 'series', 'composite', 'enumeration']) - - # Builtin metadata {{{ - - _field_metadata_prototype = [ +def _builtin_field_metadata(): + # This is a function so that changing the UI language allows newly created + # field metadata objects to have correctly translated labels for builtin + # fields. + return [ ('authors', {'table':'authors', 'column':'name', 'link_column':'author', @@ -388,13 +334,70 @@ class FieldMetadata(dict): 'is_category':False, 'is_csp': False}), ] - # }}} +# }}} + +class FieldMetadata(dict): + ''' + key: the key to the dictionary is: + - for standard fields, the metadata field name. + - for custom fields, the metadata field name prefixed by '#' + This is done to create two 'namespaces' so the names don't clash + + label: the actual column label. No prefixing. + + datatype: the type of information in the field. Valid values are listed in + VALID_DATA_TYPES below. + is_multiple: valid for the text datatype. If {}, the field is to be + treated as a single term. If not None, it contains a dict of the form + {'cache_to_list': ',', + 'ui_to_list': ',', + 'list_to_ui': ', '} + where the cache_to_list contains the character used to split the value in + the meta2 table, ui_to_list contains the character used to create a list + from a value shown in the ui (each resulting value must be strip()ed and + empty values removed), and list_to_ui contains the string used in join() + to create a displayable string from the list. + + kind == field: is a db field. + kind == category: standard tag category that isn't a field. see news. + kind == user: user-defined tag category. + kind == search: saved-searches category. + + is_category: is a tag browser category. If true, then: + table: name of the db table used to construct item list + column: name of the column in the normalized table to join on + link_column: name of the column in the connection table to join on. This + key should not be present if there is no link table + category_sort: the field in the normalized table to sort on. This + key must be present if is_category is True + If these are None, then the category constructor must know how + to build the item list (e.g., formats, news). + The order below is the order that the categories will + appear in the tags pane. + + name: the text that is to be used when displaying the field. Column headings + in the GUI, etc. + + search_terms: the terms that can be used to identify the field when + searching. They can be thought of as aliases for metadata keys, but are only + valid when passed to search(). + + is_custom: the field has been added by the user. + + rec_index: the index of the field in the db metadata record. + + is_csp: field contains colon-separated pairs. Must also be text, is_multiple + + ''' + + VALID_DATA_TYPES = frozenset([None, 'rating', 'text', 'comments', 'datetime', + 'int', 'float', 'bool', 'series', 'composite', 'enumeration']) # search labels that are not db columns search_items = ['all', 'search'] def __init__(self): - self._field_metadata = copy.deepcopy(self._field_metadata_prototype) + self._field_metadata = _builtin_field_metadata() self._tb_cats = OrderedDict() self._tb_custom_fields = {} self._search_term_map = {}