%PDF- %PDF-
| Direktori : /usr/lib/calibre/calibre/gui2/actions/ |
| Current File : //usr/lib/calibre/calibre/gui2/actions/choose_library.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os
import posixpath
import sys
import weakref
from contextlib import suppress
from functools import partial, lru_cache
from qt.core import (
QAction, QCoreApplication, QDialog, QDialogButtonBox, QGridLayout, QIcon,
QInputDialog, QLabel, QLineEdit, QMenu, QSize, Qt, QTimer, QToolButton,
QVBoxLayout, pyqtSignal
)
from calibre import isbytestring, sanitize_file_name
from calibre.constants import (
config_dir, filesystem_encoding, get_portable_base, isportable, iswindows
)
from calibre.gui2 import (
Dispatcher, choose_dir, choose_images, error_dialog, gprefs, info_dialog,
open_local_file, pixmap_to_data, question_dialog, warning_dialog
)
from calibre.gui2.actions import InterfaceAction
from calibre.library import current_library_name
from calibre.utils.config import prefs, tweaks
from calibre.utils.icu import sort_key
def db_class():
from calibre.db.legacy import LibraryDatabase
return LibraryDatabase
def library_icon_path(lib_name=''):
return os.path.join(config_dir, 'library_icons', sanitize_file_name(lib_name or current_library_name()) + '.png')
@lru_cache(maxsize=512)
def library_qicon(lib_name=''):
q = library_icon_path(lib_name)
if os.path.exists(q):
return QIcon(q)
return getattr(library_qicon, 'default_icon', None) or QIcon.ic('lt.png')
class LibraryUsageStats: # {{{
def __init__(self):
self.stats = {}
self.read_stats()
base = get_portable_base()
if base is not None:
lp = prefs['library_path']
if lp:
# Rename the current library. Renaming of other libraries is
# handled by the switch function
q = os.path.basename(lp)
for loc in list(self.stats):
bn = posixpath.basename(loc)
if bn.lower() == q.lower():
self.rename(loc, lp)
def read_stats(self):
stats = gprefs.get('library_usage_stats', {})
self.stats = stats
def write_stats(self):
locs = list(self.stats.keys())
locs.sort(key=lambda x: self.stats[x], reverse=True)
for key in locs[500:]:
self.stats.pop(key)
gprefs.set('library_usage_stats', self.stats)
def remove(self, location):
self.stats.pop(location, None)
self.write_stats()
def canonicalize_path(self, lpath):
if isbytestring(lpath):
lpath = lpath.decode(filesystem_encoding)
lpath = lpath.replace(os.sep, '/')
return lpath
def library_used(self, db):
lpath = self.canonicalize_path(db.library_path)
if lpath not in self.stats:
self.stats[lpath] = 0
self.stats[lpath] += 1
self.write_stats()
return self.pretty(lpath)
def locations(self, db, limit=None):
lpath = self.canonicalize_path(db.library_path)
locs = list(self.stats.keys())
if lpath in locs:
locs.remove(lpath)
limit = tweaks['many_libraries'] if limit is None else limit
key = (lambda x:sort_key(os.path.basename(x))) if len(locs) > limit else self.stats.get
locs.sort(key=key, reverse=len(locs)<=limit)
for loc in locs:
yield self.pretty(loc), loc
def pretty(self, loc):
if loc.endswith('/'):
loc = loc[:-1]
return loc.split('/')[-1]
def rename(self, location, newloc):
newloc = self.canonicalize_path(newloc)
stats = self.stats.pop(location, None)
if stats is not None:
self.stats[newloc] = stats
self.write_stats()
# }}}
class MovedDialog(QDialog): # {{{
def __init__(self, stats, location, parent=None):
QDialog.__init__(self, parent)
self.setWindowTitle(_('No library found'))
self._l = l = QGridLayout(self)
self.setLayout(l)
self.stats, self.location = stats, location
loc = self.oldloc = location.replace('/', os.sep)
self.header = QLabel(_('No existing calibre library was found at %s. '
'If the library was moved, select its new location below. '
'Otherwise calibre will forget this library.')%loc)
self.header.setWordWrap(True)
ncols = 2
l.addWidget(self.header, 0, 0, 1, ncols)
self.cl = QLabel('<b>'+_('New location of this library:'))
l.addWidget(self.cl, l.rowCount(), 0, 1, ncols)
self.loc = QLineEdit(loc, self)
l.addWidget(self.loc, l.rowCount(), 0, 1, 1)
self.cd = QToolButton(self)
self.cd.setIcon(QIcon(I('document_open.png')))
self.cd.clicked.connect(self.choose_dir)
l.addWidget(self.cd, l.rowCount() - 1, 1, 1, 1)
self.bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Abort)
b = self.bb.addButton(_('Library moved'), QDialogButtonBox.ButtonRole.AcceptRole)
b.setIcon(QIcon(I('ok.png')))
b = self.bb.addButton(_('Forget library'), QDialogButtonBox.ButtonRole.RejectRole)
b.setIcon(QIcon(I('edit-clear.png')))
b.clicked.connect(self.forget_library)
self.bb.accepted.connect(self.accept)
self.bb.rejected.connect(self.reject)
l.addWidget(self.bb, 3, 0, 1, ncols)
self.resize(self.sizeHint() + QSize(120, 0))
def choose_dir(self):
d = choose_dir(self, 'library moved choose new loc',
_('New library location'), default_dir=self.oldloc)
if d is not None:
self.loc.setText(d)
def forget_library(self):
self.stats.remove(self.location)
def accept(self):
newloc = str(self.loc.text())
if not db_class().exists_at(newloc):
error_dialog(self, _('No library found'),
_('No existing calibre library found at %s')%newloc,
show=True)
return
self.stats.rename(self.location, newloc)
self.newloc = newloc
QDialog.accept(self)
# }}}
class BackupStatus(QDialog): # {{{
def __init__(self, gui):
QDialog.__init__(self, gui)
self.l = l = QVBoxLayout(self)
self.msg = QLabel('')
self.msg.setWordWrap(True)
l.addWidget(self.msg)
self.bb = bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
bb.accepted.connect(self.accept)
bb.rejected.connect(self.reject)
b = bb.addButton(_('Queue &all books for backup'), QDialogButtonBox.ButtonRole.ActionRole)
b.clicked.connect(self.mark_all_dirty)
b.setIcon(QIcon(I('lt.png')))
l.addWidget(bb)
self.db = weakref.ref(gui.current_db)
self.setResult(9)
self.setWindowTitle(_('Backup status'))
self.update()
self.resize(self.sizeHint() + QSize(50, 15))
def update(self):
db = self.db()
if db is None:
return
if self.result() != 9:
return
dirty_text = 'no'
try:
dirty_text = '%s' % db.dirty_queue_length()
except:
dirty_text = _('none')
self.msg.setText('<p>' + _(
'Book metadata files remaining to be written: %s') % dirty_text)
QTimer.singleShot(1000, self.update)
def mark_all_dirty(self):
db = self.db()
if db is None:
return
db.new_api.mark_as_dirty(db.new_api.all_book_ids())
# }}}
current_change_library_action_pi = None
def set_change_library_action_plugin(pi):
global current_change_library_action_pi
current_change_library_action_pi = pi
def get_change_library_action_plugin():
return current_change_library_action_pi
class ChooseLibraryAction(InterfaceAction):
name = 'Choose Library'
action_spec = (_('Choose library'), 'lt.png',
_('Choose calibre library to work with'), None)
dont_add_to = frozenset(('context-menu-device',))
action_add_menu = True
action_menu_clone_qaction = _('Switch/create library')
restore_view_state = pyqtSignal(object)
rebuild_change_library_menus = pyqtSignal()
def genesis(self):
self.prev_lname = self.last_lname = ''
self.count_changed(0)
self.action_choose = self.menuless_qaction
self.action_exim = ac = QAction(_('Export/import all calibre data'), self.gui)
ac.triggered.connect(self.exim_data)
self.stats = LibraryUsageStats()
self.popup_type = (QToolButton.ToolButtonPopupMode.InstantPopup if len(self.stats.stats) > 1 else
QToolButton.ToolButtonPopupMode.MenuButtonPopup)
if len(self.stats.stats) > 1:
self.action_choose.triggered.connect(self.choose_library)
else:
self.qaction.triggered.connect(self.choose_library)
self.choose_menu = self.qaction.menu()
ac = self.create_action(spec=(_('Pick a random book'), 'random.png',
None, None), attr='action_pick_random')
ac.triggered.connect(self.pick_random)
self.choose_library_icon_menu = QMenu(_('Change the icon for this library'))
self.choose_library_icon_menu.setIcon(QIcon(I('icon_choose.png')))
self.choose_library_icon_action = self.create_action(
spec=(_('Choose an icon'), 'icon_choose.png', None, None),
attr='action_choose_library_icon')
self.remove_library_icon_action = self.create_action(
spec=(_('Remove current icon'), 'trash.png', None, None),
attr='action_remove_library_icon')
self.choose_library_icon_action.triggered.connect(self.get_library_icon)
self.remove_library_icon_action.triggered.connect(partial(self.remove_library_icon, ''))
self.choose_library_icon_menu.addAction(self.choose_library_icon_action)
self.choose_library_icon_menu.addAction(self.remove_library_icon_action)
self.original_library_icon = library_qicon.default_icon = self.qaction.icon()
if not os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
self.choose_menu.addAction(self.action_choose)
self.quick_menu = QMenu(_('Quick switch'))
self.quick_menu_action = self.choose_menu.addMenu(self.quick_menu)
self.choose_menu.addMenu(self.choose_library_icon_menu)
self.rename_menu = QMenu(_('Rename library'))
self.rename_menu_action = self.choose_menu.addMenu(self.rename_menu)
self.choose_menu.addAction(ac)
self.delete_menu = QMenu(_('Remove library'))
self.delete_menu_action = self.choose_menu.addMenu(self.delete_menu)
self.vl_to_apply_menu = QMenu('waiting ...')
self.vl_to_apply_action = self.choose_menu.addMenu(self.vl_to_apply_menu)
self.rebuild_change_library_menus.connect(self.build_menus,
type=Qt.ConnectionType.QueuedConnection)
self.choose_menu.addAction(self.action_exim)
else:
self.choose_menu.addMenu(self.choose_library_icon_menu)
self.choose_menu.addAction(ac)
self.rename_separator = self.choose_menu.addSeparator()
self.switch_actions = []
for i in range(5):
ac = self.create_action(spec=('', None, None, None),
attr='switch_action%d'%i)
ac.setObjectName(str(i))
self.switch_actions.append(ac)
ac.setVisible(False)
connect_lambda(ac.triggered, self, lambda self:
self.switch_requested(self.qs_locations[int(self.gui.sender().objectName())]),
type=Qt.ConnectionType.QueuedConnection)
self.choose_menu.addAction(ac)
self.rename_separator = self.choose_menu.addSeparator()
self.maintenance_menu = QMenu(_('Library maintenance'))
ac = self.create_action(spec=(_('Library metadata backup status'),
'lt.png', None, None), attr='action_backup_status')
ac.triggered.connect(self.backup_status, type=Qt.ConnectionType.QueuedConnection)
self.maintenance_menu.addAction(ac)
ac = self.create_action(spec=(_('Check library'), 'lt.png',
None, None), attr='action_check_library')
ac.triggered.connect(self.check_library, type=Qt.ConnectionType.QueuedConnection)
self.maintenance_menu.addAction(ac)
ac = self.create_action(spec=(_('Restore database'), 'lt.png',
None, None),
attr='action_restore_database')
ac.triggered.connect(self.restore_database, type=Qt.ConnectionType.QueuedConnection)
self.maintenance_menu.addAction(ac)
self.choose_menu.addMenu(self.maintenance_menu)
self.view_state_map = {}
self.restore_view_state.connect(self._restore_view_state,
type=Qt.ConnectionType.QueuedConnection)
ac = self.create_action(spec=(_('Switch to previous library'), 'lt.png',
None, None),
attr='action_previous_library')
ac.triggered.connect(self.switch_to_previous_library, type=Qt.ConnectionType.QueuedConnection)
self.gui.keyboard.register_shortcut(
self.unique_name + '-' + 'action_previous_library',
ac.text(), action=ac, group=self.action_spec[0], default_keys=('Ctrl+Alt+p',))
self.gui.addAction(ac)
@property
def preserve_state_on_switch(self):
ans = getattr(self, '_preserve_state_on_switch', None)
if ans is None:
self._preserve_state_on_switch = ans = \
self.gui.library_view.preserve_state(require_selected_ids=False)
return ans
def pick_random(self, *args):
self.gui.iactions['Pick Random Book'].pick_random()
def get_library_icon(self):
try:
paths = choose_images(self.gui, 'choose_library_icon',
_('Select icon for library "%s"') % current_library_name())
if paths:
path = paths[0]
p = QIcon(path).pixmap(QSize(256, 256))
icp = library_icon_path()
os.makedirs(os.path.dirname(icp), exist_ok=True)
with open(icp, 'wb') as f:
f.write(pixmap_to_data(p, format='PNG'))
self.set_library_icon()
library_qicon.cache_clear()
except Exception:
import traceback
traceback.print_exc()
def rename_library_icon(self, old_name, new_name):
old_path = library_icon_path(old_name)
new_path = library_icon_path(new_name)
try:
if os.path.exists(old_path):
os.replace(old_path, new_path)
library_qicon.cache_clear()
except Exception:
import traceback
traceback.print_exc()
def remove_library_icon(self, name=''):
try:
with suppress(FileNotFoundError):
os.remove(library_icon_path(name or current_library_name()))
self.set_library_icon()
library_qicon.cache_clear()
except Exception:
import traceback
traceback.print_exc()
def set_library_icon(self):
icon = QIcon(library_icon_path())
has_icon = not icon.isNull() and len(icon.availableSizes()) > 0
if not has_icon:
icon = self.original_library_icon
self.qaction.setIcon(icon)
self.gui.setWindowIcon(icon)
self.remove_library_icon_action.setEnabled(has_icon)
def exim_data(self):
if isportable:
return error_dialog(self.gui, _('Cannot export/import'), _(
'You are running calibre portable, all calibre data is already in the'
' calibre portable folder. Export/import is unavailable.'), show=True)
if self.gui.job_manager.has_jobs():
return error_dialog(self.gui, _('Cannot export/import'),
_('Cannot export/import data while there are running jobs.'), show=True)
from calibre.gui2.dialogs.exim import EximDialog
d = EximDialog(parent=self.gui)
if d.exec() == QDialog.DialogCode.Accepted:
if d.restart_needed:
self.gui.iactions['Restart'].restart()
def library_name(self):
db = self.gui.library_view.model().db
path = db.library_path
if isbytestring(path):
path = path.decode(filesystem_encoding)
path = path.replace(os.sep, '/')
return self.stats.pretty(path)
def update_tooltip(self, count):
tooltip = self.action_spec[2] + '\n\n' + ngettext('{0} [{1} book]', '{0} [{1} books]', count).format(
getattr(self, 'last_lname', ''), count)
a = self.qaction
a.setToolTip(tooltip)
a.setStatusTip(tooltip)
a.setWhatsThis(tooltip)
def library_changed(self, db):
lname = self.stats.library_used(db)
if lname != self.last_lname:
self.prev_lname = self.last_lname
self.last_lname = lname
if len(lname) > 16:
lname = lname[:16] + '…'
a = self.qaction
a.setText(lname.replace('&', '&&&')) # I have no idea why this requires a triple ampersand
self.update_tooltip(db.count())
self.build_menus()
self.set_library_icon()
state = self.view_state_map.get(self.stats.canonicalize_path(
db.library_path), None)
if state is not None:
self.restore_view_state.emit(state)
def _restore_view_state(self, state):
self.preserve_state_on_switch.state = state
def initialization_complete(self):
self.library_changed(self.gui.library_view.model().db)
set_change_library_action_plugin(self)
def switch_to_previous_library(self):
db = self.gui.library_view.model().db
locations = list(self.stats.locations(db))
for name, loc in locations:
is_prev_lib = name == self.prev_lname
if is_prev_lib:
self.switch_requested(loc)
break
def build_menus(self):
if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
return
db = self.gui.library_view.model().db
lname = self.stats.library_used(db)
self.vl_to_apply_action.setText(_('Apply Virtual library when %s is opened') % lname)
locations = list(self.stats.locations(db))
for ac in self.switch_actions:
ac.setVisible(False)
self.quick_menu.clear()
self.rename_menu.clear()
self.delete_menu.clear()
quick_actions, rename_actions, delete_actions = [], [], []
for name, loc in locations:
is_prev_lib = name == self.prev_lname
ic = library_qicon(name)
name = name.replace('&', '&&')
ac = self.quick_menu.addAction(ic, name, Dispatcher(partial(self.switch_requested,
loc)))
ac.setStatusTip(_('Switch to: %s') % loc)
if is_prev_lib:
f = ac.font()
f.setBold(True)
ac.setFont(f)
quick_actions.append(ac)
ac = self.rename_menu.addAction(name, Dispatcher(partial(self.rename_requested,
name, loc)))
rename_actions.append(ac)
ac.setStatusTip(_('Rename: %s') % loc)
ac = self.delete_menu.addAction(name, Dispatcher(partial(self.delete_requested,
name, loc)))
delete_actions.append(ac)
ac.setStatusTip(_('Remove: %s') % loc)
if is_prev_lib:
ac.setFont(f)
qs_actions = []
locations_by_frequency = locations
if len(locations) >= tweaks['many_libraries']:
locations_by_frequency = list(self.stats.locations(db, limit=sys.maxsize))
for i, x in enumerate(locations_by_frequency[:len(self.switch_actions)]):
name, loc = x
ic = library_qicon(name)
name = name.replace('&', '&&')
ac = self.switch_actions[i]
ac.setText(name)
ac.setIcon(ic)
ac.setStatusTip(_('Switch to: %s') % loc)
ac.setVisible(True)
qs_actions.append(ac)
self.qs_locations = [i[1] for i in locations_by_frequency]
self.quick_menu_action.setVisible(bool(locations))
self.rename_menu_action.setVisible(bool(locations))
self.delete_menu_action.setVisible(bool(locations))
self.gui.location_manager.set_switch_actions(quick_actions,
rename_actions, delete_actions, qs_actions,
self.action_choose)
# VL at startup
self.vl_to_apply_menu.clear()
restrictions = sorted(db.prefs['virtual_libraries'], key=sort_key)
# check that the virtual library choice still exists
vl_at_startup = db.prefs['virtual_lib_on_startup']
if vl_at_startup and vl_at_startup not in restrictions:
vl_at_startup = db.prefs['virtual_lib_on_startup'] = ''
restrictions.insert(0, '')
for vl in restrictions:
if vl == vl_at_startup:
self.vl_to_apply_menu.addAction(QIcon(I('ok.png')), vl if vl else _('No Virtual library'),
Dispatcher(partial(self.change_vl_at_startup_requested, vl)))
else:
self.vl_to_apply_menu.addAction(vl if vl else _('No Virtual library'),
Dispatcher(partial(self.change_vl_at_startup_requested, vl)))
# Allow the cloned actions in the OS X global menubar to update
for a in (self.qaction, self.menuless_qaction):
a.changed.emit()
def change_vl_at_startup_requested(self, vl):
self.gui.library_view.model().db.prefs['virtual_lib_on_startup'] = vl
self.build_menus()
def location_selected(self, loc):
enabled = loc == 'library'
self.qaction.setEnabled(enabled)
self.menuless_qaction.setEnabled(enabled)
def rename_requested(self, name, location):
LibraryDatabase = db_class()
loc = location.replace('/', os.sep)
base = os.path.dirname(loc)
old_name = name.replace('&&', '&')
newname, ok = QInputDialog.getText(self.gui, _('Rename') + ' ' + old_name,
'<p>'+_(
'Choose a new name for the library <b>%s</b>. ')%name + '<p>'+_(
'Note that the actual library folder will be renamed.'),
text=old_name)
newname = sanitize_file_name(str(newname))
if not ok or not newname or newname == old_name:
return
newloc = os.path.join(base, newname)
if os.path.exists(newloc):
return error_dialog(self.gui, _('Already exists'),
_('The folder %s already exists. Delete it first.') %
newloc, show=True)
if (iswindows and len(newloc) > LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT):
return error_dialog(self.gui, _('Too long'),
_('Path to library too long. It must be less than'
' %d characters.')%LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT,
show=True)
if not os.path.exists(loc):
error_dialog(self.gui, _('Not found'),
_('Cannot rename as no library was found at %s. '
'Try switching to this library first, then switch back '
'and retry the renaming.')%loc, show=True)
return
self.gui.library_broker.remove_library(loc)
try:
os.rename(loc, newloc)
except:
import traceback
det_msg = 'Location: %r New Location: %r\n%s'%(loc, newloc,
traceback.format_exc())
error_dialog(self.gui, _('Rename failed'),
_('Failed to rename the library at %s. '
'The most common cause for this is if one of the files'
' in the library is open in another program.') % loc,
det_msg=det_msg, show=True)
return
self.stats.rename(location, newloc)
self.rename_library_icon(old_name, newname)
self.build_menus()
self.gui.iactions['Copy To Library'].build_menus()
def delete_requested(self, name, location):
loc = location.replace('/', os.sep)
if not question_dialog(
self.gui, _('Library removed'), _(
'The library %s has been removed from calibre. '
'The files remain on your computer, if you want '
'to delete them, you will have to do so manually.') % ('<code>%s</code>' % loc),
override_icon='dialog_information.png',
yes_text=_('&OK'), no_text=_('&Undo'), yes_icon='ok.png', no_icon='edit-undo.png'):
return
self.remove_library_icon(name)
self.stats.remove(location)
self.gui.library_broker.remove_library(location)
self.build_menus()
self.gui.iactions['Copy To Library'].build_menus()
if os.path.exists(loc):
open_local_file(loc)
def backup_status(self, location):
self.__backup_status_dialog = d = BackupStatus(self.gui)
d.show()
def mark_dirty(self):
db = self.gui.library_view.model().db
db.dirtied(list(db.data.iterallids()))
info_dialog(self.gui, _('Backup metadata'),
_('Metadata will be backed up while calibre is running, at the '
'rate of approximately 1 book every three seconds.'), show=True)
def restore_database(self):
LibraryDatabase = db_class()
m = self.gui.library_view.model()
db = m.db
if (iswindows and len(db.library_path) > LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT):
return error_dialog(self.gui, _('Too long'),
_('Path to library too long. It must be less than'
' %d characters. Move your library to a location with'
' a shorter path using Windows Explorer, then point'
' calibre to the new location and try again.')%
LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT,
show=True)
from calibre.gui2.dialogs.restore_library import restore_database
m = self.gui.library_view.model()
m.stop_metadata_backup()
db = m.db
db.prefs.disable_setting = True
if restore_database(db, self.gui):
self.gui.library_moved(db.library_path)
def check_library(self):
from calibre.gui2.dialogs.check_library import CheckLibraryDialog, DBCheck
self.gui.library_view.save_state()
m = self.gui.library_view.model()
m.stop_metadata_backup()
db = m.db
db.prefs.disable_setting = True
library_path = db.library_path
d = DBCheck(self.gui, db)
d.start()
try:
m.close()
except:
pass
d.break_cycles()
self.gui.library_moved(library_path)
if d.rejected:
return
if d.error is None:
if not question_dialog(self.gui, _('Success'),
_('Found no errors in your calibre library database.'
' Do you want calibre to check if the files in your'
' library match the information in the database?')):
return
else:
return error_dialog(self.gui, _('Failed'),
_('Database integrity check failed, click "Show details"'
' for details.'), show=True, det_msg=d.error[1])
self.gui.status_bar.show_message(
_('Starting library scan, this may take a while'))
try:
QCoreApplication.processEvents()
d = CheckLibraryDialog(self.gui, m.db)
if not d.do_exec():
info_dialog(self.gui, _('No problems found'),
_('The files in your library match the information '
'in the database.'), show=True)
finally:
self.gui.status_bar.clear_message()
def look_for_portable_lib(self, db, location):
base = get_portable_base()
if base is None:
return False, None
loc = location.replace('/', os.sep)
candidate = os.path.join(base, os.path.basename(loc))
if db.exists_at(candidate):
newloc = candidate.replace(os.sep, '/')
self.stats.rename(location, newloc)
return True, newloc
return False, None
def switch_requested(self, location):
if not self.change_library_allowed():
return
db = self.gui.library_view.model().db
current_lib = self.stats.canonicalize_path(db.library_path)
self.view_state_map[current_lib] = self.preserve_state_on_switch.state
loc = location.replace('/', os.sep)
exists = db.exists_at(loc)
if not exists:
exists, new_location = self.look_for_portable_lib(db, location)
if exists:
location = new_location
loc = location.replace('/', os.sep)
if not exists:
d = MovedDialog(self.stats, location, self.gui)
ret = d.exec()
self.build_menus()
self.gui.iactions['Copy To Library'].build_menus()
if ret == QDialog.DialogCode.Accepted:
loc = d.newloc.replace('/', os.sep)
else:
return
# from calibre.utils.mem import memory
# import weakref
# from qt.core import QTimer
# self.dbref = weakref.ref(self.gui.library_view.model().db)
# self.before_mem = memory()
self.gui.library_moved(loc, allow_rebuild=True)
# QTimer.singleShot(5000, self.debug_leak)
def debug_leak(self):
import gc
from calibre.utils.mem import memory
ref = self.dbref
for i in range(3):
gc.collect()
if ref() is not None:
print('DB object alive:', ref())
for r in gc.get_referrers(ref())[:10]:
print(r)
print()
print('before:', self.before_mem)
print('after:', memory())
print()
self.dbref = self.before_mem = None
def count_changed(self, new_count):
self.update_tooltip(new_count)
def choose_library(self, *args):
if not self.change_library_allowed():
return
from calibre.gui2.dialogs.choose_library import ChooseLibrary
self.gui.library_view.save_state()
db = self.gui.library_view.model().db
location = self.stats.canonicalize_path(db.library_path)
self.pre_choose_dialog_location = location
c = ChooseLibrary(db, self.choose_library_callback, self.gui)
c.exec()
def choose_library_callback(self, newloc, copy_structure=False, library_renamed=False):
self.gui.library_moved(newloc, copy_structure=copy_structure,
allow_rebuild=True)
if library_renamed:
self.stats.rename(self.pre_choose_dialog_location, prefs['library_path'])
self.build_menus()
self.gui.iactions['Copy To Library'].build_menus()
def change_library_allowed(self):
if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
warning_dialog(self.gui, _('Not allowed'),
_('You cannot change libraries while using the environment'
' variable CALIBRE_OVERRIDE_DATABASE_PATH.'), show=True)
return False
if self.gui.job_manager.has_jobs():
warning_dialog(self.gui, _('Not allowed'),
_('You cannot change libraries while jobs'
' are running.'), show=True)
return False
if self.gui.proceed_question.questions:
warning_dialog(self.gui, _('Not allowed'),
_('You cannot change libraries until all'
' updates are accepted or rejected.'), show=True)
return False
return True