From e7bcf65c34bc10aae181826ca13dc336e97f5040 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 5 Sep 2013 09:43:58 +0530 Subject: [PATCH] Fix changing language in welcome wizard Fix changing the user interface language in the welcome wizard causing some parts of the interface to remain in the old language until calibre is restarted. Fixes #1220767 [Language mismatch on the welcome wizard e-mail page & throughout the rest of the program UI](https://bugs.launchpad.net/calibre/+bug/1220767) --- src/calibre/gui2/main.py | 19 ++-- src/calibre/gui2/main_window.py | 19 ++-- src/calibre/gui2/wizard/__init__.py | 14 ++- src/calibre/library/field_metadata.py | 129 +++++++++++++------------- 4 files changed, 98 insertions(+), 83 deletions(-) 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 = {}