mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Run bulk metadata updates in a separate thread
This commit is contained in:
parent
47fa5382b6
commit
c02df37c78
@ -11,7 +11,7 @@ from functools import partial
|
|||||||
from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateEdit, \
|
from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateEdit, \
|
||||||
QDate, QGroupBox, QVBoxLayout, QPlainTextEdit, QSizePolicy, \
|
QDate, QGroupBox, QVBoxLayout, QPlainTextEdit, QSizePolicy, \
|
||||||
QSpacerItem, QIcon, QCheckBox, QWidget, QHBoxLayout, SIGNAL, \
|
QSpacerItem, QIcon, QCheckBox, QWidget, QHBoxLayout, SIGNAL, \
|
||||||
QPushButton, QCoreApplication
|
QPushButton
|
||||||
|
|
||||||
from calibre.utils.date import qt_to_dt, now
|
from calibre.utils.date import qt_to_dt, now
|
||||||
from calibre.gui2.widgets import TagsLineEdit, EnComboBox
|
from calibre.gui2.widgets import TagsLineEdit, EnComboBox
|
||||||
@ -32,8 +32,13 @@ class Base(object):
|
|||||||
val = self.normalize_db_val(val)
|
val = self.normalize_db_val(val)
|
||||||
self.setter(val)
|
self.setter(val)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def gui_val(self):
|
||||||
|
return self.getter()
|
||||||
|
|
||||||
|
|
||||||
def commit(self, book_id, notify=False):
|
def commit(self, book_id, notify=False):
|
||||||
val = self.getter()
|
val = self.gui_val
|
||||||
val = self.normalize_ui_val(val)
|
val = self.normalize_ui_val(val)
|
||||||
if val != self.initial_val:
|
if val != self.initial_val:
|
||||||
self.db.set_custom(book_id, val, num=self.col_id, notify=notify)
|
self.db.set_custom(book_id, val, num=self.col_id, notify=notify)
|
||||||
@ -284,10 +289,14 @@ class Series(Base):
|
|||||||
if idx is not None:
|
if idx is not None:
|
||||||
self.widgets[1].setCurrentIndex(idx)
|
self.widgets[1].setCurrentIndex(idx)
|
||||||
|
|
||||||
|
def getter(self):
|
||||||
|
n = unicode(self.name_widget.currentText()).strip()
|
||||||
|
i = self.idx_widget.value()
|
||||||
|
return n, i
|
||||||
|
|
||||||
def commit(self, book_id, notify=False):
|
def commit(self, book_id, notify=False):
|
||||||
val = unicode(self.name_widget.currentText()).strip()
|
val, s_index = self.gui_val
|
||||||
val = self.normalize_ui_val(val)
|
val = self.normalize_ui_val(val)
|
||||||
s_index = self.idx_widget.value()
|
|
||||||
if val != self.initial_val or s_index != self.initial_index:
|
if val != self.initial_val or s_index != self.initial_index:
|
||||||
if s_index == 0.0:
|
if s_index == 0.0:
|
||||||
if tweaks['series_index_auto_increment'] == 'next':
|
if tweaks['series_index_auto_increment'] == 'next':
|
||||||
@ -378,6 +387,13 @@ def populate_metadata_page(layout, db, book_id, bulk=False, two_column=False, pa
|
|||||||
|
|
||||||
class BulkBase(Base):
|
class BulkBase(Base):
|
||||||
|
|
||||||
|
@property
|
||||||
|
def gui_val(self):
|
||||||
|
if not hasattr(self, '_cached_gui_val_'):
|
||||||
|
self._cached_gui_val_ = self.getter()
|
||||||
|
return self._cached_gui_val_
|
||||||
|
|
||||||
|
|
||||||
def get_initial_value(self, book_ids):
|
def get_initial_value(self, book_ids):
|
||||||
values = set([])
|
values = set([])
|
||||||
for book_id in book_ids:
|
for book_id in book_ids:
|
||||||
@ -400,11 +416,10 @@ class BulkBase(Base):
|
|||||||
self.setter(val)
|
self.setter(val)
|
||||||
|
|
||||||
def commit(self, book_ids, notify=False):
|
def commit(self, book_ids, notify=False):
|
||||||
val = self.getter()
|
val = self.gui_val
|
||||||
val = self.normalize_ui_val(val)
|
val = self.normalize_ui_val(val)
|
||||||
if val != self.initial_val:
|
if val != self.initial_val:
|
||||||
for book_id in book_ids:
|
for book_id in book_ids:
|
||||||
QCoreApplication.processEvents()
|
|
||||||
self.db.set_custom(book_id, val, num=self.col_id, notify=notify)
|
self.db.set_custom(book_id, val, num=self.col_id, notify=notify)
|
||||||
|
|
||||||
class BulkBool(BulkBase, Bool):
|
class BulkBool(BulkBase, Bool):
|
||||||
@ -443,13 +458,16 @@ class BulkSeries(BulkBase):
|
|||||||
self.name_widget.addItem(c)
|
self.name_widget.addItem(c)
|
||||||
self.name_widget.setEditText('')
|
self.name_widget.setEditText('')
|
||||||
|
|
||||||
|
def getter(self):
|
||||||
|
n = unicode(self.name_widget.currentText()).strip()
|
||||||
|
i = self.idx_widget.checkState()
|
||||||
|
return n, i
|
||||||
|
|
||||||
def commit(self, book_ids, notify=False):
|
def commit(self, book_ids, notify=False):
|
||||||
val = unicode(self.name_widget.currentText()).strip()
|
val, update_indices = self.gui_val
|
||||||
val = self.normalize_ui_val(val)
|
val = self.normalize_ui_val(val)
|
||||||
update_indices = self.idx_widget.checkState()
|
|
||||||
if val != '':
|
if val != '':
|
||||||
for book_id in book_ids:
|
for book_id in book_ids:
|
||||||
QCoreApplication.processEvents()
|
|
||||||
if update_indices:
|
if update_indices:
|
||||||
if tweaks['series_index_auto_increment'] == 'next':
|
if tweaks['series_index_auto_increment'] == 'next':
|
||||||
s_index = self.db.get_next_cc_series_num_for\
|
s_index = self.db.get_next_cc_series_num_for\
|
||||||
@ -526,41 +544,35 @@ class BulkText(BulkBase):
|
|||||||
|
|
||||||
def commit(self, book_ids, notify=False):
|
def commit(self, book_ids, notify=False):
|
||||||
if self.col_metadata['is_multiple']:
|
if self.col_metadata['is_multiple']:
|
||||||
|
remove_all, adding, rtext = self.gui_val
|
||||||
remove = set()
|
remove = set()
|
||||||
if self.removing_widget.checkbox.isChecked():
|
if remove_all:
|
||||||
for book_id in book_ids:
|
for book_id in book_ids:
|
||||||
remove |= set(self.db.get_custom(book_id, num=self.col_id,
|
remove |= set(self.db.get_custom(book_id, num=self.col_id,
|
||||||
index_is_id=True))
|
index_is_id=True))
|
||||||
else:
|
else:
|
||||||
txt = unicode(self.removing_widget.tags_box.text())
|
txt = rtext
|
||||||
if txt:
|
if txt:
|
||||||
remove = set([v.strip() for v in txt.split(',')])
|
remove = set([v.strip() for v in txt.split(',')])
|
||||||
txt = unicode(self.adding_widget.text())
|
txt = adding
|
||||||
if txt:
|
if txt:
|
||||||
add = set([v.strip() for v in txt.split(',')])
|
add = set([v.strip() for v in txt.split(',')])
|
||||||
else:
|
else:
|
||||||
add = set()
|
add = set()
|
||||||
self.db.set_custom_bulk(book_ids, add=add, remove=remove, num=self.col_id)
|
self.db.set_custom_bulk(book_ids, add=add, remove=remove, num=self.col_id)
|
||||||
else:
|
else:
|
||||||
val = self.getter()
|
val = self.gui_val
|
||||||
val = self.normalize_ui_val(val)
|
val = self.normalize_ui_val(val)
|
||||||
if val != self.initial_val:
|
if val != self.initial_val:
|
||||||
for book_id in book_ids:
|
for book_id in book_ids:
|
||||||
QCoreApplication.processEvents()
|
|
||||||
self.db.set_custom(book_id, val, num=self.col_id, notify=notify)
|
self.db.set_custom(book_id, val, num=self.col_id, notify=notify)
|
||||||
|
|
||||||
def getter(self, original_value = None):
|
def getter(self):
|
||||||
if self.col_metadata['is_multiple']:
|
if self.col_metadata['is_multiple']:
|
||||||
if self.removing_widget.checkbox.isChecked():
|
return self.removing_widget.checkbox.isChecked(), \
|
||||||
ans = set()
|
unicode(self.adding_widget.text()), \
|
||||||
else:
|
unicode(self.removing_widget.tags_box.text())
|
||||||
ans = set(original_value)
|
|
||||||
ans -= set([v.strip() for v in
|
|
||||||
unicode(self.removing_widget.tags_box.text()).split(',')])
|
|
||||||
txt = unicode(self.adding_widget.text())
|
|
||||||
if txt:
|
|
||||||
ans |= set([v.strip() for v in txt.split(',')])
|
|
||||||
return ans # returning a set instead of a list works, for now at least.
|
|
||||||
val = unicode(self.widgets[1].currentText()).strip()
|
val = unicode(self.widgets[1].currentText()).strip()
|
||||||
if not val:
|
if not val:
|
||||||
val = None
|
val = None
|
||||||
|
@ -4,14 +4,33 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
'''Dialog to edit metadata in bulk'''
|
'''Dialog to edit metadata in bulk'''
|
||||||
|
|
||||||
from PyQt4.Qt import SIGNAL, QObject, QDialog, QGridLayout, \
|
from PyQt4.Qt import SIGNAL, QObject, QDialog, QGridLayout, \
|
||||||
QCoreApplication
|
QThread, Qt
|
||||||
|
|
||||||
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
|
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
|
||||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||||
from calibre.ebooks.metadata import string_to_authors, \
|
from calibre.ebooks.metadata import string_to_authors, \
|
||||||
authors_to_string
|
authors_to_string
|
||||||
from calibre.gui2.custom_column_widgets import populate_metadata_page
|
from calibre.gui2.custom_column_widgets import populate_metadata_page
|
||||||
from calibre.gui2.dialogs.progress import ProgressDialog
|
from calibre.gui2.dialogs.progress import BlockingBusy
|
||||||
|
from calibre.gui2 import error_dialog
|
||||||
|
|
||||||
|
class Worker(QThread):
|
||||||
|
|
||||||
|
def __init__(self, func, parent=None):
|
||||||
|
QThread.__init__(self, parent)
|
||||||
|
self.func = func
|
||||||
|
self.error = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
self.func()
|
||||||
|
except Exception, err:
|
||||||
|
import traceback
|
||||||
|
try:
|
||||||
|
err = unicode(err)
|
||||||
|
except:
|
||||||
|
err = repr(err)
|
||||||
|
self.error = (err, traceback.format_exc())
|
||||||
|
|
||||||
class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||||
|
|
||||||
@ -107,35 +126,29 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
if len(self.ids) < 1:
|
if len(self.ids) < 1:
|
||||||
return QDialog.accept(self)
|
return QDialog.accept(self)
|
||||||
|
|
||||||
pd = ProgressDialog(_('Working'),
|
remove = unicode(self.remove_tags.text()).strip().split(',')
|
||||||
_('Applying changes to %d books. This may take a while.')%len(self.ids),
|
add = unicode(self.tags.text()).strip().split(',')
|
||||||
0, 0, self, cancelable=False)
|
au = unicode(self.authors.text())
|
||||||
pd.setModal(True)
|
aus = unicode(self.author_sort.text())
|
||||||
pd.show()
|
do_aus = self.author_sort.isEnabled()
|
||||||
def upd():
|
rating = self.rating.value()
|
||||||
QCoreApplication.processEvents()
|
pub = unicode(self.publisher.text())
|
||||||
|
do_series = self.write_series
|
||||||
|
series = unicode(self.series.currentText()).strip()
|
||||||
|
do_autonumber = self.autonumber_series.isChecked()
|
||||||
|
do_remove_format = self.remove_format.currentIndex() > -1
|
||||||
|
remove_format = unicode(self.remove_format.currentText())
|
||||||
|
do_swap_ta = self.swap_title_and_author.isChecked()
|
||||||
|
do_remove_conv = self.remove_conversion_settings.isChecked()
|
||||||
|
do_auto_author = self.auto_author_sort.isChecked()
|
||||||
|
self.changed = bool(self.ids)
|
||||||
|
# Cache values from GUI so that Qt widgets are not used in
|
||||||
|
# non GUI thread
|
||||||
|
for w in getattr(self, 'custom_column_widgets', []):
|
||||||
|
w.gui_val
|
||||||
|
|
||||||
try:
|
def doit():
|
||||||
remove = unicode(self.remove_tags.text()).strip().split(',')
|
|
||||||
add = unicode(self.tags.text()).strip().split(',')
|
|
||||||
au = unicode(self.authors.text())
|
|
||||||
aus = unicode(self.author_sort.text())
|
|
||||||
do_aus = self.author_sort.isEnabled()
|
|
||||||
rating = self.rating.value()
|
|
||||||
pub = unicode(self.publisher.text())
|
|
||||||
do_series = self.write_series
|
|
||||||
series = unicode(self.series.currentText()).strip()
|
|
||||||
do_autonumber = self.autonumber_series.isChecked()
|
|
||||||
do_remove_format = self.remove_format.currentIndex() > -1
|
|
||||||
remove_format = unicode(self.remove_format.currentText())
|
|
||||||
do_swap_ta = self.swap_title_and_author.isChecked()
|
|
||||||
do_remove_conv = self.remove_conversion_settings.isChecked()
|
|
||||||
do_auto_author = self.auto_author_sort.isChecked()
|
|
||||||
|
|
||||||
upd()
|
|
||||||
self.changed = bool(self.ids)
|
|
||||||
for id in self.ids:
|
for id in self.ids:
|
||||||
upd()
|
|
||||||
if do_swap_ta:
|
if do_swap_ta:
|
||||||
title = self.db.title(id, index_is_id=True)
|
title = self.db.title(id, index_is_id=True)
|
||||||
aum = self.db.authors(id, index_is_id=True)
|
aum = self.db.authors(id, index_is_id=True)
|
||||||
@ -146,55 +159,54 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
if title:
|
if title:
|
||||||
new_authors = string_to_authors(title)
|
new_authors = string_to_authors(title)
|
||||||
self.db.set_authors(id, new_authors, notify=False)
|
self.db.set_authors(id, new_authors, notify=False)
|
||||||
upd()
|
|
||||||
|
|
||||||
if au:
|
if au:
|
||||||
self.db.set_authors(id, string_to_authors(au), notify=False)
|
self.db.set_authors(id, string_to_authors(au), notify=False)
|
||||||
upd()
|
|
||||||
|
|
||||||
if do_auto_author:
|
if do_auto_author:
|
||||||
x = self.db.author_sort_from_book(id, index_is_id=True)
|
x = self.db.author_sort_from_book(id, index_is_id=True)
|
||||||
if x:
|
if x:
|
||||||
self.db.set_author_sort(id, x, notify=False)
|
self.db.set_author_sort(id, x, notify=False)
|
||||||
upd()
|
|
||||||
|
|
||||||
if aus and do_aus:
|
if aus and do_aus:
|
||||||
self.db.set_author_sort(id, aus, notify=False)
|
self.db.set_author_sort(id, aus, notify=False)
|
||||||
upd()
|
|
||||||
|
|
||||||
if rating != -1:
|
if rating != -1:
|
||||||
self.db.set_rating(id, 2*rating, notify=False)
|
self.db.set_rating(id, 2*rating, notify=False)
|
||||||
|
|
||||||
if pub:
|
if pub:
|
||||||
self.db.set_publisher(id, pub, notify=False)
|
self.db.set_publisher(id, pub, notify=False)
|
||||||
upd()
|
|
||||||
|
|
||||||
if do_series:
|
if do_series:
|
||||||
next = self.db.get_next_series_num_for(series)
|
next = self.db.get_next_series_num_for(series)
|
||||||
self.db.set_series(id, series, notify=False)
|
self.db.set_series(id, series, notify=False)
|
||||||
num = next if do_autonumber and series else 1.0
|
num = next if do_autonumber and series else 1.0
|
||||||
self.db.set_series_index(id, num, notify=False)
|
self.db.set_series_index(id, num, notify=False)
|
||||||
upd()
|
|
||||||
|
|
||||||
if do_remove_format:
|
if do_remove_format:
|
||||||
self.db.remove_format(id, remove_format, index_is_id=True, notify=False)
|
self.db.remove_format(id, remove_format, index_is_id=True, notify=False)
|
||||||
upd()
|
|
||||||
|
|
||||||
if do_remove_conv:
|
if do_remove_conv:
|
||||||
self.db.delete_conversion_options(id, 'PIPE')
|
self.db.delete_conversion_options(id, 'PIPE')
|
||||||
|
|
||||||
upd()
|
|
||||||
for w in getattr(self, 'custom_column_widgets', []):
|
for w in getattr(self, 'custom_column_widgets', []):
|
||||||
w.commit(self.ids)
|
w.commit(self.ids)
|
||||||
self.db.bulk_modify_tags(self.ids, add=add, remove=remove,
|
self.db.bulk_modify_tags(self.ids, add=add, remove=remove,
|
||||||
notify=False)
|
notify=False)
|
||||||
upd()
|
self.db.clean()
|
||||||
|
|
||||||
|
self.worker = Worker(doit, self)
|
||||||
|
self.worker.start()
|
||||||
|
|
||||||
finally:
|
bb = BlockingBusy(_('Applying changes to %d books. This may take a while.')
|
||||||
pd.hide()
|
%len(self.ids), parent=self)
|
||||||
|
self.worker.finished.connect(bb.accept, type=Qt.QueuedConnection)
|
||||||
|
bb.exec_()
|
||||||
|
|
||||||
self.db.clean()
|
if self.worker.error is not None:
|
||||||
|
return error_dialog(self, _('Failed'),
|
||||||
|
self.worker.error[0], det_msg=self.worker.error[1],
|
||||||
|
show=True)
|
||||||
return QDialog.accept(self)
|
return QDialog.accept(self)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user