From 99b0d8f2b9ec980e650aacd0aa31d0a95017334a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 18 Aug 2010 12:24:58 -0600 Subject: [PATCH 1/2] Fix bug in new bulk tag edit function when adding already existing tags --- src/calibre/library/custom_columns.py | 2 +- src/calibre/library/database2.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/library/custom_columns.py b/src/calibre/library/custom_columns.py index f02294a102..60be09a193 100644 --- a/src/calibre/library/custom_columns.py +++ b/src/calibre/library/custom_columns.py @@ -377,7 +377,7 @@ class CustomColumns(object): if add: self.conn.execute( ''' - INSERT INTO {0}(book, value) SELECT {1}.id, {2}.id FROM {1}, {2} + INSERT OR REPLACE INTO {0}(book, value) SELECT {1}.id, {2}.id FROM {1}, {2} '''.format(link_table, temp_tables[0], temp_tables[1]) ) # get rid of the temp tables diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 6728735eb3..4b7a71aa64 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1434,7 +1434,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if add: self.conn.execute( ''' - INSERT INTO books_tags_link(book, tag) SELECT {0}.id, {1}.id FROM + INSERT OR REPLACE INTO books_tags_link(book, tag) SELECT {0}.id, {1}.id FROM {0}, {1} '''.format(tables[0], tables[1]) ) From 5d846dae6f85e2447600adb72f3a0dbff2b867f1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 18 Aug 2010 13:02:14 -0600 Subject: [PATCH 2/2] Fix errors raised during bulk metadata edit causing hang --- src/calibre/gui2/dialogs/metadata_bulk.py | 151 ++++++++++++---------- 1 file changed, 83 insertions(+), 68 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index c216463941..9b6f8e53ab 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -3,8 +3,9 @@ __copyright__ = '2008, Kovid Goyal ' '''Dialog to edit metadata in bulk''' -from PyQt4.Qt import SIGNAL, QObject, QDialog, QGridLayout, \ - QThread, Qt +from threading import Thread + +from PyQt4.Qt import QDialog, QGridLayout from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog from calibre.gui2.dialogs.tag_editor import TagEditor @@ -12,18 +13,73 @@ from calibre.ebooks.metadata import string_to_authors, \ authors_to_string from calibre.gui2.custom_column_widgets import populate_metadata_page from calibre.gui2.dialogs.progress import BlockingBusy -from calibre.gui2 import error_dialog +from calibre.gui2 import error_dialog, Dispatcher -class Worker(QThread): +class Worker(Thread): - def __init__(self, func, parent=None): - QThread.__init__(self, parent) - self.func = func + def __init__(self, args, db, ids, callback): + Thread.__init__(self) + self.args = args + self.db = db + self.ids = ids self.error = None + self.callback = callback + + def doit(self): + remove, add, au, aus, do_aus, rating, pub, do_series, \ + do_autonumber, do_remove_format, remove_format, do_swap_ta, \ + do_remove_conv, do_auto_author, series = self.args + + for id in self.ids: + if do_swap_ta: + title = self.db.title(id, index_is_id=True) + aum = self.db.authors(id, index_is_id=True) + if aum: + aum = [a.strip().replace('|', ',') for a in aum.split(',')] + new_title = authors_to_string(aum) + self.db.set_title(id, new_title, notify=False) + if title: + new_authors = string_to_authors(title) + self.db.set_authors(id, new_authors, notify=False) + + if au: + self.db.set_authors(id, string_to_authors(au), notify=False) + + if do_auto_author: + x = self.db.author_sort_from_book(id, index_is_id=True) + if x: + self.db.set_author_sort(id, x, notify=False) + + if aus and do_aus: + self.db.set_author_sort(id, aus, notify=False) + + if rating != -1: + self.db.set_rating(id, 2*rating, notify=False) + + if pub: + self.db.set_publisher(id, pub, notify=False) + + if do_series: + next = self.db.get_next_series_num_for(series) + self.db.set_series(id, series, notify=False) + num = next if do_autonumber and series else 1.0 + self.db.set_series_index(id, num, notify=False) + + if do_remove_format: + self.db.remove_format(id, remove_format, index_is_id=True, notify=False) + + if do_remove_conv: + self.db.delete_conversion_options(id, 'PIPE') + + for w in getattr(self, 'custom_column_widgets', []): + w.commit(self.ids) + self.db.bulk_modify_tags(self.ids, add=add, remove=remove, + notify=False) + self.db.clean() def run(self): try: - self.func() + self.doit() except Exception, err: import traceback try: @@ -32,6 +88,9 @@ class Worker(QThread): err = repr(err) self.error = (err, traceback.format_exc()) + self.callback() + + class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): def __init__(self, window, rows, db): @@ -57,9 +116,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): self.remove_format.setCurrentIndex(-1) - QObject.connect(self.series, SIGNAL('currentIndexChanged(int)'), self.series_changed) - QObject.connect(self.series, SIGNAL('editTextChanged(QString)'), self.series_changed) - QObject.connect(self.tag_editor_button, SIGNAL('clicked()'), self.tag_editor) + self.series.currentIndexChanged[int].connect(self.series_changed) + self.series.editTextChanged.connect(self.series_changed) + self.tag_editor_button.clicked.connect(self.tag_editor) if len(db.custom_column_label_map) == 0: self.central_widget.tabBar().setVisible(False) else: @@ -113,7 +172,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): self.publisher.addItem(name) self.publisher.setEditText('') - def tag_editor(self): + def tag_editor(self, *args): d = TagEditor(self, self.db, None) d.exec_() if d.result() == QDialog.Accepted: @@ -126,6 +185,12 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): if len(self.ids) < 1: return QDialog.accept(self) + 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 + remove = unicode(self.remove_tags.text()).strip().split(',') add = unicode(self.tags.text()).strip().split(',') au = unicode(self.authors.text()) @@ -141,66 +206,16 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): 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 - def doit(): - for id in self.ids: - if do_swap_ta: - title = self.db.title(id, index_is_id=True) - aum = self.db.authors(id, index_is_id=True) - if aum: - aum = [a.strip().replace('|', ',') for a in aum.split(',')] - new_title = authors_to_string(aum) - self.db.set_title(id, new_title, notify=False) - if title: - new_authors = string_to_authors(title) - self.db.set_authors(id, new_authors, notify=False) - - if au: - self.db.set_authors(id, string_to_authors(au), notify=False) - - if do_auto_author: - x = self.db.author_sort_from_book(id, index_is_id=True) - if x: - self.db.set_author_sort(id, x, notify=False) - - if aus and do_aus: - self.db.set_author_sort(id, aus, notify=False) - - if rating != -1: - self.db.set_rating(id, 2*rating, notify=False) - - if pub: - self.db.set_publisher(id, pub, notify=False) - - if do_series: - next = self.db.get_next_series_num_for(series) - self.db.set_series(id, series, notify=False) - num = next if do_autonumber and series else 1.0 - self.db.set_series_index(id, num, notify=False) - - if do_remove_format: - self.db.remove_format(id, remove_format, index_is_id=True, notify=False) - - if do_remove_conv: - self.db.delete_conversion_options(id, 'PIPE') - - for w in getattr(self, 'custom_column_widgets', []): - w.commit(self.ids) - self.db.bulk_modify_tags(self.ids, add=add, remove=remove, - notify=False) - self.db.clean() - - self.worker = Worker(doit, self) - self.worker.start() + args = (remove, add, au, aus, do_aus, rating, pub, do_series, + do_autonumber, do_remove_format, remove_format, do_swap_ta, + do_remove_conv, do_auto_author, series) bb = BlockingBusy(_('Applying changes to %d books. This may take a while.') %len(self.ids), parent=self) - self.worker.finished.connect(bb.accept, type=Qt.QueuedConnection) + self.worker = Worker(args, self.db, self.ids, Dispatcher(bb.accept, + parent=bb)) + self.worker.start() bb.exec_() if self.worker.error is not None: