From 38273e5b10940194965bfcf59791b68f6bce7c6f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 30 Nov 2006 16:35:53 +0000 Subject: [PATCH] Possible fix for available space bug in the GUI --- README | 17 +++--- libprs500/gui/database.py | 44 +++++++++++++-- libprs500/gui/editbook.py | 90 ++++++++++++++++++++++++++++++ libprs500/gui/editbook.ui | 38 +++++++------ libprs500/gui/images/fileopen.png | Bin 0 -> 2232 bytes libprs500/gui/main.py | 31 +++++++--- prs-500.e4p | 21 ++++--- 7 files changed, 194 insertions(+), 47 deletions(-) create mode 100644 libprs500/gui/editbook.py create mode 100644 libprs500/gui/images/fileopen.png diff --git a/README b/README index bca84415ff..b78423228f 100644 --- a/README +++ b/README @@ -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 diff --git a/libprs500/gui/database.py b/libprs500/gui/database.py index e49da8ebe3..17e591f845 100644 --- a/libprs500/gui/database.py +++ b/libprs500/gui/database.py @@ -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"]) diff --git a/libprs500/gui/editbook.py b/libprs500/gui/editbook.py new file mode 100644 index 0000000000..d22a53d9bd --- /dev/null +++ b/libprs500/gui/editbook.py @@ -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")) diff --git a/libprs500/gui/editbook.ui b/libprs500/gui/editbook.ui index e90c549f86..b65f4b3542 100644 --- a/libprs500/gui/editbook.ui +++ b/libprs500/gui/editbook.ui @@ -242,7 +242,11 @@ 6 - + + + true + + @@ -353,22 +357,6 @@ - - button_box - accepted() - BookEditDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - button_box rejected() @@ -385,5 +373,21 @@ + + button_box + accepted() + BookEditDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + diff --git a/libprs500/gui/images/fileopen.png b/libprs500/gui/images/fileopen.png new file mode 100644 index 0000000000000000000000000000000000000000..503a0045911aea0af4dfa1562ca1216941935702 GIT binary patch literal 2232 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 y9fyE`yPi+hMiG$Q8L#T~L@HDW_JError: "+str(e)+"

Traceback:
"+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 diff --git a/prs-500.e4p b/prs-500.e4p index ae64148905..38f2e97943 100644 --- a/prs-500.e4p +++ b/prs-500.e4p @@ -1,7 +1,7 @@ - + Python @@ -70,6 +70,11 @@ gui database.py + + libprs500 + gui + editbook.py +
@@ -96,12 +101,6 @@ epydoc-pdf.conf - - libprs500 - gui - resources - images.qrc - libprs500 @@ -222,10 +221,10 @@ - - - - + + + +