Bulk metadata editing implemented.

This commit is contained in:
Kovid Goyal 2007-08-04 01:02:08 +00:00
parent 7cd1973a85
commit ef0b3c9e0b
8 changed files with 364 additions and 11 deletions

View File

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

View File

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

View File

@ -0,0 +1,248 @@
<ui version="4.0" >
<class>MetadataBulkDialog</class>
<widget class="QDialog" name="MetadataBulkDialog" >
<property name="windowModality" >
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>461</width>
<height>342</height>
</rect>
</property>
<property name="windowTitle" >
<string>Edit Meta information</string>
</property>
<property name="windowIcon" >
<iconset resource="../images.qrc" >:/images/edit_input.svg</iconset>
</property>
<layout class="QGridLayout" >
<item row="0" column="0" >
<widget class="QSplitter" name="splitter" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget" >
<layout class="QVBoxLayout" >
<property name="spacing" >
<number>6</number>
</property>
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox" >
<property name="title" >
<string>Meta information</string>
</property>
<layout class="QGridLayout" >
<property name="leftMargin" >
<number>9</number>
</property>
<property name="topMargin" >
<number>9</number>
</property>
<property name="rightMargin" >
<number>9</number>
</property>
<property name="bottomMargin" >
<number>9</number>
</property>
<property name="horizontalSpacing" >
<number>6</number>
</property>
<property name="verticalSpacing" >
<number>6</number>
</property>
<item row="2" column="1" colspan="2" >
<widget class="QSpinBox" name="rating" >
<property name="toolTip" >
<string>Rating of this book. 0-5 stars</string>
</property>
<property name="whatsThis" >
<string>Rating of this book. 0-5 stars</string>
</property>
<property name="buttonSymbols" >
<enum>QAbstractSpinBox::PlusMinus</enum>
</property>
<property name="suffix" >
<string> stars</string>
</property>
<property name="maximum" >
<number>5</number>
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="label_6" >
<property name="text" >
<string>&amp;Rating:</string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2" >
<widget class="QLineEdit" name="publisher" >
<property name="toolTip" >
<string>Change the publisher of this book</string>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<string>&amp;Publisher: </string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<cstring>publisher</cstring>
</property>
</widget>
</item>
<item row="4" column="0" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<string>Add Ta&amp;gs: </string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<cstring>tags</cstring>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2" >
<widget class="QLineEdit" name="tags" >
<property name="toolTip" >
<string>Tags categorize the book. This is particularly useful while searching. &lt;br>&lt;br>They can be any words or phrases, separated by commas.</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2" >
<widget class="QLineEdit" name="authors" >
<property name="toolTip" >
<string>Change the author(s) of this book. Multiple authors should be separated by a comma</string>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<string>&amp;Author(s): </string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<cstring>authors</cstring>
</property>
</widget>
</item>
<item row="5" column="1" colspan="2" >
<widget class="QComboBox" name="series" >
<property name="toolTip" >
<string>List of known series. You can add new series.</string>
</property>
<property name="whatsThis" >
<string>List of known series. You can add new series.</string>
</property>
<property name="editable" >
<bool>true</bool>
</property>
<property name="insertPolicy" >
<enum>QComboBox::InsertAlphabetically</enum>
</property>
<property name="sizeAdjustPolicy" >
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item row="5" column="0" >
<widget class="QLabel" name="label_7" >
<property name="text" >
<string>&amp;Series:</string>
</property>
<property name="textFormat" >
<enum>Qt::PlainText</enum>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<cstring>series</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0" >
<widget class="QDialogButtonBox" name="button_box" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../images.qrc" />
</resources>
<connections>
<connection>
<sender>button_box</sender>
<signal>accepted()</signal>
<receiver>MetadataBulkDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel" >
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>button_box</sender>
<signal>rejected()</signal>
<receiver>MetadataBulkDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel" >
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

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

View File

@ -1,6 +1,9 @@
<ui version="4.0" >
<class>MetadataSingleDialog</class>
<widget class="QDialog" name="MetadataSingleDialog" >
<property name="windowModality" >
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry" >
<rect>
<x>0</x>
@ -10,7 +13,7 @@
</rect>
</property>
<property name="windowTitle" >
<string>libprs500 - Edit Meta Information</string>
<string>Edit Meta Information</string>
</property>
<property name="windowIcon" >
<iconset resource="../images.qrc" >:/images/edit_input.svg</iconset>

View File

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

View File

@ -319,7 +319,6 @@
</attribute>
<addaction name="action_add" />
<addaction name="action_del" />
<addaction name="action_edit" />
</widget>
<widget class="QStatusBar" name="statusBar" >
<property name="mouseTracking" >

View File

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