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)
This commit is contained in:
Kovid Goyal 2013-09-05 09:43:58 +05:30
parent ffc0b5af73
commit e7bcf65c34
4 changed files with 98 additions and 83 deletions

View File

@ -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)

View File

@ -78,15 +78,16 @@ class MainWindow(QMainWindow):
@classmethod
def create_application_menubar(cls):
if not cls.__actions:
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
return cls.__actions
@classmethod
def get_menubar_actions(cls):

View File

@ -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:

View File

@ -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',
}
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',
@ -390,11 +336,68 @@ class FieldMetadata(dict):
]
# }}}
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 = {}