mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
GUI now supports displaying lists of books from both the library and the device
This commit is contained in:
parent
1ec8632924
commit
c8d21c5930
@ -45,12 +45,12 @@ Contains the logic for communication with the device (a SONY PRS-500).
|
|||||||
The public interface of class L{PRS500Device} defines the methods for performing various tasks.
|
The public interface of class L{PRS500Device} defines the methods for performing various tasks.
|
||||||
"""
|
"""
|
||||||
import usb, sys, os, time
|
import usb, sys, os, time
|
||||||
|
from base64 import b64decode as decode
|
||||||
|
from tempfile import TemporaryFile
|
||||||
from array import array
|
from array import array
|
||||||
from xml.sax.handler import ContentHandler
|
from xml.sax.handler import ContentHandler
|
||||||
from xml.sax import make_parser
|
from xml.sax import make_parser
|
||||||
from xml.sax.handler import feature_namespaces
|
from xml.sax.handler import feature_namespaces
|
||||||
from base64 import b64decode as decode
|
|
||||||
from tempfile import TemporaryFile
|
|
||||||
|
|
||||||
from prstypes import *
|
from prstypes import *
|
||||||
from errors import *
|
from errors import *
|
||||||
|
91
libprs500/gui/database.py
Normal file
91
libprs500/gui/database.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
## 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 sqlite3 as sqlite
|
||||||
|
import os, os.path, zlib
|
||||||
|
from stat import ST_SIZE
|
||||||
|
from libprs500.lrf.meta import LRFMetaFile
|
||||||
|
|
||||||
|
|
||||||
|
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 );
|
||||||
|
create table if not exists books_data(id INTEGER, extension TEXT, data BLOB);
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, dbpath):
|
||||||
|
self.con = sqlite.connect(dbpath)
|
||||||
|
self.con.row_factory = sqlite.Row # Allow case insensitive field access by name
|
||||||
|
self.con.executescript(LibraryDatabase.BOOKS_SQL)
|
||||||
|
|
||||||
|
def get_cover(self, id):
|
||||||
|
raw = self.con.execute("select cover from books_meta where id=?", (id,)).next()["cover"]
|
||||||
|
return zlib.decompress(str(raw)) if raw else None
|
||||||
|
|
||||||
|
def get_extensions(self, id):
|
||||||
|
exts = []
|
||||||
|
cur = self.con.execute("select extension from books_data where id=?", (id,))
|
||||||
|
for row in cur:
|
||||||
|
exts.append(row["extension"])
|
||||||
|
return exts
|
||||||
|
|
||||||
|
def add_book(self, path):
|
||||||
|
file = os.path.abspath(path)
|
||||||
|
title, author, publisher, size, cover = os.path.basename(file), None, None, os.stat(file)[ST_SIZE], None
|
||||||
|
ext = title[title.rfind(".")+1:].lower() if title.find(".") > -1 else None
|
||||||
|
if ext == "lrf":
|
||||||
|
lrf = LRFMetaFile(open(file, "r+b"))
|
||||||
|
title, author, cover, publisher = lrf.title, lrf.author.strip(), lrf.thumbnail, lrf.publisher.strip()
|
||||||
|
if "unknown" in publisher.lower(): publisher = None
|
||||||
|
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))
|
||||||
|
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_table(self, columns):
|
||||||
|
cols = ",".join([ c for c in columns])
|
||||||
|
cur = self.con.execute("select " + cols + " from books_meta")
|
||||||
|
rows = []
|
||||||
|
for row in cur:
|
||||||
|
r = {}
|
||||||
|
for c in columns: r[c] = row[c]
|
||||||
|
rows.append(r)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
def get_meta_data(self, id):
|
||||||
|
try: row = self.con.execute("select * from books_meta where id=?", (id,)).next()
|
||||||
|
except StopIteration: return None
|
||||||
|
data = {}
|
||||||
|
for field in ("id", "title", "authors", "publisher", "size", "tags", "cover", "date"):
|
||||||
|
data[field] = row[field]
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def search(self, query): pass
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
lbm = LibraryDatabase("/home/kovid/library.sqlite")
|
||||||
|
# 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")
|
||||||
|
# lbm.add_book("/home/kovid/documents/ebooks/hobblive01.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/hobbtawny03.lrf")
|
||||||
|
print lbm.get_table(["id","title"])
|
1581
libprs500/gui/images.py
Normal file
1581
libprs500/gui/images.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -14,15 +14,24 @@
|
|||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
from libprs500.communicate import PRS500Device as device
|
from libprs500.communicate import PRS500Device as device
|
||||||
from libprs500.errors import *
|
from libprs500.errors import *
|
||||||
|
from libprs500.lrf.meta import LRFMetaFile, LRFException
|
||||||
|
from database import LibraryDatabase
|
||||||
import images
|
import images
|
||||||
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
|
QAbstractItemView, QImage, QPixmap, QIcon, QSize, QMessageBox, QSettings, QFileDialog, QErrorMessage
|
||||||
from PyQt4 import uic
|
from PyQt4 import uic
|
||||||
import sys, pkg_resources, re, string
|
import sys, pkg_resources, re, string, time, os, os.path, traceback, textwrap, zlib
|
||||||
|
from stat import ST_SIZE
|
||||||
|
from tempfile import TemporaryFile, NamedTemporaryFile
|
||||||
|
from exceptions import Exception as Exception
|
||||||
|
import xml.dom.minidom as dom
|
||||||
|
from xml.dom.ext import PrettyPrint as PrettyPrint
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
NONE = QVariant()
|
NONE = QVariant()
|
||||||
|
TIME_WRITE_FMT = "%d %b %Y"
|
||||||
|
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 readle form """
|
||||||
@ -34,12 +43,122 @@ def human_readable(size):
|
|||||||
if size.find(".") > -1: size = size[:size.find(".")+2]
|
if size.find(".") > -1: size = size[:size.find(".")+2]
|
||||||
return size + " " + suffix
|
return size + " " + suffix
|
||||||
|
|
||||||
class DeviceBooksModel(QAbstractTableModel):
|
|
||||||
|
def wrap(s, width=20):
|
||||||
|
return textwrap.fill(str(s), width)
|
||||||
|
|
||||||
|
class LibraryBooksModel(QAbstractTableModel):
|
||||||
|
FIELDS = ["id", "title", "authors", "size", "date", "publisher", "tags"]
|
||||||
|
TIME_READ_FMT = "%Y-%m-%d %H:%M:%S"
|
||||||
|
def __init__(self, parent, db_path):
|
||||||
|
QAbstractTableModel.__init__(self, parent)
|
||||||
|
self.db = LibraryDatabase(db_path)
|
||||||
|
self._data = self.db.get_table(self.FIELDS)
|
||||||
|
self._orig_data = self._data
|
||||||
|
self.image_file = None
|
||||||
|
self.sort(0, Qt.DescendingOrder)
|
||||||
|
|
||||||
|
def rowCount(self, parent): return len(self._data)
|
||||||
|
def columnCount(self, parent): return len(self.FIELDS)-2
|
||||||
|
|
||||||
|
def set_data(self, db):
|
||||||
|
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
||||||
|
self.db = db
|
||||||
|
self._data = self.db.get_table(self.FIELDS)
|
||||||
|
self._orig_data = self._data
|
||||||
|
self.sort(0, Qt.DescendingOrder)
|
||||||
|
|
||||||
|
def headerData(self, section, orientation, role):
|
||||||
|
if role != Qt.DisplayRole:
|
||||||
|
return NONE
|
||||||
|
text = ""
|
||||||
|
if orientation == Qt.Horizontal:
|
||||||
|
if section == 0: text = "Title"
|
||||||
|
elif section == 1: text = "Author(s)"
|
||||||
|
elif section == 2: text = "Size"
|
||||||
|
elif section == 3: text = "Date"
|
||||||
|
elif section == 4: text = "Publisher"
|
||||||
|
return QVariant(self.trUtf8(text))
|
||||||
|
else: return QVariant(str(1+section))
|
||||||
|
|
||||||
|
def info(self, row):
|
||||||
|
row = self._data[row]
|
||||||
|
cover = self.db.get_cover(row["id"])
|
||||||
|
exts = ",".join(self.db.get_extensions(row["id"]))
|
||||||
|
if not cover:
|
||||||
|
cover = QPixmap(":/images/book.png")
|
||||||
|
self.image_file = None
|
||||||
|
else:
|
||||||
|
pix = QPixmap()
|
||||||
|
self.image_file = NamedTemporaryFile()
|
||||||
|
self.image_file.write(cover)
|
||||||
|
self.image_file.flush()
|
||||||
|
pix.loadFromData(cover, "", Qt.AutoColor)
|
||||||
|
cover = pix.scaledToHeight(COVER_HEIGHT, Qt.SmoothTransformation)
|
||||||
|
return row["title"], row["authors"], human_readable(int(row["size"])), exts, cover
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role == Qt.DisplayRole:
|
||||||
|
row, col = index.row(), index.column()
|
||||||
|
text = None
|
||||||
|
row = self._data[row]
|
||||||
|
if col == 0: text = wrap(row["title"], width=25)
|
||||||
|
elif col == 1:
|
||||||
|
au = row["authors"]
|
||||||
|
if au : text = wrap(re.sub("&", "\n", au), width=25)
|
||||||
|
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 == 4:
|
||||||
|
pub = row["publisher"]
|
||||||
|
if pub: text = wrap(pub, 20)
|
||||||
|
if not text: text = "Unknown"
|
||||||
|
return QVariant(text)
|
||||||
|
elif role == Qt.TextAlignmentRole and index.column() in [2,3,4]:
|
||||||
|
return QVariant(Qt.AlignRight)
|
||||||
|
return NONE
|
||||||
|
|
||||||
|
def sort(self, col, order):
|
||||||
|
descending = order != Qt.AscendingOrder
|
||||||
|
def getter(key, func): return lambda x : func(itemgetter(key)(x))
|
||||||
|
if col == 0: key, func = "title", string.lower
|
||||||
|
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 == 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 ""
|
||||||
|
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
||||||
|
self._data.sort(key=getter(key, func))
|
||||||
|
if descending: self._data.reverse()
|
||||||
|
self.emit(SIGNAL("layoutChanged()"))
|
||||||
|
self.emit(SIGNAL("sorted()"))
|
||||||
|
|
||||||
|
def search(self, query):
|
||||||
|
def query_in(book, q):
|
||||||
|
au = book["authors"]
|
||||||
|
if not au : au = "unknown"
|
||||||
|
pub = book["publisher"]
|
||||||
|
if not pub : pub = "unknown"
|
||||||
|
return q in book["title"].lower() or q in au.lower() or q in pub.lower()
|
||||||
|
queries = unicode(query, 'utf-8').lower().split()
|
||||||
|
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
||||||
|
self._data = []
|
||||||
|
for book in self._orig_data:
|
||||||
|
match = True
|
||||||
|
for q in queries:
|
||||||
|
if query_in(book, q) : continue
|
||||||
|
else:
|
||||||
|
match = False
|
||||||
|
break
|
||||||
|
if match: self._data.append(book)
|
||||||
|
self.emit(SIGNAL("layoutChanged()"))
|
||||||
|
self.emit(SIGNAL("searched()"))
|
||||||
|
|
||||||
|
class DeviceBooksModel(QAbstractTableModel):
|
||||||
|
TIME_READ_FMT = "%a, %d %b %Y %H:%M:%S %Z"
|
||||||
def __init__(self, parent, data):
|
def __init__(self, parent, data):
|
||||||
QAbstractTableModel.__init__(self, parent)
|
QAbstractTableModel.__init__(self, parent)
|
||||||
self._data = data
|
self._data = data
|
||||||
self._orig_data = data
|
self._orig_data = data
|
||||||
|
self.image_file = None
|
||||||
|
|
||||||
def set_data(self, data):
|
def set_data(self, data):
|
||||||
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
||||||
@ -48,7 +167,7 @@ class DeviceBooksModel(QAbstractTableModel):
|
|||||||
self.sort(0, Qt.DescendingOrder)
|
self.sort(0, Qt.DescendingOrder)
|
||||||
|
|
||||||
def rowCount(self, parent): return len(self._data)
|
def rowCount(self, parent): return len(self._data)
|
||||||
def columnCount(self, parent): return 3
|
def columnCount(self, parent): return 4
|
||||||
|
|
||||||
def headerData(self, section, orientation, role):
|
def headerData(self, section, orientation, role):
|
||||||
if role != Qt.DisplayRole:
|
if role != Qt.DisplayRole:
|
||||||
@ -58,6 +177,7 @@ class DeviceBooksModel(QAbstractTableModel):
|
|||||||
if section == 0: text = "Title"
|
if section == 0: text = "Title"
|
||||||
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"
|
||||||
return QVariant(self.trUtf8(text))
|
return QVariant(self.trUtf8(text))
|
||||||
else: return QVariant(str(1+section))
|
else: return QVariant(str(1+section))
|
||||||
|
|
||||||
@ -68,24 +188,33 @@ class DeviceBooksModel(QAbstractTableModel):
|
|||||||
if col == 0: text = book["title"]
|
if col == 0: text = book["title"]
|
||||||
elif col == 1: text = book["author"]
|
elif col == 1: text = book["author"]
|
||||||
elif col == 2: text = human_readable(int(book["size"]))
|
elif col == 2: text = human_readable(int(book["size"]))
|
||||||
|
elif col == 3: text = time.strftime(TIME_WRITE_FMT, time.strptime(book["date"], self.TIME_READ_FMT))
|
||||||
return QVariant(text)
|
return QVariant(text)
|
||||||
elif role == Qt.TextAlignmentRole and index.column() == 2:
|
elif role == Qt.TextAlignmentRole and index.column() in [2,3]:
|
||||||
return QVariant(Qt.AlignRight)
|
return QVariant(Qt.AlignRight)
|
||||||
elif role == Qt.DecorationRole and index.column() == 0:
|
|
||||||
book = self._data[index.row()]
|
|
||||||
if book.has_key("thumbnail"):
|
|
||||||
return QVariant(book["thumbnail"])
|
|
||||||
return NONE
|
return NONE
|
||||||
|
|
||||||
def info(self, row):
|
def info(self, row):
|
||||||
row = self._data[row]
|
row = self._data[row]
|
||||||
return row["title"], row["author"], human_readable(int(row["size"])), row["mime"], row["thumbnail"].pixmap(60, 80, QIcon.Normal, QIcon.On)
|
try:
|
||||||
|
cover = row["thumbnail"]
|
||||||
|
pix = QPixmap()
|
||||||
|
self.image_file = NamedTemporaryFile()
|
||||||
|
self.image_file.write(cover)
|
||||||
|
self.image_file.flush()
|
||||||
|
pix.loadFromData(cover, "", Qt.AutoColor)
|
||||||
|
cover = pix.scaledToHeight(COVER_HEIGHT, Qt.SmoothTransformation)
|
||||||
|
except Exception, e:
|
||||||
|
self.image_file = None
|
||||||
|
cover = QPixmap(":/images/book.png")
|
||||||
|
return row["title"], row["author"], human_readable(int(row["size"])), row["mime"], cover
|
||||||
|
|
||||||
def sort(self, col, order):
|
def sort(self, col, order):
|
||||||
def getter(key, func): return lambda x : func(itemgetter(key)(x))
|
def getter(key, func): return lambda x : func(itemgetter(key)(x))
|
||||||
if col == 0: key, func = "title", string.lower
|
if col == 0: key, func = "title", string.lower
|
||||||
if col == 1: key, func = "author", lambda x : x.split()[-1:][0].lower()
|
if col == 1: key, func = "author", lambda x : x.split()[-1:][0].lower()
|
||||||
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))
|
||||||
descending = order != Qt.AscendingOrder
|
descending = order != Qt.AscendingOrder
|
||||||
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
||||||
self._data.sort(key=getter(key, func))
|
self._data.sort(key=getter(key, func))
|
||||||
@ -94,7 +223,7 @@ class DeviceBooksModel(QAbstractTableModel):
|
|||||||
self.emit(SIGNAL("sorted()"))
|
self.emit(SIGNAL("sorted()"))
|
||||||
|
|
||||||
def search(self, query):
|
def search(self, query):
|
||||||
queries = unicode(query).lower().split()
|
queries = unicode(query, 'utf-8').lower().split()
|
||||||
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
||||||
self._data = []
|
self._data = []
|
||||||
for book in self._orig_data:
|
for book in self._orig_data:
|
||||||
@ -108,64 +237,198 @@ class DeviceBooksModel(QAbstractTableModel):
|
|||||||
self.emit(SIGNAL("layoutChanged()"))
|
self.emit(SIGNAL("layoutChanged()"))
|
||||||
self.emit(SIGNAL("searched()"))
|
self.emit(SIGNAL("searched()"))
|
||||||
|
|
||||||
|
def delete_by_path(self, path):
|
||||||
|
self.emit(SIGNAL("layoutAboutToBeChanged()"))
|
||||||
|
index = -1
|
||||||
|
for book in self._data:
|
||||||
|
if path in book["path"]:
|
||||||
|
self._data.remove(book)
|
||||||
|
break
|
||||||
|
for book in self._orig_data:
|
||||||
|
if path in book["path"]:
|
||||||
|
self._orig_data.remove(book)
|
||||||
|
break
|
||||||
|
self.emit(SIGNAL("layoutChanged()"))
|
||||||
|
self.emit(SIGNAL("deleted()"))
|
||||||
|
|
||||||
|
def path(self, index): return self._data[index.row()]["path"]
|
||||||
|
def title(self, index): return self._data[index.row()]["title"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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 tree_clicked(self, index):
|
def tree_clicked(self, index):
|
||||||
|
def show_device(yes):
|
||||||
|
if yes: self.device_view.show(), self.library_view.hide()
|
||||||
|
else: self.device_view.hide(), self.library_view.show()
|
||||||
item = self.tree.itemFromIndex(index)
|
item = self.tree.itemFromIndex(index)
|
||||||
text = str(item.text())
|
text = str(item.text())
|
||||||
if text == "Library":
|
if text == "Library":
|
||||||
print "Library Clicked"
|
show_device(False)
|
||||||
elif text == "SONY Reader":
|
elif text == "SONY Reader":
|
||||||
self.set_data(self.main_books + self.card_books)
|
show_device(True)
|
||||||
|
self.set_device_data(self.main_books + self.card_books)
|
||||||
elif text == "Main Memory":
|
elif text == "Main Memory":
|
||||||
self.set_data(self.main_books)
|
show_device(True)
|
||||||
|
self.set_device_data(self.main_books)
|
||||||
elif text == "Storage Card":
|
elif text == "Storage Card":
|
||||||
self.set_data(self.card_books)
|
show_device(True)
|
||||||
|
self.set_device_data(self.card_books)
|
||||||
elif text == "Books":
|
elif text == "Books":
|
||||||
text = str(item.parent().text())
|
text = str(item.parent().text())
|
||||||
if text == "Library":
|
if text == "Library":
|
||||||
print "Library --> Books Clicked"
|
show_device(False)
|
||||||
elif text == "Main Memory":
|
elif text == "Main Memory":
|
||||||
self.set_data(self.main_books)
|
show_device(True)
|
||||||
|
self.set_device_data(self.main_books)
|
||||||
elif text == "Storage Card":
|
elif text == "Storage Card":
|
||||||
|
show_device(True)
|
||||||
self.set_data(self.card_books)
|
self.set_data(self.card_books)
|
||||||
|
|
||||||
def set_data(self, data):
|
def set_device_data(self, data):
|
||||||
self.model.set_data(data)
|
self.device_model.set_data(data)
|
||||||
self.table_view.resizeColumnsToContents()
|
self.device_view.resizeColumnsToContents()
|
||||||
|
|
||||||
|
|
||||||
def data_sorted(self):
|
def model_modified(self):
|
||||||
self.table_view.resizeRowsToContents()
|
self.device_view.clearSelection()
|
||||||
self.table_view.clearSelection()
|
self.library_view.clearSelection()
|
||||||
self.book_cover.hide()
|
self.book_cover.hide()
|
||||||
self.book_info.hide()
|
self.book_info.hide()
|
||||||
|
QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
|
||||||
|
|
||||||
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))
|
||||||
self.book_cover.setPixmap(thumbnail)
|
self.book_cover.setPixmap(thumbnail)
|
||||||
|
try:
|
||||||
|
name = os.path.abspath(current.model().image_file.name)
|
||||||
|
self.book_cover.setToolTip('<img src="'+name+'">')
|
||||||
|
except Exception, e: self.book_cover.setToolTip('<img src=":/images/book.png">')
|
||||||
self.book_cover.show()
|
self.book_cover.show()
|
||||||
self.book_info.show()
|
self.book_info.show()
|
||||||
|
|
||||||
def searched(self):
|
|
||||||
self.table_view.clearSelection()
|
|
||||||
self.book_cover.hide()
|
|
||||||
self.book_info.hide()
|
|
||||||
self.table_view.resizeRowsToContents()
|
|
||||||
|
|
||||||
def clear(self, checked): self.search.setText("")
|
def clear(self, checked): self.search.setText("")
|
||||||
|
|
||||||
|
def list_context_event(self, event):
|
||||||
|
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):
|
||||||
|
self.window.setCursor(Qt.WaitCursor)
|
||||||
|
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 ]
|
||||||
|
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)
|
||||||
|
if ret == QMessageBox.YesToAll:
|
||||||
|
self.do_delete(rows)
|
||||||
|
self.window.setCursor(Qt.ArrowCursor)
|
||||||
|
|
||||||
|
def read_settings(self):
|
||||||
|
settings = QSettings()
|
||||||
|
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()
|
||||||
|
|
||||||
|
def write_settings(self):
|
||||||
|
settings = QSettings()
|
||||||
|
settings.beginGroup("MainWindow")
|
||||||
|
settings.setValue("size", QVariant(self.window.size()))
|
||||||
|
settings.endGroup()
|
||||||
|
|
||||||
|
def close_event(self, e):
|
||||||
|
self.write_settings()
|
||||||
|
e.accept()
|
||||||
|
|
||||||
|
def add(self, action):
|
||||||
|
settings = QSettings()
|
||||||
|
dir = settings.value("add books dialog dir", QVariant(os.path.expanduser("~"))).toString()
|
||||||
|
files = QFileDialog.getOpenFileNames(self.window, "Choose books to add to library", dir, "Books (*.lrf *.lrx *.rtf *.pdf *.txt);;All files (*)")
|
||||||
|
if not files.isEmpty():
|
||||||
|
x = str(files[0])
|
||||||
|
settings.setValue("add books dialog dir", QVariant(os.path.dirname(x)))
|
||||||
|
files = str(files.join("|||")).split("|||")
|
||||||
|
for file in files:
|
||||||
|
file = os.path.abspath(file)
|
||||||
|
title, author, cover, publisher = None, None, None, None
|
||||||
|
if ext == "lrf":
|
||||||
|
try:
|
||||||
|
lrf = LRFMetaFile(open(file, "r+b"))
|
||||||
|
title = lrf.title
|
||||||
|
author = lrf.author
|
||||||
|
publisher = lrf.publisher
|
||||||
|
cover = lrf.thumbnail
|
||||||
|
if "unknown" in author.lower(): author = None
|
||||||
|
except IOError, e:
|
||||||
|
self.show_error(e, "Unable to access <b>"+file+"</b>")
|
||||||
|
return
|
||||||
|
except LRFException: pass
|
||||||
|
self.library_model.add(file, title, author, publisher, cover)
|
||||||
|
|
||||||
|
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):
|
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)
|
||||||
self.card = None
|
self.card = None
|
||||||
# Create Tree
|
self.window = window
|
||||||
|
window.closeEvent = self.close_event
|
||||||
|
self.read_settings()
|
||||||
|
|
||||||
|
# Setup Library Book list
|
||||||
|
self.library_model = LibraryBooksModel(window, str(self.database_path))
|
||||||
|
self.library_view.setModel(self.library_model)
|
||||||
|
self.library_view.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
self.library_view.setSortingEnabled(True)
|
||||||
|
self.library_view.contextMenuEvent = self.list_context_event
|
||||||
|
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.search, SIGNAL("textChanged(QString)"), self.library_model.search)
|
||||||
|
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("deleted()"), self.model_modified)
|
||||||
|
self.library_view.resizeColumnsToContents()
|
||||||
|
|
||||||
|
|
||||||
|
# Create Device list
|
||||||
self.tree = QStandardItemModel()
|
self.tree = QStandardItemModel()
|
||||||
library = QStandardItem(QString("Library"))
|
library = QStandardItem(QString("Library"))
|
||||||
library.setIcon(QIcon(":/images/mycomputer.png"))
|
library.setIcon(QIcon(":/images/mycomputer.png"))
|
||||||
@ -187,8 +450,6 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
self.reader.setIcon(QIcon(":/images/reader.png"))
|
self.reader.setIcon(QIcon(":/images/reader.png"))
|
||||||
self.tree.appendRow(self.reader)
|
self.tree.appendRow(self.reader)
|
||||||
self.reader.setFont(font)
|
self.reader.setFont(font)
|
||||||
|
|
||||||
|
|
||||||
self.treeView.setModel(self.tree)
|
self.treeView.setModel(self.tree)
|
||||||
self.treeView.header().hide()
|
self.treeView.header().hide()
|
||||||
self.treeView.setExpanded(library.index(), True)
|
self.treeView.setExpanded(library.index(), True)
|
||||||
@ -199,17 +460,21 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
QObject.connect(self.treeView, SIGNAL("activated(QModelIndex)"), self.tree_clicked)
|
QObject.connect(self.treeView, SIGNAL("activated(QModelIndex)"), self.tree_clicked)
|
||||||
QObject.connect(self.treeView, SIGNAL("clicked(QModelIndex)"), self.tree_clicked)
|
QObject.connect(self.treeView, SIGNAL("clicked(QModelIndex)"), self.tree_clicked)
|
||||||
|
|
||||||
# Create Table
|
# Create Device Book list
|
||||||
self.model = DeviceBooksModel(window, [])
|
self.device_model = DeviceBooksModel(window, [])
|
||||||
QObject.connect(self.model, SIGNAL("sorted()"), self.data_sorted)
|
self.device_view.setModel(self.device_model)
|
||||||
self.table_view.setModel(self.model)
|
self.device_view.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows)
|
self.device_view.setSortingEnabled(True)
|
||||||
self.table_view.setSortingEnabled(True)
|
self.device_view.contextMenuEvent = self.list_context_event
|
||||||
QObject.connect(self.table_view.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.show_book)
|
QObject.connect(self.device_model, SIGNAL("layoutChanged()"), self.device_view.resizeRowsToContents)
|
||||||
QObject.connect(self.search, SIGNAL("textChanged(QString)"), self.model.search)
|
QObject.connect(self.device_view.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.show_book)
|
||||||
QObject.connect(self.model, SIGNAL("searched()"), self.searched)
|
QObject.connect(self.search, SIGNAL("textChanged(QString)"), self.device_model.search)
|
||||||
|
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("deleted()"), self.model_modified)
|
||||||
self.clearButton.setIcon(QIcon(":/images/clear.png"))
|
self.clearButton.setIcon(QIcon(":/images/clear.png"))
|
||||||
QObject.connect(self.clearButton, SIGNAL("clicked(bool)"), self.clear)
|
QObject.connect(self.clearButton, SIGNAL("clicked(bool)"), self.clear)
|
||||||
|
self.device_view.hide()
|
||||||
|
|
||||||
# Setup book display
|
# Setup book display
|
||||||
self.BOOK_TEMPLATE = self.book_info.text()
|
self.BOOK_TEMPLATE = self.book_info.text()
|
||||||
@ -217,11 +482,19 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
self.book_cover.hide()
|
self.book_cover.hide()
|
||||||
self.book_info.hide()
|
self.book_info.hide()
|
||||||
|
|
||||||
|
# Populate toolbar
|
||||||
|
self.add_action = self.tool_bar.addAction(QIcon(":/images/fileopen.png"), "Add files to Library")
|
||||||
|
self.add_action.setShortcut(Qt.Key_A)
|
||||||
|
QObject.connect(self.add_action, SIGNAL("triggered(bool)"), self.add)
|
||||||
|
self.del_action = self.tool_bar.addAction(QIcon(":/images/delete.png"), "Delete selected items")
|
||||||
|
self.del_action.setShortcut(Qt.Key_Delete)
|
||||||
|
QObject.connect(self.del_action, SIGNAL("triggered(bool)"), self.delete)
|
||||||
|
|
||||||
|
|
||||||
self.device_detector = self.startTimer(1000)
|
self.device_detector = self.startTimer(1000)
|
||||||
self.splitter.setStretchFactor(0,0)
|
self.splitter.setStretchFactor(0,0)
|
||||||
self.splitter.setStretchFactor(1,100)
|
self.splitter.setStretchFactor(1,100)
|
||||||
self.search.setFocus(Qt.OtherFocusReason)
|
self.search.setFocus(Qt.OtherFocusReason)
|
||||||
self.window = window
|
|
||||||
window.show()
|
window.show()
|
||||||
|
|
||||||
def timerEvent(self, e):
|
def timerEvent(self, e):
|
||||||
@ -238,9 +511,10 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
self.df.setText("Main memory: <br><br>Storage card:")
|
self.df.setText("Main memory: <br><br>Storage card:")
|
||||||
self.card = None
|
self.card = None
|
||||||
self.treeView.setRowHidden(2, self.tree.invisibleRootItem().index(), True)
|
self.treeView.setRowHidden(2, self.tree.invisibleRootItem().index(), True)
|
||||||
self.model.set_data([])
|
self.device_model.set_data([])
|
||||||
self.book_cover.hide()
|
self.book_cover.hide()
|
||||||
self.book_info.hide()
|
self.book_info.hide()
|
||||||
|
self.device_view.hide()
|
||||||
|
|
||||||
|
|
||||||
def timeout_error(self):
|
def timeout_error(self):
|
||||||
@ -260,6 +534,7 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
|
QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
|
||||||
|
|
||||||
def establish_connection(self):
|
def establish_connection(self):
|
||||||
|
self.window.setCursor(Qt.WaitCursor)
|
||||||
self.status("Connecting to device")
|
self.status("Connecting to device")
|
||||||
try:
|
try:
|
||||||
space = self.dev.available_space()
|
space = self.dev.available_space()
|
||||||
@ -275,20 +550,17 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
sc = space[1][1] if space[1][1] else space[2][1]
|
sc = space[1][1] if space[1][1] else space[2][1]
|
||||||
self.df.setText("Main memory: " + human_readable(space[0][1]) + "<br><br>Storage card: " + human_readable(sc))
|
self.df.setText("Main memory: " + human_readable(space[0][1]) + "<br><br>Storage card: " + human_readable(sc))
|
||||||
self.is_connected = True
|
self.is_connected = True
|
||||||
if space[1][2] > 0: self.card = "a:/"
|
if space[1][2] > 0: self.card = "a:"
|
||||||
elif space[2][2] > 0: self.card = "b:/"
|
elif space[2][2] > 0: self.card = "b:"
|
||||||
else: self.card = None
|
else: self.card = None
|
||||||
if self.card: self.treeView.setRowHidden(1, self.reader.index(), False)
|
if self.card: self.treeView.setRowHidden(1, self.reader.index(), False)
|
||||||
else: self.treeView.setRowHidden(1, self.reader.index(), True)
|
else: self.treeView.setRowHidden(1, self.reader.index(), True)
|
||||||
self.treeView.setRowHidden(2, self.tree.invisibleRootItem().index(), False)
|
self.treeView.setRowHidden(2, self.tree.invisibleRootItem().index(), False)
|
||||||
self.status("Loading media list")
|
self.status("Loading media list from device")
|
||||||
mb, cb, mx, cx = self.dev.books()
|
mb, cb, mx, cx = self.dev.books()
|
||||||
|
|
||||||
for x in (mb, cb):
|
for x in (mb, cb):
|
||||||
for book in x:
|
for book in x:
|
||||||
if book.has_key("thumbnail"):
|
|
||||||
book["thumbnail"] = QIcon(QPixmap.fromImage(QImage.fromData(book["thumbnail"])))
|
|
||||||
else: book["thumbnail"] = QIcon(self.BOOK_IMAGE)
|
|
||||||
if "&" in book["author"]:
|
if "&" in book["author"]:
|
||||||
book["author"] = re.sub(r"&\s*", r"\n", book["author"])
|
book["author"] = re.sub(r"&\s*", r"\n", book["author"])
|
||||||
|
|
||||||
@ -296,11 +568,15 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
self.card_books = cb
|
self.card_books = cb
|
||||||
self.main_xml = mx
|
self.main_xml = mx
|
||||||
self.cache_xml = cx
|
self.cache_xml = cx
|
||||||
|
self.window.setCursor(Qt.ArrowCursor)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
from PyQt4.Qt import QApplication, QMainWindow
|
from PyQt4.Qt import QApplication, QMainWindow
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
window = QMainWindow()
|
window = QMainWindow()
|
||||||
gui = MainWindow(window)
|
QCoreApplication.setOrganizationName("KovidsBrain")
|
||||||
return app.exec_()
|
QCoreApplication.setApplicationName("prs500-gui")
|
||||||
|
gui = MainWindow(window)
|
||||||
|
ret = app.exec_()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>871</width>
|
<width>878</width>
|
||||||
<height>631</height>
|
<height>759</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy" >
|
<property name="sizePolicy" >
|
||||||
@ -22,14 +22,24 @@
|
|||||||
<string>SONY Reader</string>
|
<string>SONY Reader</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget" >
|
<widget class="QWidget" name="centralwidget" >
|
||||||
<layout class="QVBoxLayout" >
|
<layout class="QGridLayout" >
|
||||||
<property name="margin" >
|
<property name="margin" >
|
||||||
<number>9</number>
|
<number>9</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="spacing" >
|
<property name="spacing" >
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item row="1" column="0" >
|
||||||
|
<widget class="QProgressBar" name="progress_bar" >
|
||||||
|
<property name="value" >
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" >
|
||||||
<widget class="QSplitter" name="splitter" >
|
<widget class="QSplitter" name="splitter" >
|
||||||
<property name="orientation" >
|
<property name="orientation" >
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
@ -162,7 +172,25 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTableView" name="table_view" >
|
<widget class="QTableView" name="device_view" >
|
||||||
|
<property name="sizePolicy" >
|
||||||
|
<sizepolicy>
|
||||||
|
<hsizetype>7</hsizetype>
|
||||||
|
<vsizetype>7</vsizetype>
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="alternatingRowColors" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="showGrid" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="library_view" >
|
||||||
<property name="sizePolicy" >
|
<property name="sizePolicy" >
|
||||||
<sizepolicy>
|
<sizepolicy>
|
||||||
<hsizetype>7</hsizetype>
|
<hsizetype>7</hsizetype>
|
||||||
@ -220,22 +248,21 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QProgressBar" name="progress_bar" >
|
|
||||||
<property name="value" >
|
|
||||||
<number>100</number>
|
|
||||||
</property>
|
|
||||||
<property name="orientation" >
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QToolBar" name="toolBar" >
|
<widget class="QToolBar" name="tool_bar" >
|
||||||
|
<property name="movable" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<property name="orientation" >
|
<property name="orientation" >
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="iconSize" >
|
||||||
|
<size>
|
||||||
|
<width>22</width>
|
||||||
|
<height>22</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<attribute name="toolBarArea" >
|
<attribute name="toolBarArea" >
|
||||||
<number>4</number>
|
<number>4</number>
|
||||||
</attribute>
|
</attribute>
|
||||||
|
@ -4,5 +4,7 @@
|
|||||||
<file>images/mycomputer.png</file>
|
<file>images/mycomputer.png</file>
|
||||||
<file>images/reader.png</file>
|
<file>images/reader.png</file>
|
||||||
<file>images/clear.png</file>
|
<file>images/clear.png</file>
|
||||||
|
<file>images/delete.png</file>
|
||||||
|
<file>images/fileopen.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
BIN
libprs500/gui/resources/images/delete.png
Normal file
BIN
libprs500/gui/resources/images/delete.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
BIN
libprs500/gui/resources/images/fileopen.png
Normal file
BIN
libprs500/gui/resources/images/fileopen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
@ -305,12 +305,13 @@ def main():
|
|||||||
import sys, os.path
|
import sys, os.path
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
from libprs500 import __version__ as VERSION
|
from libprs500 import __version__ as VERSION
|
||||||
parser = OptionParser(usage="usage: %prog [options] mybook.lrf", version=VERSION)
|
parser = OptionParser(usage="usage: %prog [options] mybook.lrf\n\nWARNING: Based on reverse engineering the LRF format. Making changes may render your LRF file unreadable. ", version=VERSION)
|
||||||
parser.add_option("-t", "--title", action="store", type="string", dest="title", help="Set the book title")
|
parser.add_option("-t", "--title", action="store", type="string", dest="title", help="Set the book title")
|
||||||
parser.add_option("-a", "--author", action="store", type="string", dest="author", help="Set the author")
|
parser.add_option("-a", "--author", action="store", type="string", dest="author", help="Set the author")
|
||||||
parser.add_option("-c", "--category", action="store", type="string", dest="category", help="The category this book belongs to. E.g.: History")
|
parser.add_option("-c", "--category", action="store", type="string", dest="category", help="The category this book belongs to. E.g.: History")
|
||||||
parser.add_option("--thumbnail", action="store", type="string", dest="thumbnail", help="Path to a graphic that will be set as this files' thumbnail")
|
parser.add_option("--thumbnail", action="store", type="string", dest="thumbnail", help="Path to a graphic that will be set as this files' thumbnail")
|
||||||
parser.add_option("-p", "--page", action="store", type="string", dest="page", help="Set the current page number (I think)")
|
parser.add_option("--get-thumbnail", action="store_true", dest="get_thumbnail", default=False, help="Extract thumbnail from LRF file")
|
||||||
|
parser.add_option("-p", "--page", action="store", type="string", dest="page", help="Don't know what this is for")
|
||||||
options, args = parser.parse_args()
|
options, args = parser.parse_args()
|
||||||
if len(args) != 1:
|
if len(args) != 1:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
@ -325,20 +326,17 @@ def main():
|
|||||||
lrf.thumbnail = f.read()
|
lrf.thumbnail = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
t = lrf.thumbnail
|
if options.get_thumbnail:
|
||||||
td = "None"
|
t = lrf.thumbnail
|
||||||
if t and len(t) > 0:
|
td = "None"
|
||||||
td = os.path.basename(args[0])+"_thumbnail_."+lrf.thumbail_extension()
|
if t and len(t) > 0:
|
||||||
f = open(td, "w")
|
td = os.path.basename(args[0])+"_thumbnail_."+lrf.thumbail_extension()
|
||||||
f.write(t)
|
f = open(td, "w")
|
||||||
f.close()
|
f.write(t)
|
||||||
|
f.close()
|
||||||
|
|
||||||
fields = LRFMetaFile.__dict__.items()
|
fields = LRFMetaFile.__dict__.items()
|
||||||
for f in fields:
|
for f in fields:
|
||||||
if "XML" in str(f):
|
if "XML" in str(f):
|
||||||
print str(f[1]) + ":", lrf.__getattribute__(f[0])
|
print str(f[1]) + ":", lrf.__getattribute__(f[0])
|
||||||
print "Thumbnail:", td
|
if options.get_thumbnail: print "Thumbnail:", td
|
||||||
print "object index offset:", hex(lrf.object_index_offset)
|
|
||||||
print "toc object offset", hex(lrf.toc_object_offset)
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -167,6 +167,7 @@ class field(object):
|
|||||||
obj.pack(val, start=self._start, fmt=self._fmt)
|
obj.pack(val, start=self._start, fmt=self._fmt)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
typ = ""
|
||||||
if self._fmt == DWORD: typ = "unsigned int"
|
if self._fmt == DWORD: typ = "unsigned int"
|
||||||
if self._fmt == DDWORD: typ = "unsigned long long"
|
if self._fmt == DDWORD: typ = "unsigned long long"
|
||||||
return "An " + typ + " stored in " + str(struct.calcsize(self._fmt)) + " bytes starting at byte " + str(self._start)
|
return "An " + typ + " stored in " + str(struct.calcsize(self._fmt)) + " bytes starting at byte " + str(self._start)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user