Possible fix for available space bug in the GUI

This commit is contained in:
Kovid Goyal 2006-11-30 16:35:53 +00:00
parent 00ae3e2193
commit 38273e5b10
7 changed files with 194 additions and 47 deletions

17
README
View File

@ -1,19 +1,22 @@
Library implementing a reverse engineered protocol to communicate with the Sony Reader PRS-500.
Requirements:
1) Python >= 2.5
2) PyUSB >= 0.3.4 (http://sourceforge.net/projects/pyusb/)
3) For the GUI:
- pyxml >= 0.84
- PyQt4 >= 4.1
Installation:
As root
python setup.py install
Usage:
Add the following to /etc/udev/rules.d/90-local.rules
On Linux, to enable access to the reader for non-root users, you need to add the following to
/etc/udev/rules.d/90-local.rules
BUS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="plugdev"
and run udevstart to enable access to the reader for non-root users. You may have to adjust the GROUP and the location of the
rules file to suit your distribution.
You may have to adjust the GROUP and the location of the 90-local.rules file to suit your distribution.
Usage information is provided when you run the script prs500.py
Usage:
1) A command line interface is provided via, the command prs500
2) A GUI is provided via the command prs500-gui
3) You can read/edit the metadata of LRF files via the command lrf-meta

View File

@ -22,7 +22,7 @@ class LibraryDatabase(object):
BOOKS_SQL = """
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 );
cover BLOB, date DATE DEFAULT CURRENT_TIMESTAMP, comments TEXT );
create table if not exists books_data(id INTEGER, extension TEXT, data BLOB);
"""
@ -53,11 +53,19 @@ class LibraryDatabase(object):
if "unknown" in author.lower(): author = None
file = zlib.compress(open(file).read())
if cover: cover = sqlite.Binary(zlib.compress(cover))
self.con.execute("insert into books_meta (title, authors, publisher, size, tags, cover) values (?,?,?,?,?,?)", (title, author, publisher, size, None, cover))
self.con.execute("insert into books_meta (title, authors, publisher, size, tags, cover, comments) values (?,?,?,?,?,?)", (title, author, publisher, size, None, cover, None))
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.commit()
def get_row_by_id(self, id, columns):
""" @param columns: list of column names """
cols = ",".join([ c for c in columns])
cur = self.con.execute("select " + cols + " from books_meta where id=?", (id,))
row, r = cur.next(), {}
for c in columns: r[c] = row[c]
return r
def get_table(self, columns):
cols = ",".join([ c for c in columns])
cur = self.con.execute("select " + cols + " from books_meta")
@ -68,6 +76,24 @@ class LibraryDatabase(object):
rows.append(r)
return rows
def get_format(self, id, ext):
ext = ext.lower()
cur = self.cur.execute("select data from books_data where id=? and extension=?",(id, ext))
try: data = cur.next()
except: pass
else: return zlib.decompress(str(data["data"]))
def add_format(self, id, ext, data):
cur = self.con.execute("select extension from books_data where id=? and extension=?", (id, ext))
present = True
try: cur.next()
except: present = False
if present:
self.con.execute("update books_data set data=? where id=? and extension=?", (data, id, ext))
else:
self.con.execute("insert into books_data (id, extension, data) values (?, ?, ?)", (id, ext, data))
self.con.commit()
def get_meta_data(self, id):
try: row = self.con.execute("select * from books_meta where id=?", (id,)).next()
except StopIteration: return None
@ -76,11 +102,19 @@ class LibraryDatabase(object):
data[field] = row[field]
return data
def set_metadata(self, id, title=None, authors=None, publisher=None, tags=None, cover=None, comments=None):
if authors and not len(authors): authors = None
if publisher and not len(publisher): publisher = None
if tags and not len(tags): tags = None
if comments and not len(comments): comments = None
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.commit()
def search(self, query): pass
if __name__ == "__main__":
lbm = LibraryDatabase("/home/kovid/library.sqlite")
#if __name__ == "__main__":
# lbm = LibraryDatabase("/home/kovid/library.db")
# lbm.add_book("/home/kovid/documents/ebooks/hobbfar01.lrf")
# lbm.add_book("/home/kovid/documents/ebooks/hobbfar02.lrf")
# lbm.add_book("/home/kovid/documents/ebooks/hobbfar03.lrf")
@ -88,4 +122,4 @@ if __name__ == "__main__":
# lbm.add_book("/home/kovid/documents/ebooks/hobbtawny01.lrf")
# lbm.add_book("/home/kovid/documents/ebooks/hobbtawny02.lrf")
# lbm.add_book("/home/kovid/documents/ebooks/hobbtawny03.lrf")
print lbm.get_table(["id","title"])
# print lbm.get_table(["id","title"])

90
libprs500/gui/editbook.py Normal file
View File

@ -0,0 +1,90 @@
## Copyright (C) 2006 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.
import sys, os, pkg_resources, StringIO
from PyQt4 import uic
from PyQt4.QtCore import Qt, SIGNAL
from PyQt4.Qt import QObject, QDialog, QPixmap
from libprs500.lrf.meta import LRFMeta
ui = pkg_resources.resource_stream(__name__, "editbook.ui")
sys.path.append(os.path.dirname(ui.name))
Ui_BookEditDialog, bclass = uic.loadUiType(pkg_resources.resource_stream(__name__, "editbook.ui"))
class EditBookDialog(Ui_BookEditDialog):
def select_cover(self, checked):
settings = QSettings()
dir = settings.value("change cover dir", QVariant(os.path.expanduser("~"))).toString()
file = QFileDialog.getOpenFileName(self.window, "Choose cover for " + str(self.title.text(), dir, "Images (*.png *.gif *.jpeg *.jpg);;All files (*)"))
if len(str(file)):
file = os.path.abspath(file)
settings.setValue("change cover dir", QVariant(os.path.dirname(file)))
if not os.access(file, os.R_OK):
QErrorMessage(self.parent).showMessage("You do not have permission to read the file: " + file)
cf, cover = None, None
try:
cf = open(file, "rb")
cover = cf.read()
except IOError, e: QErrorMessage(self.parent).showMessage("There was an error reading from file: " + file + "\n"+str(e))
if cover:
pix = QPixmap()
pix.loadFromData(cover, "", Qt.AutoColor)
if pix.isNull(): QErrorMessage(self.parent).showMessage(file + " is not a valid picture")
else:
self.cover_path.setText(file)
self.cover.setPixmap(pix)
self.cover_data = cover
def write_data(self):
title = str(self.title.text()).strip()
authors = str(self.authors.text()).strip()
tags = str(self.tags.text()).strip()
publisher = str(self.publisher.text()).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)
lrf = self.db.get_format(self.id, "lrf")
if lrf:
lrf = StringIO.StringIO(lrf)
lf = LRFMeta(lrf)
if title: lf.title = title
if authors: lf.title = authors
if publisher: lf.publisher = publisher
if self.cover_data: lf.thumbnail = self.cover_data
self.db.add_format(self.id, "lrf", lrf.getvalue())
def __init__(self, dialog, id, db):
Ui_BookEditDialog.__init__(self)
self.parent = dialog
self.setupUi(dialog)
self.db = db
self.id = id
self.cover_data = None
QObject.connect(self.cover_button, SIGNAL("clicked(bool)"), self.select_cover)
QObject.connect(self.button_box, SIGNAL("accepted()"), self.write_data)
data = self.db.get_row_by_id(self.id, ["title","authors","publisher","tags","comments"])
self.title.setText(data["title"])
self.authors.setText(data["authors"] if data["authors"] else "")
self.publisher.setText(data["publisher"] if data["publisher"] else "")
self.tags.setText(data["tags"] if data["tags"] else "")
self.comments.setPlainText(data["comments"] if data["comments"] else "")
cover = self.db.get_cover(self.id)
if cover:
pm = QPixmap()
pm.loadFromData(cover, "", Qt.AutoColor)
self.cover.setPixmap(pm)
else:
self.cover.setPixmap(QPixmap(":/default_cover"))

View File

@ -242,7 +242,11 @@
<number>6</number>
</property>
<item>
<widget class="QLineEdit" name="cover_path" />
<widget class="QLineEdit" name="cover_path" >
<property name="readOnly" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="cover_button" >
@ -353,22 +357,6 @@
<include location="images.qrc" />
</resources>
<connections>
<connection>
<sender>button_box</sender>
<signal>accepted()</signal>
<receiver>BookEditDialog</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>
@ -385,5 +373,21 @@
</hint>
</hints>
</connection>
<connection>
<sender>button_box</sender>
<signal>accepted()</signal>
<receiver>BookEditDialog</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>
</connections>
</ui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -16,9 +16,11 @@ from libprs500.communicate import PRS500Device as device
from libprs500.errors import *
from libprs500.lrf.meta import LRFMetaFile, LRFException
from database import LibraryDatabase
from editbook import EditBookDialog
from PyQt4.QtCore import Qt, SIGNAL
from PyQt4.Qt import QObject, QThread, QCoreApplication, QEventLoop, QString, QStandardItem, QStandardItemModel, QStatusBar, QVariant, QAbstractTableModel, \
QAbstractItemView, QImage, QPixmap, QIcon, QSize, QMessageBox, QSettings, QFileDialog, QErrorMessage
QAbstractItemView, QImage, QPixmap, QIcon, QSize, QMessageBox, QSettings, QFileDialog, QErrorMessage, QDialog
from PyQt4 import uic
import sys, pkg_resources, re, string, time, os, os.path, traceback, textwrap, zlib
from stat import ST_SIZE
@ -34,7 +36,7 @@ TIME_WRITE_FMT = "%d %b %Y"
COVER_HEIGHT = 80
def human_readable(size):
""" Convert a size in bytes into a human readle form """
""" Convert a size in bytes into a human readable form """
if size < 1024: divisor, suffix = 1, "B"
elif size < 1024*1024: divisor, suffix = 1024., "KB"
elif size < 1024*1024*1024: divisor, suffix = 1024*1024, "MB"
@ -96,6 +98,12 @@ class LibraryBooksModel(QAbstractTableModel):
cover = pix.scaledToHeight(COVER_HEIGHT, Qt.SmoothTransformation)
return row["title"], row["authors"], human_readable(int(row["size"])), exts, cover
def id_from_index(self, index): return self._data[index.row()]["id"]
def refresh_row(self, row):
self._data[row] = self.db.get_row_by_id(self._data[row]["id"], self.FIELDS)
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.index(row, 0), self.index(row, self.columnCount(0)-1))
def data(self, index, role):
if role == Qt.DisplayRole:
row, col = index.row(), index.column()
@ -258,7 +266,7 @@ class DeviceBooksModel(QAbstractTableModel):
ui = pkg_resources.resource_stream(__name__, "main.ui")
sys.path.append(os.path.dirname(ui.name))
Ui_MainWindow, bclass = uic.loadUiType(pkg_resources.resource_stream(__name__, "main.ui"))
Ui_MainWindow, bclass = uic.loadUiType(pkg_resources.resource_stream(__name__, "main.ui"))
class MainWindow(QObject, Ui_MainWindow):
def show_device(self, yes):
@ -373,7 +381,7 @@ class MainWindow(QObject, Ui_MainWindow):
settings.beginGroup("MainWindow")
self.window.resize(settings.value("size", QVariant(QSize(1000, 700))).toSize())
settings.endGroup()
self.database_path = settings.value("database path", QVariant(os.path.expanduser("~/library.sqlite"))).toString()
self.database_path = settings.value("database path", QVariant(os.path.expanduser("~/library.db"))).toString()
def write_settings(self):
settings = QSettings()
@ -411,14 +419,23 @@ class MainWindow(QObject, Ui_MainWindow):
self.library_model.add(file, title, author, publisher, cover)
def edit(self, action):
pass
if self.library_view.isVisible():
rows = self.library_view.selectionModel().selectedRows()
for row in rows:
id = self.library_model.id_from_index(row)
dialog = QDialog(self.window)
ed = EditBookDialog(dialog, id, self.library_model.db)
if dialog.exec_() == QDialog.Accepted:
self.library_model.refresh_row(row.row())
def show_error(self, e, msg):
QErrorMessage(self.window).showMessage(msg+"<br><b>Error: </b>"+str(e)+"<br><br>Traceback:<br>"+traceback.format_exc(e))
def __init__(self, window):
QObject.__init__(self)
Ui_MainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.dev = device(report_progress=self.progress)
self.is_connected = False
self.setupUi(window)
@ -548,7 +565,7 @@ class MainWindow(QObject, Ui_MainWindow):
self.status("Connecting to device")
try:
space = self.dev.available_space()
except TimeoutError:
except ProtocolError:
c = 0
self.status("Waiting for device to initialize")
while c < 100: # Delay for 10s while device is initializing

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-4.0.dtd">
<!-- eric4 project file for project prs-500 -->
<!-- Saved: 2006-11-24, 20:24:29 -->
<!-- Saved: 2006-11-29, 11:51:16 -->
<!-- Copyright (C) 2006 Kovid Goyal, kovid@kovidgoyal.net -->
<Project version="4.0">
<ProgLanguage mixed="0">Python</ProgLanguage>
@ -70,6 +70,11 @@
<Dir>gui</Dir>
<Name>database.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Dir>gui</Dir>
<Name>editbook.py</Name>
</Source>
</Sources>
<Forms>
<Form>
@ -96,12 +101,6 @@
<Other>
<Name>epydoc-pdf.conf</Name>
</Other>
<Other>
<Dir>libprs500</Dir>
<Dir>gui</Dir>
<Dir>resources</Dir>
<Name>images.qrc</Name>
</Other>
</Others>
<MainScript>
<Dir>libprs500</Dir>
@ -222,10 +221,10 @@
</VcsOtherData>
</Vcs>
<FiletypeAssociations>
<FiletypeAssociation pattern="*.ui.h" type="FORMS" />
<FiletypeAssociation pattern="*.ui" type="FORMS" />
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
<FiletypeAssociation pattern="*.ptl" type="SOURCES" />
<FiletypeAssociation pattern="*.py" type="SOURCES" />
<FiletypeAssociation pattern="*.ui.h" type="FORMS" />
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
<FiletypeAssociation pattern="*.ui" type="FORMS" />
<FiletypeAssociation pattern="*.ptl" type="SOURCES" />
</FiletypeAssociations>
</Project>