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: Requirements:
1) Python >= 2.5 1) Python >= 2.5
2) PyUSB >= 0.3.4 (http://sourceforge.net/projects/pyusb/) 2) PyUSB >= 0.3.4 (http://sourceforge.net/projects/pyusb/)
3) For the GUI:
- pyxml >= 0.84
- PyQt4 >= 4.1
Installation: Installation:
As root As root
python setup.py install python setup.py install
Usage: On Linux, to enable access to the reader for non-root users, you need to add the following to
Add the following to /etc/udev/rules.d/90-local.rules /etc/udev/rules.d/90-local.rules
BUS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="plugdev" 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 You may have to adjust the GROUP and the location of the 90-local.rules file to suit your distribution.
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 = """ 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 ); cover BLOB, date DATE DEFAULT CURRENT_TIMESTAMP, comments TEXT );
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,11 +53,19 @@ 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) 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] 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()
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): 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")
@ -68,6 +76,24 @@ class LibraryDatabase(object):
rows.append(r) rows.append(r)
return rows 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): def get_meta_data(self, id):
try: row = self.con.execute("select * from books_meta where id=?", (id,)).next() try: row = self.con.execute("select * from books_meta where id=?", (id,)).next()
except StopIteration: return None except StopIteration: return None
@ -76,11 +102,19 @@ 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):
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 def search(self, query): pass
if __name__ == "__main__": #if __name__ == "__main__":
lbm = LibraryDatabase("/home/kovid/library.sqlite") # lbm = LibraryDatabase("/home/kovid/library.db")
# lbm.add_book("/home/kovid/documents/ebooks/hobbfar01.lrf") # 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/hobbfar02.lrf")
# lbm.add_book("/home/kovid/documents/ebooks/hobbfar03.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/hobbtawny01.lrf")
# lbm.add_book("/home/kovid/documents/ebooks/hobbtawny02.lrf") # lbm.add_book("/home/kovid/documents/ebooks/hobbtawny02.lrf")
# lbm.add_book("/home/kovid/documents/ebooks/hobbtawny03.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> <number>6</number>
</property> </property>
<item> <item>
<widget class="QLineEdit" name="cover_path" /> <widget class="QLineEdit" name="cover_path" >
<property name="readOnly" >
<bool>true</bool>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="QToolButton" name="cover_button" > <widget class="QToolButton" name="cover_button" >
@ -353,22 +357,6 @@
<include location="images.qrc" /> <include location="images.qrc" />
</resources> </resources>
<connections> <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> <connection>
<sender>button_box</sender> <sender>button_box</sender>
<signal>rejected()</signal> <signal>rejected()</signal>
@ -385,5 +373,21 @@
</hint> </hint>
</hints> </hints>
</connection> </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> </connections>
</ui> </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.errors import *
from libprs500.lrf.meta import LRFMetaFile, LRFException from libprs500.lrf.meta import LRFMetaFile, LRFException
from database import LibraryDatabase from database import LibraryDatabase
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 QAbstractItemView, QImage, QPixmap, QIcon, QSize, QMessageBox, QSettings, QFileDialog, QErrorMessage, QDialog
from PyQt4 import uic from PyQt4 import uic
import sys, pkg_resources, re, string, time, os, os.path, traceback, textwrap, zlib import sys, pkg_resources, re, string, time, os, os.path, traceback, textwrap, zlib
from stat import ST_SIZE from stat import ST_SIZE
@ -34,7 +36,7 @@ TIME_WRITE_FMT = "%d %b %Y"
COVER_HEIGHT = 80 COVER_HEIGHT = 80
def human_readable(size): 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" if size < 1024: divisor, suffix = 1, "B"
elif size < 1024*1024: divisor, suffix = 1024., "KB" elif size < 1024*1024: divisor, suffix = 1024., "KB"
elif size < 1024*1024*1024: divisor, suffix = 1024*1024, "MB" elif size < 1024*1024*1024: divisor, suffix = 1024*1024, "MB"
@ -96,6 +98,12 @@ class LibraryBooksModel(QAbstractTableModel):
cover = pix.scaledToHeight(COVER_HEIGHT, Qt.SmoothTransformation) cover = pix.scaledToHeight(COVER_HEIGHT, Qt.SmoothTransformation)
return row["title"], row["authors"], human_readable(int(row["size"])), exts, cover 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): def data(self, index, role):
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
row, col = index.row(), index.column() row, col = index.row(), index.column()
@ -258,7 +266,7 @@ class DeviceBooksModel(QAbstractTableModel):
ui = pkg_resources.resource_stream(__name__, "main.ui") ui = pkg_resources.resource_stream(__name__, "main.ui")
sys.path.append(os.path.dirname(ui.name)) 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): class MainWindow(QObject, Ui_MainWindow):
def show_device(self, yes): def show_device(self, yes):
@ -373,7 +381,7 @@ class MainWindow(QObject, Ui_MainWindow):
settings.beginGroup("MainWindow") settings.beginGroup("MainWindow")
self.window.resize(settings.value("size", QVariant(QSize(1000, 700))).toSize()) self.window.resize(settings.value("size", QVariant(QSize(1000, 700))).toSize())
settings.endGroup() 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): def write_settings(self):
settings = QSettings() settings = QSettings()
@ -411,14 +419,23 @@ class MainWindow(QObject, Ui_MainWindow):
self.library_model.add(file, title, author, publisher, cover) self.library_model.add(file, title, author, publisher, cover)
def edit(self, action): 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): 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)) QErrorMessage(self.window).showMessage(msg+"<br><b>Error: </b>"+str(e)+"<br><br>Traceback:<br>"+traceback.format_exc(e))
def __init__(self, window): def __init__(self, window):
QObject.__init__(self) QObject.__init__(self)
Ui_MainWindow.__init__(self) Ui_MainWindow.__init__(self)
self.dev = device(report_progress=self.progress) self.dev = device(report_progress=self.progress)
self.is_connected = False self.is_connected = False
self.setupUi(window) self.setupUi(window)
@ -548,7 +565,7 @@ class MainWindow(QObject, Ui_MainWindow):
self.status("Connecting to device") self.status("Connecting to device")
try: try:
space = self.dev.available_space() space = self.dev.available_space()
except TimeoutError: except ProtocolError:
c = 0 c = 0
self.status("Waiting for device to initialize") self.status("Waiting for device to initialize")
while c < 100: # Delay for 10s while device is initializing while c < 100: # Delay for 10s while device is initializing

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-4.0.dtd"> <!DOCTYPE Project SYSTEM "Project-4.0.dtd">
<!-- eric4 project file for project prs-500 --> <!-- 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 --> <!-- Copyright (C) 2006 Kovid Goyal, kovid@kovidgoyal.net -->
<Project version="4.0"> <Project version="4.0">
<ProgLanguage mixed="0">Python</ProgLanguage> <ProgLanguage mixed="0">Python</ProgLanguage>
@ -70,6 +70,11 @@
<Dir>gui</Dir> <Dir>gui</Dir>
<Name>database.py</Name> <Name>database.py</Name>
</Source> </Source>
<Source>
<Dir>libprs500</Dir>
<Dir>gui</Dir>
<Name>editbook.py</Name>
</Source>
</Sources> </Sources>
<Forms> <Forms>
<Form> <Form>
@ -96,12 +101,6 @@
<Other> <Other>
<Name>epydoc-pdf.conf</Name> <Name>epydoc-pdf.conf</Name>
</Other> </Other>
<Other>
<Dir>libprs500</Dir>
<Dir>gui</Dir>
<Dir>resources</Dir>
<Name>images.qrc</Name>
</Other>
</Others> </Others>
<MainScript> <MainScript>
<Dir>libprs500</Dir> <Dir>libprs500</Dir>
@ -222,10 +221,10 @@
</VcsOtherData> </VcsOtherData>
</Vcs> </Vcs>
<FiletypeAssociations> <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="*.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> </FiletypeAssociations>
</Project> </Project>