mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Made the library view editable
Added a rating column
This commit is contained in:
parent
95ab3d492d
commit
df9c8be1ef
@ -17,12 +17,11 @@ import os, os.path, zlib
|
|||||||
from stat import ST_SIZE
|
from stat import ST_SIZE
|
||||||
from libprs500.lrf.meta import LRFMetaFile
|
from libprs500.lrf.meta import LRFMetaFile
|
||||||
|
|
||||||
|
|
||||||
class LibraryDatabase(object):
|
class LibraryDatabase(object):
|
||||||
|
|
||||||
BOOKS_SQL = """
|
BOOKS_SQL = """
|
||||||
create table if not exists books_meta(id INTEGER PRIMARY KEY, title TEXT, authors TEXT, publisher TEXT, size INTEGER, tags TEXT,
|
create table if not exists books_meta(id INTEGER PRIMARY KEY, title TEXT, authors TEXT, publisher TEXT, size INTEGER, tags TEXT,
|
||||||
cover BLOB, date DATE DEFAULT CURRENT_TIMESTAMP, comments TEXT );
|
cover BLOB, date DATE DEFAULT CURRENT_TIMESTAMP, comments TEXT, rating INTEGER);
|
||||||
create table if not exists books_data(id INTEGER, extension TEXT, data BLOB);
|
create table if not exists books_data(id INTEGER, extension TEXT, data BLOB);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -53,7 +52,7 @@ class LibraryDatabase(object):
|
|||||||
if "unknown" in author.lower(): author = None
|
if "unknown" in author.lower(): author = None
|
||||||
file = zlib.compress(open(file).read())
|
file = zlib.compress(open(file).read())
|
||||||
if cover: cover = sqlite.Binary(zlib.compress(cover))
|
if cover: cover = sqlite.Binary(zlib.compress(cover))
|
||||||
self.con.execute("insert into books_meta (title, authors, publisher, size, tags, cover, comments) values (?,?,?,?,?,?)", (title, author, publisher, size, None, cover, None))
|
self.con.execute("insert into books_meta (title, authors, publisher, size, tags, cover, comments, rating) values (?,?,?,?,?,?,?,?)", (title, author, publisher, size, None, cover, None, None))
|
||||||
id = self.con.execute("select max(id) from books_meta").next()[0]
|
id = self.con.execute("select max(id) from books_meta").next()[0]
|
||||||
self.con.execute("insert into books_data values (?,?,?)", (id, ext, sqlite.Binary(file)))
|
self.con.execute("insert into books_data values (?,?,?)", (id, ext, sqlite.Binary(file)))
|
||||||
self.con.commit()
|
self.con.commit()
|
||||||
@ -66,6 +65,12 @@ class LibraryDatabase(object):
|
|||||||
for c in columns: r[c] = row[c]
|
for c in columns: r[c] = row[c]
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
def commit(self): self.con.commit()
|
||||||
|
|
||||||
|
def delete_by_id(self, id):
|
||||||
|
self.con.execute("delete from books_meta where id=?", (id,))
|
||||||
|
self.con.execute("delete from books_data where id=?", (id,))
|
||||||
|
|
||||||
def get_table(self, columns):
|
def get_table(self, columns):
|
||||||
cols = ",".join([ c for c in columns])
|
cols = ",".join([ c for c in columns])
|
||||||
cur = self.con.execute("select " + cols + " from books_meta")
|
cur = self.con.execute("select " + cols + " from books_meta")
|
||||||
@ -102,13 +107,17 @@ class LibraryDatabase(object):
|
|||||||
data[field] = row[field]
|
data[field] = row[field]
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def set_metadata(self, id, title=None, authors=None, publisher=None, tags=None, cover=None, comments=None):
|
def set_metadata(self, id, title=None, authors=None, rating=None, publisher=None, tags=None, cover=None, comments=None):
|
||||||
if authors and not len(authors): authors = None
|
if authors and not len(authors): authors = None
|
||||||
if publisher and not len(publisher): publisher = None
|
if publisher and not len(publisher): publisher = None
|
||||||
if tags and not len(tags): tags = None
|
if tags and not len(tags): tags = None
|
||||||
if comments and not len(comments): comments = None
|
if comments and not len(comments): comments = None
|
||||||
if cover: cover = sqlite.Binary(zlib.compress(cover))
|
if cover: cover = sqlite.Binary(zlib.compress(cover))
|
||||||
self.con.execute('update books_meta set title=?, authors=?, publisher=?, tags=?, cover=?, comments=? where id=?', (title, authors, publisher, tags, cover, comments, id))
|
self.con.execute('update books_meta set title=?, authors=?, publisher=?, tags=?, cover=?, comments=?, rating=? where id=?', (title, authors, publisher, tags, cover, comments, rating, id))
|
||||||
|
self.con.commit()
|
||||||
|
|
||||||
|
def set_metadata_item(self, id, col, val):
|
||||||
|
self.con.execute('update books_meta set '+col+'=? where id=?',(val, id))
|
||||||
self.con.commit()
|
self.con.commit()
|
||||||
|
|
||||||
def search(self, query): pass
|
def search(self, query): pass
|
||||||
|
@ -56,10 +56,11 @@ class EditBookDialog(Ui_BookEditDialog):
|
|||||||
def write_data(self):
|
def write_data(self):
|
||||||
title = str(self.title.text()).strip()
|
title = str(self.title.text()).strip()
|
||||||
authors = str(self.authors.text()).strip()
|
authors = str(self.authors.text()).strip()
|
||||||
|
rating = self.rating.value()
|
||||||
tags = str(self.tags.text()).strip()
|
tags = str(self.tags.text()).strip()
|
||||||
publisher = str(self.publisher.text()).strip()
|
publisher = str(self.publisher.text()).strip()
|
||||||
comments = str(self.comments.toPlainText()).strip()
|
comments = str(self.comments.toPlainText()).strip()
|
||||||
self.db.set_metadata(self.id, title=title, authors=authors, tags=tags, publisher=publisher, comments=comments, cover=self.cover_data)
|
self.db.set_metadata(self.id, title=title, authors=authors, rating=rating, tags=tags, publisher=publisher, comments=comments, cover=self.cover_data)
|
||||||
if self.formats_changed:
|
if self.formats_changed:
|
||||||
for r in range(self.formats.count()):
|
for r in range(self.formats.count()):
|
||||||
format = self.formats.item(r)
|
format = self.formats.item(r)
|
||||||
@ -116,11 +117,12 @@ class EditBookDialog(Ui_BookEditDialog):
|
|||||||
QObject.connect(self.button_box, SIGNAL("accepted()"), self.write_data)
|
QObject.connect(self.button_box, SIGNAL("accepted()"), self.write_data)
|
||||||
QObject.connect(self.add_format_button, SIGNAL("clicked(bool)"), self.add_format)
|
QObject.connect(self.add_format_button, SIGNAL("clicked(bool)"), self.add_format)
|
||||||
QObject.connect(self.remove_format_button, SIGNAL("clicked(bool)"), self.remove_format)
|
QObject.connect(self.remove_format_button, SIGNAL("clicked(bool)"), self.remove_format)
|
||||||
data = self.db.get_row_by_id(self.id, ["title","authors","publisher","tags","comments"])
|
data = self.db.get_row_by_id(self.id, ["title","authors","rating","publisher","tags","comments"])
|
||||||
self.title.setText(data["title"])
|
self.title.setText(data["title"])
|
||||||
self.authors.setText(data["authors"] if data["authors"] else "")
|
self.authors.setText(data["authors"] if data["authors"] else "")
|
||||||
self.publisher.setText(data["publisher"] if data["publisher"] else "")
|
self.publisher.setText(data["publisher"] if data["publisher"] else "")
|
||||||
self.tags.setText(data["tags"] if data["tags"] else "")
|
self.tags.setText(data["tags"] if data["tags"] else "")
|
||||||
|
if data["rating"] > 0: self.rating.setValue(data["rating"])
|
||||||
self.comments.setPlainText(data["comments"] if data["comments"] else "")
|
self.comments.setPlainText(data["comments"] if data["comments"] else "")
|
||||||
cover = self.db.get_cover(self.id)
|
cover = self.db.get_cover(self.id)
|
||||||
if cover:
|
if cover:
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>865</width>
|
<width>865</width>
|
||||||
<height>642</height>
|
<height>776</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle" >
|
<property name="windowTitle" >
|
||||||
<string>Edit Meta Information</string>
|
<string>SONY Reader - Edit Meta Information</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" >
|
<layout class="QGridLayout" >
|
||||||
<property name="margin" >
|
<property name="margin" >
|
||||||
@ -44,20 +44,75 @@
|
|||||||
<property name="spacing" >
|
<property name="spacing" >
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="3" column="1" colspan="2" >
|
<item row="2" column="1" colspan="2" >
|
||||||
<widget class="QLineEdit" name="tags" >
|
<widget class="QSpinBox" name="rating" >
|
||||||
<property name="toolTip" >
|
<property name="toolTip" >
|
||||||
<string>Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.</string>
|
<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>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1" colspan="2" >
|
<item row="2" column="0" >
|
||||||
|
<widget class="QLabel" name="label_6" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>&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" >
|
<widget class="QLineEdit" name="publisher" >
|
||||||
<property name="toolTip" >
|
<property name="toolTip" >
|
||||||
<string>Change the publisher of this book</string>
|
<string>Change the publisher of this book</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0" >
|
||||||
|
<widget class="QLabel" name="label_3" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>&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>Ta&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. <br><br>They can be any words or phrases, separated by commas.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="1" column="1" colspan="2" >
|
<item row="1" column="1" colspan="2" >
|
||||||
<widget class="QLineEdit" name="authors" >
|
<widget class="QLineEdit" name="authors" >
|
||||||
<property name="toolTip" >
|
<property name="toolTip" >
|
||||||
@ -72,32 +127,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" >
|
|
||||||
<widget class="QLabel" name="label_4" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>Ta&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="2" column="0" >
|
|
||||||
<widget class="QLabel" name="label_3" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>&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="1" column="0" >
|
<item row="1" column="0" >
|
||||||
<widget class="QLabel" name="label_2" >
|
<widget class="QLabel" name="label_2" >
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
@ -124,7 +153,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="2" >
|
<item row="6" column="2" >
|
||||||
<layout class="QVBoxLayout" >
|
<layout class="QVBoxLayout" >
|
||||||
<property name="margin" >
|
<property name="margin" >
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
@ -174,33 +203,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="2" >
|
<item rowspan="3" row="5" column="0" colspan="2" >
|
||||||
<spacer>
|
|
||||||
<property name="orientation" >
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" >
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="2" >
|
|
||||||
<spacer>
|
|
||||||
<property name="orientation" >
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" >
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>21</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item rowspan="3" row="4" column="0" colspan="2" >
|
|
||||||
<widget class="QLabel" name="cover" >
|
<widget class="QLabel" name="cover" >
|
||||||
<property name="sizePolicy" >
|
<property name="sizePolicy" >
|
||||||
<sizepolicy>
|
<sizepolicy>
|
||||||
@ -227,6 +230,32 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="7" column="2" >
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="2" >
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>21</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -21,7 +21,8 @@ from editbook import EditBookDialog
|
|||||||
|
|
||||||
from PyQt4.QtCore import Qt, SIGNAL
|
from PyQt4.QtCore import Qt, SIGNAL
|
||||||
from PyQt4.Qt import QObject, QThread, QCoreApplication, QEventLoop, QString, QStandardItem, QStandardItemModel, QStatusBar, QVariant, QAbstractTableModel, \
|
from PyQt4.Qt import QObject, QThread, QCoreApplication, QEventLoop, QString, QStandardItem, QStandardItemModel, QStatusBar, QVariant, QAbstractTableModel, \
|
||||||
QAbstractItemView, QImage, QPixmap, QIcon, QSize, QMessageBox, QSettings, QFileDialog, QErrorMessage, QDialog
|
QAbstractItemView, QImage, QPixmap, QIcon, QSize, QMessageBox, QSettings, QFileDialog, QErrorMessage, QDialog, QSpinBox,\
|
||||||
|
QPainterPath, QItemDelegate, QPainter, QPen, QColor, QLinearGradient, QBrush, QStyle
|
||||||
from PyQt4 import uic
|
from PyQt4 import uic
|
||||||
import sys, re, string, time, os, os.path, traceback, textwrap, zlib
|
import sys, re, string, time, os, os.path, traceback, textwrap, zlib
|
||||||
from stat import ST_SIZE
|
from stat import ST_SIZE
|
||||||
@ -30,6 +31,7 @@ from exceptions import Exception as Exception
|
|||||||
import xml.dom.minidom as dom
|
import xml.dom.minidom as dom
|
||||||
from xml.dom.ext import PrettyPrint as PrettyPrint
|
from xml.dom.ext import PrettyPrint as PrettyPrint
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
from math import sin, cos, pi
|
||||||
|
|
||||||
DEFAULT_BOOK_COVER = None
|
DEFAULT_BOOK_COVER = None
|
||||||
NONE = QVariant()
|
NONE = QVariant()
|
||||||
@ -50,8 +52,95 @@ def human_readable(size):
|
|||||||
def wrap(s, width=20):
|
def wrap(s, width=20):
|
||||||
return textwrap.fill(str(s), width)
|
return textwrap.fill(str(s), width)
|
||||||
|
|
||||||
|
class LibraryDelegate(QItemDelegate):
|
||||||
|
COLOR = QColor("blue")
|
||||||
|
SIZE = 16
|
||||||
|
PEN = QPen(COLOR, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QItemDelegate.__init__(self, parent)
|
||||||
|
self.star_path = QPainterPath()
|
||||||
|
self.star_path.moveTo(90, 50)
|
||||||
|
for i in range(1, 5):
|
||||||
|
self.star_path.lineTo(50 + 40 * cos(0.8 * i * pi), 50 + 40 * sin(0.8 * i * pi))
|
||||||
|
self.star_path.closeSubpath()
|
||||||
|
self.star_path.setFillRule(Qt.WindingFill)
|
||||||
|
gradient = QLinearGradient(0, 0, 0, 100)
|
||||||
|
gradient.setColorAt(0.0, self.COLOR)
|
||||||
|
gradient.setColorAt(1.0, self.COLOR)
|
||||||
|
self. brush = QBrush(gradient)
|
||||||
|
self.factor = self.SIZE/100.
|
||||||
|
|
||||||
|
|
||||||
|
def sizeHint(self, option, index):
|
||||||
|
if index.column() != 4:
|
||||||
|
return QItemDelegate.sizeHint(self, option, index)
|
||||||
|
num = index.model().data(index, Qt.DisplayRole).toInt()[0]
|
||||||
|
return QSize(num*(self.SIZE), self.SIZE+4)
|
||||||
|
|
||||||
|
def paint(self, painter, option, index):
|
||||||
|
if index.column() != 4:
|
||||||
|
return QItemDelegate.paint(self, painter, option, index)
|
||||||
|
num = index.model().data(index, Qt.DisplayRole).toInt()[0]
|
||||||
|
def draw_star():
|
||||||
|
painter.save()
|
||||||
|
painter.scale(self.factor, self.factor)
|
||||||
|
painter.translate(50.0, 50.0)
|
||||||
|
painter.rotate(-20)
|
||||||
|
painter.translate(-50.0, -50.0)
|
||||||
|
painter.drawPath(self.star_path)
|
||||||
|
painter.restore()
|
||||||
|
|
||||||
|
painter.save()
|
||||||
|
try:
|
||||||
|
if option.state & QStyle.State_Selected:
|
||||||
|
painter.fillRect(option.rect, option.palette.highlight())
|
||||||
|
painter.setRenderHint(QPainter.Antialiasing)
|
||||||
|
y = option.rect.center().y()-self.SIZE/2.
|
||||||
|
x = option.rect.right() - self.SIZE
|
||||||
|
painter.setPen(self.PEN)
|
||||||
|
painter.setBrush(self.brush)
|
||||||
|
painter.translate(x, y)
|
||||||
|
for i in range(num):
|
||||||
|
draw_star()
|
||||||
|
painter.translate(-self.SIZE, 0)
|
||||||
|
except Exception, e:
|
||||||
|
traceback.print_exc(e)
|
||||||
|
painter.restore()
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
if index.column() != 4:
|
||||||
|
return QItemDelegate.createEditor(self, parent, option, index)
|
||||||
|
print "hello"
|
||||||
|
editor = QSpinBox(parent)
|
||||||
|
editor.setSuffix(" stars")
|
||||||
|
editor.setMinimum(0)
|
||||||
|
editor.setMaximum(5)
|
||||||
|
editor.installEventFilter(self)
|
||||||
|
return editor
|
||||||
|
|
||||||
|
def setEditorData(self, editor, index):
|
||||||
|
if index.column() != 4:
|
||||||
|
return QItemDelegate.setEditorData(self, editor, index)
|
||||||
|
val = index.model()._data[index.row()]["rating"]
|
||||||
|
if not val: val = 0
|
||||||
|
editor.setValue(val)
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
if index.column() != 4:
|
||||||
|
return QItemDelegate.setModelData(self, editor, model, index)
|
||||||
|
editor.interpretText()
|
||||||
|
index.model().setData(index, QVariant(editor.value()), Qt.EditRole)
|
||||||
|
|
||||||
|
def updateEditorGeometry(self, editor, option, index):
|
||||||
|
if index.column() != 4:
|
||||||
|
return QItemDelegate.updateEditorGeometry(self, editor, option, index)
|
||||||
|
editor.setGeometry(option.rect)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class LibraryBooksModel(QAbstractTableModel):
|
class LibraryBooksModel(QAbstractTableModel):
|
||||||
FIELDS = ["id", "title", "authors", "size", "date", "publisher", "tags"]
|
FIELDS = ["id", "title", "authors", "size", "date", "rating", "publisher", "tags"]
|
||||||
TIME_READ_FMT = "%Y-%m-%d %H:%M:%S"
|
TIME_READ_FMT = "%Y-%m-%d %H:%M:%S"
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QAbstractTableModel.__init__(self, parent)
|
QAbstractTableModel.__init__(self, parent)
|
||||||
@ -63,6 +152,40 @@ class LibraryBooksModel(QAbstractTableModel):
|
|||||||
def rowCount(self, parent): return len(self._data)
|
def rowCount(self, parent): return len(self._data)
|
||||||
def columnCount(self, parent): return len(self.FIELDS)-2
|
def columnCount(self, parent): return len(self.FIELDS)-2
|
||||||
|
|
||||||
|
def setData(self, index, value, role):
|
||||||
|
done = False
|
||||||
|
if role == Qt.EditRole:
|
||||||
|
row = index.row()
|
||||||
|
id = self._data[row]["id"]
|
||||||
|
col = index.column()
|
||||||
|
val = str(value.toString())
|
||||||
|
if col == 0: col = "title"
|
||||||
|
elif col == 1: col = "authors"
|
||||||
|
elif col == 2: return False
|
||||||
|
elif col == 3: return False
|
||||||
|
elif col == 4:
|
||||||
|
col, val = "rating", int(value.toInt()[0])
|
||||||
|
if val < 0: val =0
|
||||||
|
if val > 5: val = 5
|
||||||
|
elif col == 5: col = "publisher"
|
||||||
|
else: return False
|
||||||
|
self.db.set_metadata_item(id, col, val)
|
||||||
|
self._data[row][col] = val
|
||||||
|
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), index, index)
|
||||||
|
for i in range(len(self._orig_data)):
|
||||||
|
if self._orig_data[i]["id"] == self._data[row]["id"]:
|
||||||
|
self._orig_data[i][col] = self._data[row][col]
|
||||||
|
break
|
||||||
|
done = True
|
||||||
|
return done
|
||||||
|
|
||||||
|
def flags(self, index):
|
||||||
|
flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
|
||||||
|
col = index.column()
|
||||||
|
if col not in [2,3]:
|
||||||
|
flags |= Qt.ItemIsEditable
|
||||||
|
return flags
|
||||||
|
|
||||||
def set_data(self, db):
|
def set_data(self, db):
|
||||||
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
||||||
self.db = db
|
self.db = db
|
||||||
@ -79,7 +202,8 @@ class LibraryBooksModel(QAbstractTableModel):
|
|||||||
elif section == 1: text = "Author(s)"
|
elif section == 1: text = "Author(s)"
|
||||||
elif section == 2: text = "Size"
|
elif section == 2: text = "Size"
|
||||||
elif section == 3: text = "Date"
|
elif section == 3: text = "Date"
|
||||||
elif section == 4: text = "Publisher"
|
elif section == 4: text = "Rating"
|
||||||
|
elif section == 5: text = "Publisher"
|
||||||
return QVariant(self.trUtf8(text))
|
return QVariant(self.trUtf8(text))
|
||||||
else: return QVariant(str(1+section))
|
else: return QVariant(str(1+section))
|
||||||
|
|
||||||
@ -103,23 +227,32 @@ class LibraryBooksModel(QAbstractTableModel):
|
|||||||
|
|
||||||
def refresh_row(self, row):
|
def refresh_row(self, row):
|
||||||
self._data[row] = self.db.get_row_by_id(self._data[row]["id"], self.FIELDS)
|
self._data[row] = self.db.get_row_by_id(self._data[row]["id"], self.FIELDS)
|
||||||
|
for i in range(len(self._orig_data)):
|
||||||
|
if self._orig_data[i]["id"] == self._data[row]["id"]:
|
||||||
|
self._orig_data[i:i+1] = self._data[row]
|
||||||
|
break
|
||||||
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.index(row, 0), self.index(row, self.columnCount(0)-1))
|
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.index(row, 0), self.index(row, self.columnCount(0)-1))
|
||||||
|
|
||||||
def data(self, index, role):
|
def data(self, index, role):
|
||||||
if role == Qt.DisplayRole:
|
if role == Qt.DisplayRole or role == Qt.EditRole:
|
||||||
row, col = index.row(), index.column()
|
row, col = index.row(), index.column()
|
||||||
text = None
|
text = None
|
||||||
row = self._data[row]
|
row = self._data[row]
|
||||||
|
if col == 4:
|
||||||
|
r = row["rating"] if row["rating"] else 0
|
||||||
|
if r < 0: r= 0
|
||||||
|
if r > 5: r=5
|
||||||
|
return QVariant(r)
|
||||||
if col == 0: text = wrap(row["title"], width=25)
|
if col == 0: text = wrap(row["title"], width=25)
|
||||||
elif col == 1:
|
elif col == 1:
|
||||||
au = row["authors"]
|
au = row["authors"]
|
||||||
if au : text = wrap(re.sub("&", "\n", au), width=25)
|
if au : text = wrap(re.sub("&", "\n", au), width=25)
|
||||||
elif col == 2: text = human_readable(row["size"])
|
elif col == 2: text = human_readable(row["size"])
|
||||||
elif col == 3: text = time.strftime(TIME_WRITE_FMT, time.strptime(row["date"], self.TIME_READ_FMT))
|
elif col == 3: text = time.strftime(TIME_WRITE_FMT, time.strptime(row["date"], self.TIME_READ_FMT))
|
||||||
elif col == 4:
|
elif col == 5:
|
||||||
pub = row["publisher"]
|
pub = row["publisher"]
|
||||||
if pub: text = wrap(pub, 20)
|
if pub: text = wrap(pub, 20)
|
||||||
if not text: text = "Unknown"
|
if text == None: text = "Unknown"
|
||||||
return QVariant(text)
|
return QVariant(text)
|
||||||
elif role == Qt.TextAlignmentRole and index.column() in [2,3,4]:
|
elif role == Qt.TextAlignmentRole and index.column() in [2,3,4]:
|
||||||
return QVariant(Qt.AlignRight)
|
return QVariant(Qt.AlignRight)
|
||||||
@ -132,7 +265,8 @@ class LibraryBooksModel(QAbstractTableModel):
|
|||||||
if col == 1: key, func = "authors", lambda x : x.split()[-1:][0].lower() if x else ""
|
if col == 1: key, func = "authors", lambda x : x.split()[-1:][0].lower() if x else ""
|
||||||
if col == 2: key, func = "size", int
|
if col == 2: key, func = "size", int
|
||||||
if col == 3: key, func = "date", lambda x: time.mktime(time.strptime(x, self.TIME_READ_FMT))
|
if col == 3: key, func = "date", lambda x: time.mktime(time.strptime(x, self.TIME_READ_FMT))
|
||||||
if col == 4: key, func = "publisher", lambda x : x.lower() if x else ""
|
if col == 4: key, func = "rating", lambda x: x if x else 0
|
||||||
|
if col == 5: key, func = "publisher", lambda x : x.lower() if x else ""
|
||||||
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
||||||
self._data.sort(key=getter(key, func))
|
self._data.sort(key=getter(key, func))
|
||||||
if descending: self._data.reverse()
|
if descending: self._data.reverse()
|
||||||
@ -159,6 +293,20 @@ class LibraryBooksModel(QAbstractTableModel):
|
|||||||
if match: self._data.append(book)
|
if match: self._data.append(book)
|
||||||
self.emit(SIGNAL("layoutChanged()"))
|
self.emit(SIGNAL("layoutChanged()"))
|
||||||
self.emit(SIGNAL("searched()"))
|
self.emit(SIGNAL("searched()"))
|
||||||
|
|
||||||
|
def delete(self, indices):
|
||||||
|
if len(indices): self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
||||||
|
for index in indices:
|
||||||
|
id = self.id_from_index(index)
|
||||||
|
self.db.delete_by_id(id)
|
||||||
|
row = index.row()
|
||||||
|
self._data[row:row+1] = []
|
||||||
|
for i in range(len(self._orig_data)):
|
||||||
|
if self._orig_data[i]["id"] == id:
|
||||||
|
self._orig_data[i:i+1] = []
|
||||||
|
i -=1
|
||||||
|
self.emit(SIGNAL("layoutChanged()"))
|
||||||
|
self.db.commit()
|
||||||
|
|
||||||
class DeviceBooksModel(QAbstractTableModel):
|
class DeviceBooksModel(QAbstractTableModel):
|
||||||
TIME_READ_FMT = "%a, %d %b %Y %H:%M:%S %Z"
|
TIME_READ_FMT = "%a, %d %b %Y %H:%M:%S %Z"
|
||||||
@ -311,12 +459,20 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
self.device_view.resizeColumnsToContents()
|
self.device_view.resizeColumnsToContents()
|
||||||
|
|
||||||
def model_modified(self):
|
def model_modified(self):
|
||||||
self.device_view.clearSelection()
|
if self.library_view.isVisible(): view = self.library_view
|
||||||
self.library_view.clearSelection()
|
else: view = self.device_view
|
||||||
|
view.clearSelection()
|
||||||
|
view.resizeColumnsToContents()
|
||||||
self.book_cover.hide()
|
self.book_cover.hide()
|
||||||
self.book_info.hide()
|
self.book_info.hide()
|
||||||
QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
|
QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
|
||||||
|
|
||||||
|
def resize_columns(self, topleft, bottomright):
|
||||||
|
if self.library_view.isVisible(): view = self.library_view
|
||||||
|
else: view = self.device_view
|
||||||
|
for c in range(topleft.column(), bottomright.column()+1):
|
||||||
|
view.resizeColumnToContents(c)
|
||||||
|
|
||||||
def show_book(self, current, previous):
|
def show_book(self, current, previous):
|
||||||
title, author, size, mime, thumbnail = current.model().info(current.row())
|
title, author, size, mime, thumbnail = current.model().info(current.row())
|
||||||
self.book_info.setText(self.BOOK_TEMPLATE.arg(title).arg(size).arg(author).arg(mime))
|
self.book_info.setText(self.BOOK_TEMPLATE.arg(title).arg(size).arg(author).arg(mime))
|
||||||
@ -332,48 +488,49 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
def list_context_event(self, event):
|
def list_context_event(self, event):
|
||||||
print "TODO:"
|
print "TODO:"
|
||||||
|
|
||||||
def do_delete(self, rows):
|
|
||||||
if self.device_model.__class__.__name__ == "DeviceBooksdevice_model":
|
|
||||||
paths, mc, cc = [], False, False
|
|
||||||
for book in rows:
|
|
||||||
path = book.model().path(book)
|
|
||||||
if path[0] == "/": file, prefix, mc = self.main_xml, "xs1:", True
|
|
||||||
else: file, prefix, cc = self.cache_xml, "", True
|
|
||||||
file.seek(0)
|
|
||||||
document = dom.parse(file)
|
|
||||||
books = document.getElementsByTagName(prefix + "text")
|
|
||||||
for candidate in books:
|
|
||||||
if candidate.attributes["path"].value in path:
|
|
||||||
paths.append(path)
|
|
||||||
candidate.parentNode.removeChild(candidate)
|
|
||||||
break
|
|
||||||
file.close()
|
|
||||||
file = TemporaryFile()
|
|
||||||
PrettyPrint(document, file)
|
|
||||||
if len(prefix) > 0: self.main_xml = file
|
|
||||||
else: self.cache_xml = file
|
|
||||||
for path in paths:
|
|
||||||
self.dev.del_file(path)
|
|
||||||
self.device_model.delete_by_path(path)
|
|
||||||
self.cache_xml.seek(0)
|
|
||||||
self.main_xml.seek(0)
|
|
||||||
self.status("Files deleted. Updating media list on device")
|
|
||||||
if mc:
|
|
||||||
self.dev.del_file(self.dev.MEDIA_XML)
|
|
||||||
self.dev.put_file(self.main_xml, self.dev.MEDIA_XML)
|
|
||||||
if cc:
|
|
||||||
self.dev.del_file(self.card+self.dev.CACHE_XML)
|
|
||||||
self.dev.put_file(self.cache_xml, self.card+self.dev.CACHE_XML)
|
|
||||||
|
|
||||||
def delete(self, action):
|
def delete(self, action):
|
||||||
self.window.setCursor(Qt.WaitCursor)
|
if self.device_view.isVisible():
|
||||||
rows = self.device_view.selectionModel().selectedRows()
|
rows = self.device_view.selectionModel().selectedRows()
|
||||||
items = [ row.model().title(row) + ": " + row.model().path(row)[row.model().path(row).rfind("/")+1:] for row in rows ]
|
items = [ row.model().title(row) + ": " + row.model().path(row)[row.model().path(row).rfind("/")+1:] for row in rows ]
|
||||||
ret = QMessageBox.question(self.window, self.trUtf8("SONY Reader - confirm"), self.trUtf8("Are you sure you want to delete these items from the device?\n\n") + "\n".join(items),
|
ret = QMessageBox.question(self.window, self.trUtf8("SONY Reader - confirm"), self.trUtf8("Are you sure you want to delete these items from the device?\n\n") + "\n".join(items),
|
||||||
QMessageBox.YesToAll | QMessageBox.No, QMessageBox.YesToAll)
|
QMessageBox.YesToAll | QMessageBox.No, QMessageBox.YesToAll)
|
||||||
if ret == QMessageBox.YesToAll:
|
if ret == QMessageBox.YesToAll:
|
||||||
self.do_delete(rows)
|
self.window.setCursor(Qt.WaitCursor)
|
||||||
self.window.setCursor(Qt.ArrowCursor)
|
paths, mc, cc = [], False, False
|
||||||
|
for book in rows:
|
||||||
|
path = book.model().path(book)
|
||||||
|
if path[0] == "/": file, prefix, mc = self.main_xml, "xs1:", True
|
||||||
|
else: file, prefix, cc = self.cache_xml, "", True
|
||||||
|
file.seek(0)
|
||||||
|
document = dom.parse(file)
|
||||||
|
books = document.getElementsByTagName(prefix + "text")
|
||||||
|
for candidate in books:
|
||||||
|
if candidate.attributes["path"].value in path:
|
||||||
|
paths.append(path)
|
||||||
|
candidate.parentNode.removeChild(candidate)
|
||||||
|
break
|
||||||
|
file.close()
|
||||||
|
file = TemporaryFile()
|
||||||
|
PrettyPrint(document, file)
|
||||||
|
if len(prefix) > 0: self.main_xml = file
|
||||||
|
else: self.cache_xml = file
|
||||||
|
for path in paths:
|
||||||
|
self.dev.del_file(path)
|
||||||
|
self.device_model.delete_by_path(path)
|
||||||
|
self.cache_xml.seek(0)
|
||||||
|
self.main_xml.seek(0)
|
||||||
|
self.status("Files deleted. Updating media list on device")
|
||||||
|
if mc:
|
||||||
|
self.dev.del_file(self.dev.MEDIA_XML)
|
||||||
|
self.dev.put_file(self.main_xml, self.dev.MEDIA_XML)
|
||||||
|
if cc:
|
||||||
|
self.dev.del_file(self.card+self.dev.CACHE_XML)
|
||||||
|
self.dev.put_file(self.cache_xml, self.card+self.dev.CACHE_XML)
|
||||||
|
self.window.setCursor(Qt.ArrowCursor)
|
||||||
|
else:
|
||||||
|
self.library_model.delete(self.library_view.selectionModel().selectedRows())
|
||||||
|
|
||||||
def read_settings(self):
|
def read_settings(self):
|
||||||
settings = QSettings()
|
settings = QSettings()
|
||||||
@ -451,15 +608,16 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
self.library_view.setSelectionBehavior(QAbstractItemView.SelectRows)
|
self.library_view.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
self.library_view.setSortingEnabled(True)
|
self.library_view.setSortingEnabled(True)
|
||||||
self.library_view.contextMenuEvent = self.list_context_event
|
self.library_view.contextMenuEvent = self.list_context_event
|
||||||
|
self.library_view.setItemDelegate(LibraryDelegate(self.library_view))
|
||||||
QObject.connect(self.library_model, SIGNAL("layoutChanged()"), self.library_view.resizeRowsToContents)
|
QObject.connect(self.library_model, SIGNAL("layoutChanged()"), self.library_view.resizeRowsToContents)
|
||||||
QObject.connect(self.library_view.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.show_book)
|
QObject.connect(self.library_view.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.show_book)
|
||||||
QObject.connect(self.search, SIGNAL("textChanged(QString)"), self.library_model.search)
|
QObject.connect(self.search, SIGNAL("textChanged(QString)"), self.library_model.search)
|
||||||
QObject.connect(self.library_model, SIGNAL("sorted()"), self.model_modified)
|
QObject.connect(self.library_model, SIGNAL("sorted()"), self.model_modified)
|
||||||
QObject.connect(self.library_model, SIGNAL("searched()"), self.model_modified)
|
QObject.connect(self.library_model, SIGNAL("searched()"), self.model_modified)
|
||||||
QObject.connect(self.library_model, SIGNAL("deleted()"), self.model_modified)
|
QObject.connect(self.library_model, SIGNAL("deleted()"), self.model_modified)
|
||||||
|
QObject.connect(self.library_model, SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.resize_columns)
|
||||||
self.library_view.resizeColumnsToContents()
|
self.library_view.resizeColumnsToContents()
|
||||||
|
|
||||||
|
|
||||||
# Create Device list
|
# Create Device list
|
||||||
self.tree = QStandardItemModel()
|
self.tree = QStandardItemModel()
|
||||||
library = QStandardItem(QString("Library"))
|
library = QStandardItem(QString("Library"))
|
||||||
@ -504,6 +662,7 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
QObject.connect(self.device_model, SIGNAL("sorted()"), self.model_modified)
|
QObject.connect(self.device_model, SIGNAL("sorted()"), self.model_modified)
|
||||||
QObject.connect(self.device_model, SIGNAL("searched()"), self.model_modified)
|
QObject.connect(self.device_model, SIGNAL("searched()"), self.model_modified)
|
||||||
QObject.connect(self.device_model, SIGNAL("deleted()"), self.model_modified)
|
QObject.connect(self.device_model, SIGNAL("deleted()"), self.model_modified)
|
||||||
|
QObject.connect(self.device_model, SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.resize_columns)
|
||||||
self.device_view.hide()
|
self.device_view.hide()
|
||||||
|
|
||||||
# Setup book display
|
# Setup book display
|
||||||
@ -612,3 +771,4 @@ def main():
|
|||||||
ret = app.exec_()
|
ret = app.exec_()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
if __name__ == "__main__": main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user