From ef0b3c9e0b56065251ed98a016545a872d808b3a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 4 Aug 2007 01:02:08 +0000 Subject: [PATCH] Bulk metadata editing implemented. --- src/libprs500/gui2/Makefile | 2 +- src/libprs500/gui2/dialogs/metadata_bulk.py | 73 ++++++ src/libprs500/gui2/dialogs/metadata_bulk.ui | 248 ++++++++++++++++++ src/libprs500/gui2/dialogs/metadata_single.py | 9 +- src/libprs500/gui2/dialogs/metadata_single.ui | 5 +- src/libprs500/gui2/main.py | 27 +- src/libprs500/gui2/main.ui | 1 - src/libprs500/library/database.py | 10 +- 8 files changed, 364 insertions(+), 11 deletions(-) create mode 100644 src/libprs500/gui2/dialogs/metadata_bulk.py create mode 100644 src/libprs500/gui2/dialogs/metadata_bulk.ui diff --git a/src/libprs500/gui2/Makefile b/src/libprs500/gui2/Makefile index 25241d654d..1ef49a19be 100644 --- a/src/libprs500/gui2/Makefile +++ b/src/libprs500/gui2/Makefile @@ -1,4 +1,4 @@ -UI = main_ui.py dialogs/metadata_single_ui.py +UI = main_ui.py dialogs/metadata_single_ui.py dialogs/metadata_bulk_ui.py RC = images_rc.pyc %_ui.py : %.ui diff --git a/src/libprs500/gui2/dialogs/metadata_bulk.py b/src/libprs500/gui2/dialogs/metadata_bulk.py new file mode 100644 index 0000000000..923670cb16 --- /dev/null +++ b/src/libprs500/gui2/dialogs/metadata_bulk.py @@ -0,0 +1,73 @@ +## Copyright (C) 2007 Kovid Goyal kovid@kovidgoyal.net +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License along +## with this program; if not, write to the Free Software Foundation, Inc., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +'''Dialog to edit metadata in bulk''' + +from PyQt4.QtCore import SIGNAL, QObject + +from libprs500.gui2 import qstring_to_unicode +from libprs500.gui2.dialogs import ModalDialog +from libprs500.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog + +class MetadataBulkDialog(Ui_MetadataBulkDialog, ModalDialog): + def __init__(self, window, rows, db): + Ui_MetadataBulkDialog.__init__(self) + ModalDialog.__init__(self, window) + self.setupUi(self.dialog) + self.db = db + self.ids = [ db.id(r) for r in rows] + self.write_series = False + self.write_rating = False + self.changed = False + QObject.connect(self.button_box, SIGNAL("accepted()"), self.sync) + QObject.connect(self.series, SIGNAL('currentIndexChanged(int)'), self.series_changed) + QObject.connect(self.series, SIGNAL('editTextChanged(QString)'), self.series_changed) + QObject.connect(self.rating, SIGNAL('valueChanged(int)'), self.rating_changed) + + all_series = self.db.all_series() + + for i in all_series: + id, name = i + self.series.addItem(name) + + + + self.dialog.exec_() + + + def sync(self): + for id in self.ids: + au = qstring_to_unicode(self.authors.text()) + if au: + au = au.split(',') + self.db.set_authors(id, au) + if self.write_rating: + self.db.set_rating(id, 2*self.rating.value()) + pub = qstring_to_unicode(self.publisher.text()) + if pub: + self.db.set_publisher(id, pub) + tags = qstring_to_unicode(self.tags.text()) + if tags: + tags = tags.split(tags) + self.db.set_tags(id, tags, append=True) + if self.write_series: + self.db.set_series(id, qstring_to_unicode(self.series.currentText())) + self.changed = True + + def series_changed(self): + self.write_series = True + + def rating_changed(self): + self.write_rating = True \ No newline at end of file diff --git a/src/libprs500/gui2/dialogs/metadata_bulk.ui b/src/libprs500/gui2/dialogs/metadata_bulk.ui new file mode 100644 index 0000000000..2fb614c2bf --- /dev/null +++ b/src/libprs500/gui2/dialogs/metadata_bulk.ui @@ -0,0 +1,248 @@ + + MetadataBulkDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 461 + 342 + + + + Edit Meta information + + + :/images/edit_input.svg + + + + + + Qt::Horizontal + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Meta information + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + 6 + + + + + Rating of this book. 0-5 stars + + + Rating of this book. 0-5 stars + + + QAbstractSpinBox::PlusMinus + + + stars + + + 5 + + + + + + + &Rating: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Change the publisher of this book + + + + + + + &Publisher: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + publisher + + + + + + + Add Ta&gs: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + tags + + + + + + + Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas. + + + + + + + Change the author(s) of this book. Multiple authors should be separated by a comma + + + + + + + &Author(s): + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + authors + + + + + + + List of known series. You can add new series. + + + List of known series. You can add new series. + + + true + + + QComboBox::InsertAlphabetically + + + QComboBox::AdjustToContents + + + + + + + &Series: + + + Qt::PlainText + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + series + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + + + button_box + accepted() + MetadataBulkDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + button_box + rejected() + MetadataBulkDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/libprs500/gui2/dialogs/metadata_single.py b/src/libprs500/gui2/dialogs/metadata_single.py index 2516fbb1e3..66ee796176 100644 --- a/src/libprs500/gui2/dialogs/metadata_single.py +++ b/src/libprs500/gui2/dialogs/metadata_single.py @@ -19,8 +19,7 @@ add/remove formats import os from PyQt4.QtCore import SIGNAL -from PyQt4.Qt import QObject, QPixmap, QListWidgetItem, QErrorMessage, \ - QVariant, QSettings, QFileDialog +from PyQt4.Qt import QObject, QPixmap, QListWidgetItem, QErrorMessage from libprs500.gui2 import qstring_to_unicode, error_dialog, file_icon_provider, \ @@ -171,8 +170,6 @@ class MetadataSingleDialog(Ui_MetadataSingleDialog, ModalDialog): if qstring_to_unicode(self.series.currentText()): self.enable_series_index() - QObject.connect(self.series, SIGNAL('currentIndexChanged(int)'), self.enable_series_index) - QObject.connect(self.series, SIGNAL('editTextChanged(QString)'), self.enable_series_index) all_series = self.db.all_series() series_id = self.db.series_id(row) @@ -183,10 +180,14 @@ class MetadataSingleDialog(Ui_MetadataSingleDialog, ModalDialog): idx = c self.series.addItem(name) c += 1 + + self.series.lineEdit().setText('') if idx is not None: self.series.setCurrentIndex(idx) self.series_index.setValue(self.db.series_index(row)) + QObject.connect(self.series, SIGNAL('currentIndexChanged(int)'), self.enable_series_index) + QObject.connect(self.series, SIGNAL('editTextChanged(QString)'), self.enable_series_index) self.dialog.exec_() diff --git a/src/libprs500/gui2/dialogs/metadata_single.ui b/src/libprs500/gui2/dialogs/metadata_single.ui index f3f0cee0c5..13dbda62b8 100644 --- a/src/libprs500/gui2/dialogs/metadata_single.ui +++ b/src/libprs500/gui2/dialogs/metadata_single.ui @@ -1,6 +1,9 @@ MetadataSingleDialog + + Qt::ApplicationModal + 0 @@ -10,7 +13,7 @@ - libprs500 - Edit Meta Information + Edit Meta Information :/images/edit_input.svg diff --git a/src/libprs500/gui2/main.py b/src/libprs500/gui2/main.py index 2efb32ddde..5b81ce3cf3 100644 --- a/src/libprs500/gui2/main.py +++ b/src/libprs500/gui2/main.py @@ -31,6 +31,7 @@ from libprs500.gui2.device import DeviceDetector, DeviceManager from libprs500.gui2.status import StatusBar from libprs500.gui2.jobs import JobManager, JobException from libprs500.gui2.dialogs.metadata_single import MetadataSingleDialog +from libprs500.gui2.dialogs.metadata_bulk import MetadataBulkDialog class Main(QObject, Ui_MainWindow): @@ -73,14 +74,23 @@ class Main(QObject, Ui_MainWindow): sm.addAction(QIcon(':/images/reader.svg'), 'Send to main memory') sm.addAction(QIcon(':/images/sd.svg'), 'Send to storage card') self.sync_menu = sm # Needed + md = QMenu() + md.addAction('Edit metadata individually') + md.addAction('Edit metadata in bulk') + self.metadata_menu = md QObject.connect(self.action_add, SIGNAL("triggered(bool)"), self.add_books) QObject.connect(self.action_del, SIGNAL("triggered(bool)"), self.delete_books) QObject.connect(self.action_edit, SIGNAL("triggered(bool)"), self.edit_metadata) + QObject.connect(md.actions()[0], SIGNAL('triggered(bool)'), self.edit_metadata) + QObject.connect(md.actions()[1], SIGNAL('triggered(bool)'), self.edit_bulk_metadata) QObject.connect(self.action_sync, SIGNAL("triggered(bool)"), self.sync_to_main_memory) QObject.connect(sm.actions()[0], SIGNAL('triggered(bool)'), self.sync_to_main_memory) QObject.connect(sm.actions()[1], SIGNAL('triggered(bool)'), self.sync_to_card) + self.action_sync.setMenu(sm) - self.tool_bar.insertAction(self.action_edit, self.action_sync) + self.action_edit.setMenu(md) + self.tool_bar.addAction(self.action_sync) + self.tool_bar.addAction(self.action_edit) self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu) ####################### Library view ######################## @@ -312,6 +322,8 @@ class Main(QObject, Ui_MainWindow): ''' rows = self.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: + d = error_dialog(self.window, 'Cannot edit metadata', 'No books selected') + d.exec_() return changed = False for row in rows: @@ -323,6 +335,19 @@ class Main(QObject, Ui_MainWindow): self.library_view.model().resort() self.library_view.model().research() + def edit_bulk_metadata(self, checked): + ''' + Edit metadata of selected books in library in bulk. + ''' + rows = [r.row() for r in self.library_view.selectionModel().selectedRows()] + if not rows or len(rows) == 0: + d = error_dialog(self.window, 'Cannot edit metadata', 'No books selected') + d.exec_() + return + if MetadataBulkDialog(self.window, rows, self.library_view.model().db).changed: + self.library_view.model().resort() + self.library_view.model().research() + ############################################################################ ############################# Syncing to device############################# diff --git a/src/libprs500/gui2/main.ui b/src/libprs500/gui2/main.ui index 56b4fc8f4e..b683153466 100644 --- a/src/libprs500/gui2/main.ui +++ b/src/libprs500/gui2/main.ui @@ -319,7 +319,6 @@ - diff --git a/src/libprs500/library/database.py b/src/libprs500/library/database.py index cc12d3788e..ec9de2e581 100644 --- a/src/libprs500/library/database.py +++ b/src/libprs500/library/database.py @@ -785,11 +785,13 @@ class LibraryDatabase(object): self.conn.execute('INSERT INTO comments(book,text) VALUES (?,?)', (id, text)) self.conn.commit() - def set_tags(self, id, tags): + def set_tags(self, id, tags, append=False): ''' @param tags: list of strings + @param append: If True existing tags are not removed ''' - self.conn.execute('DELETE FROM books_tags_link WHERE book=?', (id,)) + if not append: + self.conn.execute('DELETE FROM books_tags_link WHERE book=?', (id,)) tag = set(tags) for tag in tags: t = self.conn.execute('SELECT id from tags WHERE name=?', (tag,)).fetchone() @@ -797,7 +799,9 @@ class LibraryDatabase(object): tid = t[0] else: tid = self.conn.execute('INSERT INTO tags(name) VALUES(?)', (tag,)).lastrowid - self.conn.execute('INSERT INTO books_tags_link(book, tag) VALUES (?,?)', + if (append and not self.conn.execute('SELECT book FROM books_tags_link WHERE book=? AND tag=?', + (id, tid)).fetchone()) or not append: + self.conn.execute('INSERT INTO books_tags_link(book, tag) VALUES (?,?)', (id, tid)) self.conn.commit()