mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Changed GUI to only show devices. No subsets.
Added a warning about PyQt4 too setup script Added initial code for Dropping on reader (not tested)
This commit is contained in:
parent
8e4010e0f3
commit
876f994a4a
@ -32,7 +32,7 @@ the following rule in C{/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"
|
||||||
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 rules file to suit your distribution.
|
||||||
"""
|
"""
|
||||||
__version__ = "0.2.1"
|
__version__ = "0.3.0"
|
||||||
__docformat__ = "epytext"
|
__docformat__ = "epytext"
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
TEMPORARY_FILENAME_TEMPLATE = "libprs500_"+__version__+"_temp"
|
TEMPORARY_FILENAME_TEMPLATE = "libprs500_"+__version__+"_temp"
|
||||||
|
@ -16,16 +16,24 @@
|
|||||||
from xml.dom.ext import PrettyPrint as PrettyPrint
|
from xml.dom.ext import PrettyPrint as PrettyPrint
|
||||||
import xml.dom.minidom as dom
|
import xml.dom.minidom as dom
|
||||||
from base64 import b64decode as decode
|
from base64 import b64decode as decode
|
||||||
|
from base64 import b64encode as encode
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
MIME_MAP = { "lrf":"application/x-sony-bbeb", "rtf":"application/rtf", "pdf":"application/pdf", "txt":"text/plain" }
|
||||||
|
|
||||||
class book_metadata_field(object):
|
class book_metadata_field(object):
|
||||||
def __init__(self, attr, formatter=None):
|
def __init__(self, attr, formatter=None, setter=None):
|
||||||
self.attr = attr
|
self.attr = attr
|
||||||
self.formatter = formatter
|
self.formatter = formatter
|
||||||
|
|
||||||
def __get__(self, obj, typ=None):
|
def __get__(self, obj, typ=None):
|
||||||
""" Return a string. String may be empty if self.attr is absent """
|
""" Return a string. String may be empty if self.attr is absent """
|
||||||
return self.formatter(obj.elem.getAttribute(self.attr)) if self.formatter else obj.elem.getAttribute(self.attr).strip()
|
return self.formatter(obj.elem.getAttribute(self.attr)) if self.formatter else obj.elem.getAttribute(self.attr).strip()
|
||||||
|
|
||||||
|
def __set__(self, obj, val):
|
||||||
|
val = self.setter(val) if self.setter else val
|
||||||
|
obj.elem.setAttribute(self.attr, str(val))
|
||||||
|
|
||||||
class Book(object):
|
class Book(object):
|
||||||
title = book_metadata_field("title")
|
title = book_metadata_field("title")
|
||||||
author = book_metadata_field("author", formatter=lambda x: x if x.strip() else "Unknown")
|
author = book_metadata_field("author", formatter=lambda x: x if x.strip() else "Unknown")
|
||||||
@ -33,7 +41,7 @@ class Book(object):
|
|||||||
rpath = book_metadata_field("path")
|
rpath = book_metadata_field("path")
|
||||||
id = book_metadata_field("id", formatter=int)
|
id = book_metadata_field("id", formatter=int)
|
||||||
size = book_metadata_field("size", formatter=int)
|
size = book_metadata_field("size", formatter=int)
|
||||||
datetime = book_metadata_field("date", formatter=lambda x: time.strptime(x, "%a, %d %b %Y %H:%M:%S %Z"))
|
datetime = book_metadata_field("date", formatter=lambda x: time.strptime(x, "%a, %d %b %Y %H:%M:%S %Z"), setter=lambda x: time.strftime("%a, %d %b %Y %H:%M:%S %Z", x))
|
||||||
|
|
||||||
@apply
|
@apply
|
||||||
def thumbnail():
|
def thumbnail():
|
||||||
@ -78,4 +86,23 @@ class BookList(list):
|
|||||||
file.seek(0)
|
file.seek(0)
|
||||||
self.document = dom.parse(file)
|
self.document = dom.parse(file)
|
||||||
for book in self.document.getElementsByTagName(self.prefix + "text"): self.append(Book(book, root=root, prefix=prefix))
|
for book in self.document.getElementsByTagName(self.prefix + "text"): self.append(Book(book, root=root, prefix=prefix))
|
||||||
self._file = file
|
|
||||||
|
def add_book(self, info, name):
|
||||||
|
root = self.document.documentElement
|
||||||
|
node = self.document.createElement(self.prefix + "text")
|
||||||
|
mime = MIME_MAP[name[name.rfind(".")+1:]]
|
||||||
|
id = 0
|
||||||
|
for book in self:
|
||||||
|
if book.id > id:
|
||||||
|
id = book.id
|
||||||
|
break
|
||||||
|
attrs = { "title":info["title"], "author":info["authors"], "page":"0", "part":"0", "scale":"0", "sourceid":"1", "id":str(id), "date":"", "mime":mime, "path":name, "size":str(size)}
|
||||||
|
for attr in attrs.keys():
|
||||||
|
node.setAttributeNode(self.document.createAttribute(attr))
|
||||||
|
node.setAttribute(attr, attrs[attr])
|
||||||
|
book = Book(node, root=self.root, prefix=self.prefix)
|
||||||
|
book.datetime = time.gmtime()
|
||||||
|
self.append(book)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -134,6 +134,7 @@ class PRS500Device(object):
|
|||||||
PRS500_BULK_OUT_EP = 0x02 #: Endpoint for Bulk writes
|
PRS500_BULK_OUT_EP = 0x02 #: Endpoint for Bulk writes
|
||||||
MEDIA_XML = "/Data/database/cache/media.xml" #: Location of media.xml file on device
|
MEDIA_XML = "/Data/database/cache/media.xml" #: Location of media.xml file on device
|
||||||
CACHE_XML = "/Sony Reader/database/cache.xml" #: Location of cache.xml on storage card in device
|
CACHE_XML = "/Sony Reader/database/cache.xml" #: Location of cache.xml on storage card in device
|
||||||
|
FORMATS = ["lrf", "rtf", "pdf", "txt"] #: Ordered list of supported formats
|
||||||
|
|
||||||
device_descriptor = DeviceDescriptor(SONY_VENDOR_ID, PRS500_PRODUCT_ID, PRS500_INTERFACE_ID)
|
device_descriptor = DeviceDescriptor(SONY_VENDOR_ID, PRS500_PRODUCT_ID, PRS500_INTERFACE_ID)
|
||||||
|
|
||||||
@ -572,7 +573,14 @@ class PRS500Device(object):
|
|||||||
raise ProtocolError("Failed to delete directory " + path + ". Response code: " + hex(res.code))
|
raise ProtocolError("Failed to delete directory " + path + ". Response code: " + hex(res.code))
|
||||||
|
|
||||||
@safe
|
@safe
|
||||||
def books(self, oncard=False):
|
def card(self, end_session=True):
|
||||||
|
card = None
|
||||||
|
if self._exists("a:/")[0]: card = "a:"
|
||||||
|
if self._exists("b:/")[0]: card = "b:"
|
||||||
|
return card
|
||||||
|
|
||||||
|
@safe
|
||||||
|
def books(self, oncard=False, end_session=True):
|
||||||
"""
|
"""
|
||||||
Return a list of ebooks on the device.
|
Return a list of ebooks on the device.
|
||||||
@param oncard: If True return a list of ebookson the storage card, otherwise return list of ebooks in main memory of device
|
@param oncard: If True return a list of ebookson the storage card, otherwise return list of ebooks in main memory of device
|
||||||
@ -595,3 +603,32 @@ class PRS500Device(object):
|
|||||||
if file.tell() == 0: file = None
|
if file.tell() == 0: file = None
|
||||||
else: self.get_file(self.MEDIA_XML, file, end_session=False)
|
else: self.get_file(self.MEDIA_XML, file, end_session=False)
|
||||||
return BookList(prefix=prefix, root=root, file=file)
|
return BookList(prefix=prefix, root=root, file=file)
|
||||||
|
|
||||||
|
@safe
|
||||||
|
def add_book(self, infile, name, info, booklists, oncard=False, end_session=True):
|
||||||
|
"""
|
||||||
|
Add a book to the device. If oncard is True then the book is copied to the card rather than main memory.
|
||||||
|
|
||||||
|
@param infile: The source file, should be opened in "rb" mode
|
||||||
|
@param name: The name of the book file when uploaded to the device. The extension of name must be one of the supported formats for this device.
|
||||||
|
@param info: A dictionary that must have the keys "title", "authors", "cover". C{info["cover"]} should be the data from a 60x80 image file or None. If it is something else, results are undefined.
|
||||||
|
@param booklists: A tuple containing the result of calls to (L{books}(oncard=False), L{books}(oncard=True)).
|
||||||
|
@todo: Implement syncing the booklists to the device. This would mean juggling with the nextId attribute in media.xml and renumbering ids in cache.xml?
|
||||||
|
"""
|
||||||
|
infile.seek(0,2)
|
||||||
|
size = infile.tell()
|
||||||
|
infile.seek(0)
|
||||||
|
card = self.card(end_session=False)
|
||||||
|
space = self.available_space(end_session=False)
|
||||||
|
mspace = space[0][1]
|
||||||
|
cspace = space[1][1] if space[1][1] >= space[2][1] else space[2][1]
|
||||||
|
if oncard and size > cspace - 1024*1024: raise FreeSpaceError("There is insufficient free space on the storage card")
|
||||||
|
if not oncard and size > mspace - 1024*1024: raise FreeSpaceError("There is insufficient free space in main memory")
|
||||||
|
prefix = "/Data/media/"
|
||||||
|
if oncard: prefix = card + "/"
|
||||||
|
else: name = "books/"+name
|
||||||
|
path = prefix + name
|
||||||
|
self.put_file(infile, path, end_session=False)
|
||||||
|
if oncard: booklists[1].add_book(info, name, size)
|
||||||
|
else: booklists[0].add_book(info, name, size)
|
||||||
|
|
||||||
|
@ -37,6 +37,9 @@ class DeviceError(ProtocolError):
|
|||||||
class PacketError(ProtocolError):
|
class PacketError(ProtocolError):
|
||||||
""" Errors with creating/interpreting packets """
|
""" Errors with creating/interpreting packets """
|
||||||
|
|
||||||
|
class FreeSpaceError(ProtocolError):
|
||||||
|
""" Errors caused when trying to put files onto an overcrowded device """
|
||||||
|
|
||||||
class ArgumentError(ProtocolError):
|
class ArgumentError(ProtocolError):
|
||||||
""" Errors caused by invalid arguments to a public interface function """
|
""" Errors caused by invalid arguments to a public interface function """
|
||||||
|
|
||||||
|
@ -15,11 +15,35 @@
|
|||||||
__docformat__ = "epytext"
|
__docformat__ = "epytext"
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import pkg_resources, sys, os, StringIO
|
import pkg_resources, sys, os, re, StringIO, traceback
|
||||||
from PyQt4 import QtCore, QtGui # Needed for classes imported with import_ui
|
from PyQt4 import QtCore, QtGui # Needed for classes imported with import_ui
|
||||||
from libprs500.gui.widgets import LibraryBooksView, DeviceBooksView, CoverDisplay, DeviceView # Needed for import_ui
|
|
||||||
from PyQt4.uic.Compiler import compiler
|
from PyQt4.uic.Compiler import compiler
|
||||||
|
|
||||||
|
error_dialog = None
|
||||||
|
|
||||||
|
def extension(path):
|
||||||
|
return os.path.splitext(path)[1][1:].lower()
|
||||||
|
|
||||||
|
def installErrorHandler(dialog):
|
||||||
|
global error_dialog
|
||||||
|
error_dialog = dialog
|
||||||
|
error_dialog.resize(600, 400)
|
||||||
|
error_dialog.setWindowTitle("SONY Reader - Error")
|
||||||
|
error_dialog.setModal(True)
|
||||||
|
|
||||||
|
|
||||||
|
def Warning(msg, e):
|
||||||
|
print >> sys.stderr, msg
|
||||||
|
traceback.print_exc(e)
|
||||||
|
|
||||||
|
def Error(msg, e):
|
||||||
|
if error_dialog:
|
||||||
|
if e: msg += "<br>" + traceback.format_exc(e)
|
||||||
|
msg = re.sub("Traceback", "<b>Traceback</b>", msg)
|
||||||
|
msg = re.sub(r"\n", "<br>", msg)
|
||||||
|
error_dialog.showMessage(msg)
|
||||||
|
error_dialog.show()
|
||||||
|
|
||||||
def import_ui(name):
|
def import_ui(name):
|
||||||
uifile = pkg_resources.resource_stream(__name__, name)
|
uifile = pkg_resources.resource_stream(__name__, name)
|
||||||
code_string = StringIO.StringIO()
|
code_string = StringIO.StringIO()
|
||||||
@ -27,3 +51,5 @@ def import_ui(name):
|
|||||||
ui = pkg_resources.resource_filename(__name__, name)
|
ui = pkg_resources.resource_filename(__name__, name)
|
||||||
exec code_string.getvalue()
|
exec code_string.getvalue()
|
||||||
return locals()[winfo["uiclass"]]
|
return locals()[winfo["uiclass"]]
|
||||||
|
|
||||||
|
from libprs500.gui.widgets import LibraryBooksView, DeviceBooksView, CoverDisplay, DeviceView # Needed for import_ui
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
<!DOCTYPE RCC><RCC version="1.0">
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
<qresource>
|
<qresource>
|
||||||
|
<file alias="icon">images/library.png</file>
|
||||||
<file alias="default_cover">images/cherubs.jpg</file>
|
<file alias="default_cover">images/cherubs.jpg</file>
|
||||||
<file alias="library">images/mycomputer.png</file>
|
<file alias="library">images/library.png</file>
|
||||||
<file alias="reader">images/reader.png</file>
|
<file alias="reader">images/reader.png</file>
|
||||||
<file alias="card">images/memory_stick_unmount.png</file>
|
<file alias="card">images/memory_stick_unmount.png</file>
|
||||||
<file>images/clear.png</file>
|
<file>images/clear.png</file>
|
||||||
|
BIN
libprs500/gui/images/library.png
Normal file
BIN
libprs500/gui/images/library.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.5 KiB |
File diff suppressed because it is too large
Load Diff
@ -12,22 +12,24 @@
|
|||||||
## You should have received a copy of the GNU General Public License along
|
## 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.,
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, QSettings, QVariant, QSize, QEventLoop
|
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, QSettings, QVariant, QSize, QEventLoop, QString
|
||||||
from PyQt4.QtGui import QPixmap, QAbstractItemView, QErrorMessage, QMessageBox, QFileDialog
|
from PyQt4.QtGui import QPixmap, QAbstractItemView, QErrorMessage, QMessageBox, QFileDialog, QIcon
|
||||||
from PyQt4.Qt import qInstallMsgHandler, qDebug, qFatal, qWarning, qCritical
|
from PyQt4.Qt import qInstallMsgHandler, qDebug, qFatal, qWarning, qCritical
|
||||||
from PyQt4 import uic
|
from PyQt4 import uic
|
||||||
|
|
||||||
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 libprs500.lrf.meta import LRFMetaFile, LRFException
|
||||||
from libprs500.gui import import_ui
|
from libprs500.gui import import_ui, installErrorHandler, Error, Warning, extension
|
||||||
from libprs500.gui.widgets import LibraryBooksModel, DeviceBooksModel, DeviceModel, human_readable
|
from libprs500.gui.widgets import LibraryBooksModel, DeviceBooksModel, DeviceModel, TableView
|
||||||
from database import LibraryDatabase
|
from database import LibraryDatabase
|
||||||
from editbook import EditBookDialog
|
from editbook import EditBookDialog
|
||||||
|
|
||||||
import sys, re, os, traceback
|
import sys, re, os, traceback
|
||||||
|
|
||||||
DEFAULT_BOOK_COVER = None
|
DEFAULT_BOOK_COVER = None
|
||||||
|
LIBRARY_BOOK_TEMPLATE = QString("<table><tr><td><b>Formats:</b> %1 </td><td><b>Tags:</b> %2</td></tr><tr><td><b>Comments:</b>%3</td></tr></table>")
|
||||||
|
DEVICE_BOOK_TEMPLATE = QString("<table><tr><td><b>Title: </b>%1</td><td><b> Size:</b> %2</td></tr><tr><td><b>Author: </b>%3</td><td><b> Type: </b>%4</td></tr></table>")
|
||||||
|
|
||||||
Ui_MainWindow = import_ui("main.ui")
|
Ui_MainWindow = import_ui("main.ui")
|
||||||
class MainWindow(QObject, Ui_MainWindow):
|
class MainWindow(QObject, Ui_MainWindow):
|
||||||
@ -49,18 +51,19 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
|
|
||||||
def tree_clicked(self, index):
|
def tree_clicked(self, index):
|
||||||
if index.isValid():
|
if index.isValid():
|
||||||
text = (index.data(Qt.DisplayRole).toString())
|
self.search.clear()
|
||||||
if "Books" in text: text = str(index.parent().data(Qt.DisplayRole).toString())
|
show_dev = True
|
||||||
if "Library" in text:
|
model = self.device_tree.model()
|
||||||
self.show_device(False)
|
if model.is_library(index):
|
||||||
elif "SONY Reader" in text:
|
show_dev = False
|
||||||
|
elif model.is_reader(index):
|
||||||
self.device_view.setModel(self.reader_model)
|
self.device_view.setModel(self.reader_model)
|
||||||
QObject.connect(self.device_view.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.show_book)
|
QObject.connect(self.device_view.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.show_book)
|
||||||
self.show_device(True)
|
elif model.is_card(index):
|
||||||
elif "Storage Card" in text:
|
|
||||||
self.device_view.setModel(self.card_model)
|
self.device_view.setModel(self.card_model)
|
||||||
QObject.connect(self.device_view.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.show_book)
|
QObject.connect(self.device_view.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.show_book)
|
||||||
self.show_device(True)
|
self.show_device(show_dev)
|
||||||
|
|
||||||
|
|
||||||
def model_modified(self):
|
def model_modified(self):
|
||||||
if self.library_view.isVisible(): view = self.library_view
|
if self.library_view.isVisible(): view = self.library_view
|
||||||
@ -78,14 +81,18 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
view.resizeColumnToContents(c)
|
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())
|
if self.library_view.isVisible():
|
||||||
self.book_info.setText(self.BOOK_TEMPLATE.arg(title).arg(size).arg(author).arg(mime))
|
formats, tags, comments, cover = current.model().info(current.row())
|
||||||
if not thumbnail: thumbnail = DEFAULT_BOOK_COVER
|
data = LIBRARY_BOOK_TEMPLATE.arg(formats).arg(tags).arg(comments)
|
||||||
self.book_cover.setPixmap(thumbnail)
|
tooltip = "To save the cover, drag it to the desktop.<br>To change the cover drag the new cover onto this picture"
|
||||||
try:
|
else:
|
||||||
name = os.path.abspath(current.model().image_file.name)
|
title, author, size, mime, cover = current.model().info(current.row())
|
||||||
self.book_cover.setToolTip('<img src="'+name+'">')
|
data = DEVICE_BOOK_TEMPLATE.arg(title).arg(size).arg(author).arg(mime)
|
||||||
except Exception, e: self.book_cover.setToolTip('<img src=":/default_cover">')
|
tooltip = "To save the cover, drag it to the desktop."
|
||||||
|
self.book_info.setText(data)
|
||||||
|
self.book_cover.setToolTip(tooltip)
|
||||||
|
if not cover: cover = DEFAULT_BOOK_COVER
|
||||||
|
self.book_cover.setPixmap(cover)
|
||||||
self.book_cover.show()
|
self.book_cover.show()
|
||||||
self.book_info.show()
|
self.book_info.show()
|
||||||
|
|
||||||
@ -95,7 +102,7 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
|
|
||||||
def delete(self, action):
|
def delete(self, action):
|
||||||
count = str(len(self.current_view.selectionModel().selectedRows()))
|
count = str(len(self.current_view.selectionModel().selectedRows()))
|
||||||
ret = QMessageBox.question(self.window, self.trUtf8("SONY Reader - confirm"), self.trUtf8("Are you sure you want to <b>permanently delete</b> these ") +count+self.trUtf8(" items?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
|
ret = QMessageBox.question(self.window, self.trUtf8("SONY Reader - confirm"), self.trUtf8("Are you sure you want to <b>permanently delete</b> these ") +count+self.trUtf8(" item(s)?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
|
||||||
if ret != QMessageBox.Yes: return
|
if ret != QMessageBox.Yes: return
|
||||||
self.window.setCursor(Qt.WaitCursor)
|
self.window.setCursor(Qt.WaitCursor)
|
||||||
if self.library_view.isVisible():
|
if self.library_view.isVisible():
|
||||||
@ -168,14 +175,17 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
self.add_books(files)
|
self.add_books(files)
|
||||||
|
|
||||||
def add_books(self, files):
|
def add_books(self, files):
|
||||||
|
self.window.setCursor(Qt.WaitCursor)
|
||||||
for file in files:
|
for file in files:
|
||||||
file = os.path.abspath(file)
|
file = os.path.abspath(file)
|
||||||
self.library_view.model().add_book(file)
|
self.library_view.model().add_book(file)
|
||||||
self.search.clear()
|
if self.library_view.isVisible(): self.search.clear()
|
||||||
|
else: self.library_model.search("")
|
||||||
hv = self.library_view.horizontalHeader()
|
hv = self.library_view.horizontalHeader()
|
||||||
col = hv.sortIndicatorSection()
|
col = hv.sortIndicatorSection()
|
||||||
order = hv.sortIndicatorOrder()
|
order = hv.sortIndicatorOrder()
|
||||||
self.library_view.model().sort(col, order)
|
self.library_view.model().sort(col, order)
|
||||||
|
self.window.setCursor(Qt.ArrowCursor)
|
||||||
|
|
||||||
|
|
||||||
def edit(self, action):
|
def edit(self, action):
|
||||||
@ -189,15 +199,69 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
self.library_model.refresh_row(row.row())
|
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>"+re.sub("\n","<br>", traceback.format_exc(e)))
|
|
||||||
|
|
||||||
def update_cover(self, pix):
|
def update_cover(self, pix):
|
||||||
if not pix.isNull():
|
if not pix.isNull():
|
||||||
try:
|
try:
|
||||||
self.library_view.model().update_cover(self.library_view.currentIndex(), pix)
|
self.library_view.model().update_cover(self.library_view.currentIndex(), pix)
|
||||||
self.book_cover.setPixmap(pix)
|
self.book_cover.setPixmap(pix)
|
||||||
except Exception, e: self.show_error(e, "Unable to change cover")
|
except Exception, e: Error("Unable to change cover", e)
|
||||||
|
|
||||||
|
def upload_books(self, to, files, ids):
|
||||||
|
def update_models():
|
||||||
|
hv = self.device_view.horizontalHeader()
|
||||||
|
col = hv.sortIndicatorSection()
|
||||||
|
order = hv.sortIndicatorOrder()
|
||||||
|
model = self.card_model if oncard else self.reader_model
|
||||||
|
model.sort(col, order)
|
||||||
|
if self.device_view.isVisible() and self.device_view.model() == model: self.search.clear()
|
||||||
|
else: model.search("")
|
||||||
|
|
||||||
|
self.window.setCursor(Qt.WaitCursor)
|
||||||
|
oncard = False if to == "reader" else True
|
||||||
|
ename = "file"
|
||||||
|
booklists = (self.reader_model._orig_data, self.card_model._orig_data)
|
||||||
|
try:
|
||||||
|
if ids:
|
||||||
|
for id in ids:
|
||||||
|
formats = []
|
||||||
|
info = self.library_view.model().book_info(id, ["title", "authors", "cover"])
|
||||||
|
ename = info["title"]
|
||||||
|
for f in files:
|
||||||
|
if re.match("......_"+str(id)+"_", f):
|
||||||
|
formats.append(f)
|
||||||
|
file = None
|
||||||
|
try:
|
||||||
|
for format in self.dev.FORMATS:
|
||||||
|
for f in formats:
|
||||||
|
if extension(format) == format:
|
||||||
|
file = f
|
||||||
|
raise StopIteration()
|
||||||
|
except StopIteration: pass
|
||||||
|
if not file:
|
||||||
|
Error("The library does not have any compatible formats for " + ename)
|
||||||
|
continue
|
||||||
|
f = open(file, "rb")
|
||||||
|
self.status("Sending "+info["title"]+" to device")
|
||||||
|
try:
|
||||||
|
self.dev.add_book(f, "libprs500_"+str(id)+"."+extension(file), info, booklists, oncard=oncard)
|
||||||
|
update_models()
|
||||||
|
finally: f.close()
|
||||||
|
else:
|
||||||
|
for file in files:
|
||||||
|
ename = file
|
||||||
|
if extension(file) not in self.dev.FORMATS:
|
||||||
|
Error(ename + " is not in a supported format")
|
||||||
|
continue
|
||||||
|
info = { "title":file, "authors":"Unknown", cover:None }
|
||||||
|
f = open(file, "rb")
|
||||||
|
self.status("Sending "+info["title"]+" to device")
|
||||||
|
try:
|
||||||
|
self.dev.add_book(f, os.path.basename(file), info, booklists, oncard=oncard)
|
||||||
|
update_models()
|
||||||
|
finally: f.close()
|
||||||
|
except Exception, e:
|
||||||
|
Error("Unable to send "+ename+" to device", e)
|
||||||
|
finally: self.window.setCursor(Qt.WaitCursor)
|
||||||
|
|
||||||
def __init__(self, window, log_packets):
|
def __init__(self, window, log_packets):
|
||||||
QObject.__init__(self)
|
QObject.__init__(self)
|
||||||
@ -228,15 +292,12 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
self.library_view.resizeColumnsToContents()
|
self.library_view.resizeColumnsToContents()
|
||||||
|
|
||||||
# Create Device tree
|
# Create Device tree
|
||||||
|
model = DeviceModel(self.device_tree)
|
||||||
QObject.connect(self.device_tree, SIGNAL("activated(QModelIndex)"), self.tree_clicked)
|
QObject.connect(self.device_tree, SIGNAL("activated(QModelIndex)"), self.tree_clicked)
|
||||||
QObject.connect(self.device_tree, SIGNAL("clicked(QModelIndex)"), self.tree_clicked)
|
QObject.connect(self.device_tree, SIGNAL("clicked(QModelIndex)"), self.tree_clicked)
|
||||||
model = DeviceModel(self.device_tree)
|
QObject.connect(model, SIGNAL('books_dropped'), self.add_books)
|
||||||
|
QObject.connect(model, SIGNAL('upload_books'), self.upload_books)
|
||||||
self.device_tree.setModel(model)
|
self.device_tree.setModel(model)
|
||||||
self.device_tree.expand(model.indexFromItem(model.library))
|
|
||||||
self.device_tree.expand(model.indexFromItem(model.reader))
|
|
||||||
self.device_tree.expand(model.indexFromItem(model.card))
|
|
||||||
self.device_tree.hide_reader(True)
|
|
||||||
self.device_tree.hide_card(True)
|
|
||||||
|
|
||||||
# Create Device Book list
|
# Create Device Book list
|
||||||
self.reader_model = DeviceBooksModel(window)
|
self.reader_model = DeviceBooksModel(window)
|
||||||
@ -252,8 +313,6 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
QObject.connect(model, SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.resize_columns)
|
QObject.connect(model, SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.resize_columns)
|
||||||
|
|
||||||
# Setup book display
|
# Setup book display
|
||||||
self.BOOK_TEMPLATE = self.book_info.text()
|
|
||||||
self.BOOK_IMAGE = DEFAULT_BOOK_COVER
|
|
||||||
self.book_cover.hide()
|
self.book_cover.hide()
|
||||||
self.book_info.hide()
|
self.book_info.hide()
|
||||||
|
|
||||||
@ -266,9 +325,10 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
QObject.connect(self.book_cover, SIGNAL("cover_received(QPixmap)"), self.update_cover)
|
QObject.connect(self.book_cover, SIGNAL("cover_received(QPixmap)"), self.update_cover)
|
||||||
|
|
||||||
self.device_detector = self.startTimer(1000)
|
self.device_detector = self.startTimer(1000)
|
||||||
self.splitter.setStretchFactor(1,100)
|
|
||||||
self.search.setFocus(Qt.OtherFocusReason)
|
self.search.setFocus(Qt.OtherFocusReason)
|
||||||
self.show_device(False)
|
self.show_device(False)
|
||||||
|
self.df_template = self.df.text()
|
||||||
|
self.df.setText(self.df_template.arg("").arg("").arg(""))
|
||||||
window.show()
|
window.show()
|
||||||
|
|
||||||
def timerEvent(self, e):
|
def timerEvent(self, e):
|
||||||
@ -307,26 +367,27 @@ class MainWindow(QObject, Ui_MainWindow):
|
|||||||
self.window.setCursor(Qt.WaitCursor)
|
self.window.setCursor(Qt.WaitCursor)
|
||||||
self.status("Connecting to device")
|
self.status("Connecting to device")
|
||||||
try:
|
try:
|
||||||
space = self.dev.available_space()
|
info = self.dev.get_device_information(end_session=False)
|
||||||
except DeviceError:
|
except DeviceError:
|
||||||
self.dev.reconnect()
|
self.dev.reconnect()
|
||||||
return
|
return
|
||||||
except ProtocolError, e:
|
except ProtocolError, e:
|
||||||
traceback.print_exc(e)
|
traceback.print_exc(e)
|
||||||
qFatal("Unable to connect to device. Please try unplugging and reconnecting it")
|
qFatal("Unable to connect to device. Please try unplugging and reconnecting it")
|
||||||
|
self.df.setText(self.df_template.arg(info[0]).arg(info[1]).arg(info[2]))
|
||||||
|
space = self.dev.available_space(end_session=False)
|
||||||
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("SONY Reader: " + human_readable(space[0][1]) + "<br><br>Storage card: " + human_readable(sc))
|
self.device_tree.model().update_free_space(space[0][1], sc)
|
||||||
self.is_connected = True
|
self.is_connected = True
|
||||||
if space[1][2] > 0: card = "a:"
|
if space[1][2] > 0: self.card = "a:"
|
||||||
elif space[2][2] > 0: card = "b:"
|
elif space[2][2] > 0: self.card = "b:"
|
||||||
else: card = None
|
else: self.card = None
|
||||||
if card: self.device_tree.hide_card(False)
|
if self.card: self.device_tree.hide_card(False)
|
||||||
else: self.device_tree.hide_card(True)
|
else: self.device_tree.hide_card(True)
|
||||||
self.device_tree.hide_reader(False)
|
self.device_tree.hide_reader(False)
|
||||||
self.status("Loading media list from SONY Reader")
|
self.status("Loading media list from SONY Reader")
|
||||||
self.reader_model.set_data(self.dev.books())
|
self.reader_model.set_data(self.dev.books(end_session=False))
|
||||||
if card: self.status("Loading media list from Storage Card")
|
if self.card: self.status("Loading media list from Storage Card")
|
||||||
self.card_model.set_data(self.dev.books(oncard=True))
|
self.card_model.set_data(self.dev.books(oncard=True))
|
||||||
self.progress(100)
|
self.progress(100)
|
||||||
self.window.setCursor(Qt.ArrowCursor)
|
self.window.setCursor(Qt.ArrowCursor)
|
||||||
@ -344,6 +405,8 @@ def main():
|
|||||||
global DEFAULT_BOOK_COVER
|
global DEFAULT_BOOK_COVER
|
||||||
DEFAULT_BOOK_COVER = QPixmap(":/default_cover")
|
DEFAULT_BOOK_COVER = QPixmap(":/default_cover")
|
||||||
window = QMainWindow()
|
window = QMainWindow()
|
||||||
|
window.setWindowIcon(QIcon(":/icon"))
|
||||||
|
installErrorHandler(QErrorMessage(window))
|
||||||
QCoreApplication.setOrganizationName("KovidsBrain")
|
QCoreApplication.setOrganizationName("KovidsBrain")
|
||||||
QCoreApplication.setApplicationName("SONY Reader")
|
QCoreApplication.setApplicationName("SONY Reader")
|
||||||
gui = MainWindow(window, options.log_packets)
|
gui = MainWindow(window, options.log_packets)
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>878</width>
|
<width>728</width>
|
||||||
<height>759</height>
|
<height>711</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy" >
|
<property name="sizePolicy" >
|
||||||
@ -22,30 +22,15 @@
|
|||||||
<string>SONY Reader</string>
|
<string>SONY Reader</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget" >
|
<widget class="QWidget" name="centralwidget" >
|
||||||
<layout class="QGridLayout" >
|
<layout class="QVBoxLayout" >
|
||||||
<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 row="1" column="0" >
|
<item>
|
||||||
<widget class="QProgressBar" name="progress_bar" >
|
<layout class="QHBoxLayout" >
|
||||||
<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" >
|
|
||||||
<property name="orientation" >
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="layoutWidget" >
|
|
||||||
<layout class="QVBoxLayout" >
|
|
||||||
<property name="margin" >
|
<property name="margin" >
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
@ -54,33 +39,52 @@
|
|||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="DeviceView" name="device_tree" >
|
<widget class="DeviceView" name="device_tree" >
|
||||||
<property name="acceptDrops" >
|
<property name="sizePolicy" >
|
||||||
<bool>true</bool>
|
<sizepolicy>
|
||||||
|
<hsizetype>5</hsizetype>
|
||||||
|
<vsizetype>5</vsizetype>
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize" >
|
||||||
|
<size>
|
||||||
|
<width>10000</width>
|
||||||
|
<height>95</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="verticalScrollBarPolicy" >
|
||||||
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalScrollBarPolicy" >
|
||||||
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="dragDropMode" >
|
<property name="dragDropMode" >
|
||||||
<enum>QAbstractItemView::DropOnly</enum>
|
<enum>QAbstractItemView::DropOnly</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="alternatingRowColors" >
|
<property name="flow" >
|
||||||
<bool>true</bool>
|
<enum>QListView::TopToBottom</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="rootIsDecorated" >
|
<property name="spacing" >
|
||||||
<bool>false</bool>
|
<number>20</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="animated" >
|
<property name="viewMode" >
|
||||||
<bool>true</bool>
|
<enum>QListView::IconMode</enum>
|
||||||
</property>
|
|
||||||
<property name="allColumnsShowFocus" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="df" >
|
<widget class="QLabel" name="df" >
|
||||||
<property name="autoFillBackground" >
|
<property name="sizePolicy" >
|
||||||
<bool>false</bool>
|
<sizepolicy>
|
||||||
|
<hsizetype>5</hsizetype>
|
||||||
|
<vsizetype>0</vsizetype>
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string>Main Memory:<br><br>Storage card:</string>
|
<string><b>libprs500</b> was created by <b>Kovid Goyal</b> &copy; 2006<br><br>%1 %2 %3</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="textFormat" >
|
<property name="textFormat" >
|
||||||
<enum>Qt::RichText</enum>
|
<enum>Qt::RichText</enum>
|
||||||
@ -88,23 +92,77 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</item>
|
||||||
<widget class="QWidget" name="layoutWidget" >
|
|
||||||
<layout class="QVBoxLayout" >
|
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing" >
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" >
|
<layout class="QGridLayout" >
|
||||||
<property name="margin" >
|
<property name="margin" >
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="spacing" >
|
<property name="spacing" >
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
|
<item row="0" column="0" >
|
||||||
|
<widget class="DeviceBooksView" name="device_view" >
|
||||||
|
<property name="sizePolicy" >
|
||||||
|
<sizepolicy>
|
||||||
|
<hsizetype>5</hsizetype>
|
||||||
|
<vsizetype>5</vsizetype>
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="dragEnabled" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragDropOverwriteMode" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="alternatingRowColors" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior" >
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
<property name="showGrid" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" >
|
||||||
|
<widget class="LibraryBooksView" name="library_view" >
|
||||||
|
<property name="sizePolicy" >
|
||||||
|
<sizepolicy>
|
||||||
|
<hsizetype>5</hsizetype>
|
||||||
|
<vsizetype>5</vsizetype>
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>10</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="acceptDrops" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragEnabled" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragDropOverwriteMode" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="dragDropMode" >
|
||||||
|
<enum>QAbstractItemView::DragDrop</enum>
|
||||||
|
</property>
|
||||||
|
<property name="alternatingRowColors" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior" >
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
<property name="showGrid" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" >
|
<layout class="QHBoxLayout" >
|
||||||
<property name="margin" >
|
<property name="margin" >
|
||||||
@ -163,68 +221,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="DeviceBooksView" name="device_view" >
|
|
||||||
<property name="sizePolicy" >
|
|
||||||
<sizepolicy>
|
|
||||||
<hsizetype>13</hsizetype>
|
|
||||||
<vsizetype>13</vsizetype>
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="dragEnabled" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropOverwriteMode" >
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="alternatingRowColors" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="selectionBehavior" >
|
|
||||||
<enum>QAbstractItemView::SelectRows</enum>
|
|
||||||
</property>
|
|
||||||
<property name="showGrid" >
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="LibraryBooksView" name="library_view" >
|
|
||||||
<property name="sizePolicy" >
|
|
||||||
<sizepolicy>
|
|
||||||
<hsizetype>13</hsizetype>
|
|
||||||
<vsizetype>13</vsizetype>
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="acceptDrops" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragEnabled" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropOverwriteMode" >
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropMode" >
|
|
||||||
<enum>QAbstractItemView::DragDrop</enum>
|
|
||||||
</property>
|
|
||||||
<property name="alternatingRowColors" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="selectionBehavior" >
|
|
||||||
<enum>QAbstractItemView::SelectRows</enum>
|
|
||||||
</property>
|
|
||||||
<property name="showGrid" >
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" >
|
<layout class="QHBoxLayout" >
|
||||||
<property name="margin" >
|
<property name="margin" >
|
||||||
@ -244,9 +240,6 @@
|
|||||||
<property name="acceptDrops" >
|
<property name="acceptDrops" >
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="text" >
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="pixmap" >
|
<property name="pixmap" >
|
||||||
<pixmap resource="images.qrc" >:/images/cherubs.jpg</pixmap>
|
<pixmap resource="images.qrc" >:/images/cherubs.jpg</pixmap>
|
||||||
</property>
|
</property>
|
||||||
@ -257,16 +250,8 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="book_info" >
|
<widget class="QLabel" name="book_info" >
|
||||||
<property name="sizePolicy" >
|
|
||||||
<sizepolicy>
|
|
||||||
<hsizetype>5</hsizetype>
|
|
||||||
<vsizetype>5</vsizetype>
|
|
||||||
<horstretch>10</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string><table><tr><td><b>Title: </b>%1</td><td><b>&nbsp;Size:</b> %2</td></tr><tr><td><b>Author: </b>%3</td><td><b>&nbsp;Type: </b>%4</td></tr></string>
|
<string><table><tr><td><b>Title: </b>%1</td><td><b>&nbsp;Size:</b> %2</td></tr><tr><td><b>Author: </b>%3</td><td><b>&nbsp;Type: </b>%4</td></tr></table></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="textFormat" >
|
<property name="textFormat" >
|
||||||
<enum>Qt::RichText</enum>
|
<enum>Qt::RichText</enum>
|
||||||
@ -275,8 +260,14 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
<item>
|
||||||
</widget>
|
<widget class="QProgressBar" name="progress_bar" >
|
||||||
|
<property name="value" >
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -348,23 +339,23 @@
|
|||||||
</action>
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>DeviceBooksView</class>
|
||||||
|
<extends>QTableView</extends>
|
||||||
|
<header>widgets.h</header>
|
||||||
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>CoverDisplay</class>
|
<class>CoverDisplay</class>
|
||||||
<extends>QLabel</extends>
|
<extends>QLabel</extends>
|
||||||
<header>widgets.h</header>
|
<header>widgets.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
|
||||||
<class>LibraryBooksView</class>
|
|
||||||
<extends>QTableView</extends>
|
|
||||||
<header>widgets.h</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>DeviceView</class>
|
<class>DeviceView</class>
|
||||||
<extends>QTreeView</extends>
|
<extends>QListView</extends>
|
||||||
<header>widgets.h</header>
|
<header>widgets.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>DeviceBooksView</class>
|
<class>LibraryBooksView</class>
|
||||||
<extends>QTableView</extends>
|
<extends>QTableView</extends>
|
||||||
<header>widgets.h</header>
|
<header>widgets.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
from PyQt4 import QtGui, QtCore
|
from PyQt4 import QtGui, QtCore
|
||||||
from PyQt4.QtCore import Qt, SIGNAL
|
from PyQt4.QtCore import Qt, SIGNAL
|
||||||
from PyQt4.Qt import QApplication, QString, QFont, QStandardItemModel, QStandardItem, QVariant, QAbstractTableModel, QTableView, QTreeView, QLabel,\
|
from PyQt4.Qt import QApplication, QString, QFont, QAbstractListModel, QVariant, QAbstractTableModel, QTableView, QListView, QLabel,\
|
||||||
QAbstractItemView, QPixmap, QIcon, QSize, QMessageBox, QSettings, QFileDialog, QErrorMessage, QDialog, QSpinBox, QPoint, QTemporaryFile, QDir, QFile, QIODevice,\
|
QAbstractItemView, QPixmap, QIcon, QSize, QMessageBox, QSettings, QFileDialog, QErrorMessage, QDialog, QSpinBox, QPoint, QTemporaryFile, QDir, QFile, QIODevice,\
|
||||||
QPainterPath, QItemDelegate, QPainter, QPen, QColor, QLinearGradient, QBrush, QStyle,\
|
QPainterPath, QItemDelegate, QPainter, QPen, QColor, QLinearGradient, QBrush, QStyle,\
|
||||||
QStringList, QByteArray, QBuffer, QMimeData, QTextStream, QIODevice, QDrag, QRect
|
QStringList, QByteArray, QBuffer, QMimeData, QTextStream, QIODevice, QDrag, QRect
|
||||||
@ -28,25 +28,12 @@ from urllib import quote, unquote
|
|||||||
from math import sin, cos, pi
|
from math import sin, cos, pi
|
||||||
from libprs500 import TEMPORARY_FILENAME_TEMPLATE as TFT
|
from libprs500 import TEMPORARY_FILENAME_TEMPLATE as TFT
|
||||||
from libprs500.lrf.meta import LRFMetaFile
|
from libprs500.lrf.meta import LRFMetaFile
|
||||||
|
from libprs500.gui import Error, Warning
|
||||||
|
|
||||||
NONE = QVariant()
|
NONE = QVariant()
|
||||||
TIME_WRITE_FMT = "%d %b %Y"
|
TIME_WRITE_FMT = "%d %b %Y"
|
||||||
COVER_HEIGHT = 80
|
COVER_HEIGHT = 80
|
||||||
|
|
||||||
def human_readable(size):
|
|
||||||
""" 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"
|
|
||||||
elif size < 1024*1024*1024*1024: divisor, suffix = 1024*1024, "GB"
|
|
||||||
size = str(size/divisor)
|
|
||||||
if size.find(".") > -1: size = size[:size.find(".")+2]
|
|
||||||
return size + " " + suffix
|
|
||||||
|
|
||||||
|
|
||||||
def wrap(s, width=20):
|
|
||||||
return textwrap.fill(str(s), width)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FileDragAndDrop(object):
|
class FileDragAndDrop(object):
|
||||||
@ -67,11 +54,11 @@ class FileDragAndDrop(object):
|
|||||||
for url in candidates:
|
for url in candidates:
|
||||||
o = urlparse(url)
|
o = urlparse(url)
|
||||||
if o.scheme and o.scheme != 'file':
|
if o.scheme and o.scheme != 'file':
|
||||||
print >>sys.stderr, o.scheme, " not supported in drop events"
|
Warning(o.scheme + " not supported in drop events")
|
||||||
continue
|
continue
|
||||||
path = unquote(o.path)
|
path = unquote(o.path)
|
||||||
if not os.access(path, os.R_OK):
|
if not os.access(path, os.R_OK):
|
||||||
print >>sys.stderr, "You do not have read permission for: " + path
|
Warning("You do not have read permission for: " + path)
|
||||||
continue
|
continue
|
||||||
if os.path.isdir(path):
|
if os.path.isdir(path):
|
||||||
root, dirs, files2 = os.walk(path)
|
root, dirs, files2 = os.walk(path)
|
||||||
@ -81,17 +68,20 @@ class FileDragAndDrop(object):
|
|||||||
else: files.append(path)
|
else: files.append(path)
|
||||||
return files
|
return files
|
||||||
|
|
||||||
def __init__(self, QtBaseClass):
|
def __init__(self, QtBaseClass, enable_drag=True):
|
||||||
self.QtBaseClass = QtBaseClass
|
self.QtBaseClass = QtBaseClass
|
||||||
|
self.enable_drag = enable_drag
|
||||||
|
|
||||||
def mousePressEvent(self, event):
|
def mousePressEvent(self, event):
|
||||||
self.QtBaseClass.mousePressEvent(self, event)
|
self.QtBaseClass.mousePressEvent(self, event)
|
||||||
|
if self.enable_drag:
|
||||||
if event.button == Qt.LeftButton:
|
if event.button == Qt.LeftButton:
|
||||||
self._drag_start_position = event.pos()
|
self._drag_start_position = event.pos()
|
||||||
|
|
||||||
|
|
||||||
def mouseMoveEvent(self, event):
|
def mouseMoveEvent(self, event):
|
||||||
self.QtBaseClass.mousePressEvent(self, event)
|
self.QtBaseClass.mousePressEvent(self, event)
|
||||||
|
if self.enable_drag:
|
||||||
if event.buttons() & Qt.LeftButton != Qt.LeftButton: return
|
if event.buttons() & Qt.LeftButton != Qt.LeftButton: return
|
||||||
if (event.pos() - self._drag_start_position).manhattanLength() < QApplication.startDragDistance(): return
|
if (event.pos() - self._drag_start_position).manhattanLength() < QApplication.startDragDistance(): return
|
||||||
self.start_drag(self._drag_start_position)
|
self.start_drag(self._drag_start_position)
|
||||||
@ -108,9 +98,14 @@ class FileDragAndDrop(object):
|
|||||||
def dropEvent(self, event):
|
def dropEvent(self, event):
|
||||||
files = self._get_r_ok_files(event)
|
files = self._get_r_ok_files(event)
|
||||||
if files:
|
if files:
|
||||||
|
try:
|
||||||
if self.files_dropped(files, event): event.acceptProposedAction()
|
if self.files_dropped(files, event): event.acceptProposedAction()
|
||||||
|
except Exception, e:
|
||||||
|
Error("There was an error processing the dropped files.", e)
|
||||||
|
raise e
|
||||||
|
|
||||||
def files_dropped(self, files): return False
|
|
||||||
|
def files_dropped(self, files, event): return False
|
||||||
|
|
||||||
def drag_object_from_files(self, files):
|
def drag_object_from_files(self, files):
|
||||||
if files:
|
if files:
|
||||||
@ -138,8 +133,26 @@ class FileDragAndDrop(object):
|
|||||||
return self.drag_object_from_files(files), self._dragged_files
|
return self.drag_object_from_files(files), self._dragged_files
|
||||||
|
|
||||||
|
|
||||||
class TableView(QTableView):
|
class TableView(FileDragAndDrop, QTableView):
|
||||||
def renderToPixmap(self, indices):
|
def __init__(self, parent):
|
||||||
|
FileDragAndDrop.__init__(self, QTableView)
|
||||||
|
QTableView.__init__(self, parent)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def wrap(cls, s, width=20): return textwrap.fill(str(s), width)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def human_readable(cls, size):
|
||||||
|
""" 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"
|
||||||
|
elif size < 1024*1024*1024*1024: divisor, suffix = 1024*1024, "GB"
|
||||||
|
size = str(size/divisor)
|
||||||
|
if size.find(".") > -1: size = size[:size.find(".")+2]
|
||||||
|
return size + " " + suffix
|
||||||
|
|
||||||
|
def render_to_pixmap(self, indices):
|
||||||
rect = self.visualRect(indices[0])
|
rect = self.visualRect(indices[0])
|
||||||
rects = []
|
rects = []
|
||||||
for i in range(len(indices)):
|
for i in range(len(indices)):
|
||||||
@ -157,8 +170,15 @@ class TableView(QTableView):
|
|||||||
painter.end()
|
painter.end()
|
||||||
return pixmap
|
return pixmap
|
||||||
|
|
||||||
|
def drag_object_from_files(self, files):
|
||||||
|
drag = FileDragAndDrop.drag_object_from_files(self, files)
|
||||||
|
drag.setPixmap(self.render_to_pixmap(self.selectedIndexes()))
|
||||||
|
return drag
|
||||||
|
|
||||||
class TemporaryFile(QTemporaryFile):
|
class TemporaryFile(QTemporaryFile):
|
||||||
_file_name = ""
|
_file_name = ""
|
||||||
|
def __del__(self):
|
||||||
|
if os.access(self.name, os.F_OK): os.remove(self.name)
|
||||||
def __init__(self, ext=""):
|
def __init__(self, ext=""):
|
||||||
if ext: ext = "." + ext
|
if ext: ext = "." + ext
|
||||||
path = QDir.tempPath() + "/" + TFT + "_XXXXXX"+ext
|
path = QDir.tempPath() + "/" + TFT + "_XXXXXX"+ext
|
||||||
@ -202,31 +222,38 @@ class CoverDisplay(FileDragAndDrop, QLabel):
|
|||||||
file.close()
|
file.close()
|
||||||
drag.start(Qt.MoveAction)
|
drag.start(Qt.MoveAction)
|
||||||
|
|
||||||
class DeviceView(QTreeView):
|
class DeviceView(FileDragAndDrop, QListView):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QTreeView.__init__(self, parent)
|
FileDragAndDrop.__init__(self, QListView, enable_drag=False)
|
||||||
self.header().hide()
|
QListView.__init__(self, parent)
|
||||||
self.setIconSize(QSize(32,32))
|
|
||||||
|
|
||||||
def hide_reader(self, x):
|
def hide_reader(self, x):
|
||||||
self.setRowHidden(2, self.model().indexFromItem(self.model().invisibleRootItem()), x)
|
self.model().update_devices(reader=not x)
|
||||||
|
|
||||||
def hide_card(self, x):
|
def hide_card(self, x):
|
||||||
self.setRowHidden(4, self.model().indexFromItem(self.model().invisibleRootItem()), x)
|
self.model().update_devices(card=not x)
|
||||||
|
|
||||||
|
def files_dropped(self, files, event):
|
||||||
|
ids = []
|
||||||
|
md = event.mimeData()
|
||||||
|
if md.hasFormat("application/x-libprs500-id"):
|
||||||
|
ids = [ int(id) for id in FileDragAndDrop._bytes_to_string(md.data("application/x-libprs500-id")).split()]
|
||||||
|
index = self.indexAt(event.pos())
|
||||||
|
if index.isValid():
|
||||||
|
return self.model().files_dropped(files, index, ids)
|
||||||
|
|
||||||
class DeviceBooksView(TableView):
|
class DeviceBooksView(TableView):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QTableView.__init__(self, parent)
|
TableView.__init__(self, parent)
|
||||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
self.setSortingEnabled(True)
|
self.setSortingEnabled(True)
|
||||||
|
|
||||||
class LibraryBooksView(FileDragAndDrop, TableView):
|
class LibraryBooksView(TableView):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
FileDragAndDrop.__init__(self, QTableView)
|
TableView.__init__(self, parent)
|
||||||
QTableView.__init__(self, parent)
|
|
||||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
self.setSortingEnabled(True)
|
self.setSortingEnabled(True)
|
||||||
self.setItemDelegate(LibraryDelegate(self))
|
self.setItemDelegate(LibraryDelegate(self, rating_column=4))
|
||||||
|
|
||||||
def dragEnterEvent(self, event):
|
def dragEnterEvent(self, event):
|
||||||
if not event.mimeData().hasFormat("application/x-libprs500-id"):
|
if not event.mimeData().hasFormat("application/x-libprs500-id"):
|
||||||
@ -242,7 +269,6 @@ class LibraryBooksView(FileDragAndDrop, TableView):
|
|||||||
if drag:
|
if drag:
|
||||||
ids = [ str(self.model().id_from_index(index)) for index in indexes ]
|
ids = [ str(self.model().id_from_index(index)) for index in indexes ]
|
||||||
drag.mimeData().setData("application/x-libprs500-id", QByteArray("\n".join(ids)))
|
drag.mimeData().setData("application/x-libprs500-id", QByteArray("\n".join(ids)))
|
||||||
drag.setPixmap(self.renderToPixmap(indexes))
|
|
||||||
drag.start()
|
drag.start()
|
||||||
|
|
||||||
|
|
||||||
@ -261,8 +287,9 @@ class LibraryDelegate(QItemDelegate):
|
|||||||
SIZE = 16
|
SIZE = 16
|
||||||
PEN = QPen(COLOR, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
|
PEN = QPen(COLOR, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent, rating_column=-1):
|
||||||
QItemDelegate.__init__(self, parent)
|
QItemDelegate.__init__(self, parent )
|
||||||
|
self.rating_column = rating_column
|
||||||
self.star_path = QPainterPath()
|
self.star_path = QPainterPath()
|
||||||
self.star_path.moveTo(90, 50)
|
self.star_path.moveTo(90, 50)
|
||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
@ -277,13 +304,13 @@ class LibraryDelegate(QItemDelegate):
|
|||||||
|
|
||||||
|
|
||||||
def sizeHint(self, option, index):
|
def sizeHint(self, option, index):
|
||||||
if index.column() != 4:
|
if index.column() != self.rating_column:
|
||||||
return QItemDelegate.sizeHint(self, option, index)
|
return QItemDelegate.sizeHint(self, option, index)
|
||||||
num = index.model().data(index, Qt.DisplayRole).toInt()[0]
|
num = index.model().data(index, Qt.DisplayRole).toInt()[0]
|
||||||
return QSize(num*(self.SIZE), self.SIZE+4)
|
return QSize(num*(self.SIZE), self.SIZE+4)
|
||||||
|
|
||||||
def paint(self, painter, option, index):
|
def paint(self, painter, option, index):
|
||||||
if index.column() != 4:
|
if index.column() != self.rating_column:
|
||||||
return QItemDelegate.paint(self, painter, option, index)
|
return QItemDelegate.paint(self, painter, option, index)
|
||||||
num = index.model().data(index, Qt.DisplayRole).toInt()[0]
|
num = index.model().data(index, Qt.DisplayRole).toInt()[0]
|
||||||
def draw_star():
|
def draw_star():
|
||||||
@ -343,14 +370,13 @@ class LibraryDelegate(QItemDelegate):
|
|||||||
|
|
||||||
|
|
||||||
class LibraryBooksModel(QAbstractTableModel):
|
class LibraryBooksModel(QAbstractTableModel):
|
||||||
FIELDS = ["id", "title", "authors", "size", "date", "rating", "publisher", "tags"]
|
FIELDS = ["id", "title", "authors", "size", "date", "rating", "publisher", "tags", "comments"]
|
||||||
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)
|
||||||
self.db = None
|
self.db = None
|
||||||
self._data = None
|
self._data = None
|
||||||
self._orig_data = None
|
self._orig_data = None
|
||||||
self.image_file = None
|
|
||||||
|
|
||||||
def extract_formats(self, indices):
|
def extract_formats(self, indices):
|
||||||
files, rows = [], []
|
files, rows = [], []
|
||||||
@ -359,7 +385,8 @@ class LibraryBooksModel(QAbstractTableModel):
|
|||||||
if row in rows: continue
|
if row in rows: continue
|
||||||
else: rows.append(row)
|
else: rows.append(row)
|
||||||
id = self.id_from_index(index)
|
id = self.id_from_index(index)
|
||||||
basename = re.sub("\n", "", self._data[row]["title"]+" by "+ self._data[row]["authors"])
|
au = self._data[row]["authors"] if self._data[row]["authors"] else "Unknown"
|
||||||
|
basename = re.sub("\n", "", "_"+str(id)+"_"+self._data[row]["title"]+" by "+ au)
|
||||||
exts = self.db.get_extensions(id)
|
exts = self.db.get_extensions(id)
|
||||||
for ext in exts:
|
for ext in exts:
|
||||||
fmt = self.db.get_format(id, ext)
|
fmt = self.db.get_format(id, ext)
|
||||||
@ -393,7 +420,7 @@ class LibraryBooksModel(QAbstractTableModel):
|
|||||||
self.emit(SIGNAL('formats_added'), index)
|
self.emit(SIGNAL('formats_added'), index)
|
||||||
|
|
||||||
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)-3
|
||||||
|
|
||||||
def setData(self, index, value, role):
|
def setData(self, index, value, role):
|
||||||
done = False
|
done = False
|
||||||
@ -457,9 +484,11 @@ class LibraryBooksModel(QAbstractTableModel):
|
|||||||
pix = QPixmap()
|
pix = QPixmap()
|
||||||
pix.loadFromData(cover, "", Qt.AutoColor)
|
pix.loadFromData(cover, "", Qt.AutoColor)
|
||||||
cover = None if pix.isNull() else pix
|
cover = None if pix.isNull() else pix
|
||||||
au = row["authors"]
|
tags = row["tags"]
|
||||||
if not au: au = "Unknown"
|
if not tags: tags = ""
|
||||||
return row["title"], au, human_readable(int(row["size"])), exts, cover
|
comments = row["comments"]
|
||||||
|
if not comments: comments = ""
|
||||||
|
return exts, tags, comments, cover
|
||||||
|
|
||||||
def id_from_index(self, index): return self._data[index.row()]["id"]
|
def id_from_index(self, index): return self._data[index.row()]["id"]
|
||||||
|
|
||||||
@ -471,6 +500,9 @@ class LibraryBooksModel(QAbstractTableModel):
|
|||||||
break
|
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 book_info(self, id, cols=["title", "authors", "cover"]):
|
||||||
|
return self.db.get_row_by_id(id, cols)
|
||||||
|
|
||||||
def data(self, index, role):
|
def data(self, index, role):
|
||||||
if role == Qt.DisplayRole or role == Qt.EditRole:
|
if role == Qt.DisplayRole or role == Qt.EditRole:
|
||||||
row, col = index.row(), index.column()
|
row, col = index.row(), index.column()
|
||||||
@ -481,15 +513,15 @@ class LibraryBooksModel(QAbstractTableModel):
|
|||||||
if r < 0: r= 0
|
if r < 0: r= 0
|
||||||
if r > 5: r=5
|
if r > 5: r=5
|
||||||
return QVariant(r)
|
return QVariant(r)
|
||||||
if col == 0: text = wrap(row["title"], width=25)
|
if col == 0: text = TableView.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 = TableView.wrap(re.sub("&", "\n", au), width=25)
|
||||||
elif col == 2: text = human_readable(row["size"])
|
elif col == 2: text = TableView.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 == 5:
|
elif col == 5:
|
||||||
pub = row["publisher"]
|
pub = row["publisher"]
|
||||||
if pub: text = wrap(pub, 20)
|
if pub: text = TableView.wrap(pub, 20)
|
||||||
if text == None: 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]:
|
||||||
@ -582,9 +614,9 @@ class DeviceBooksModel(QAbstractTableModel):
|
|||||||
if role == Qt.DisplayRole:
|
if role == Qt.DisplayRole:
|
||||||
row, col = index.row(), index.column()
|
row, col = index.row(), index.column()
|
||||||
book = self._data[row]
|
book = self._data[row]
|
||||||
if col == 0: text = wrap(book.title, width=40)
|
if col == 0: text = TableView.wrap(book.title, width=40)
|
||||||
elif col == 1: text = re.sub("&\s*","\n", book.author)
|
elif col == 1: text = re.sub("&\s*","\n", book.author)
|
||||||
elif col == 2: text = human_readable(book.size)
|
elif col == 2: text = TableView.human_readable(book.size)
|
||||||
elif col == 3: text = time.strftime(TIME_WRITE_FMT, book.datetime)
|
elif col == 3: text = time.strftime(TIME_WRITE_FMT, book.datetime)
|
||||||
return QVariant(text)
|
return QVariant(text)
|
||||||
elif role == Qt.TextAlignmentRole and index.column() in [2,3]:
|
elif role == Qt.TextAlignmentRole and index.column() in [2,3]:
|
||||||
@ -602,7 +634,7 @@ class DeviceBooksModel(QAbstractTableModel):
|
|||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
au = row.author if row.author else "Unknown"
|
au = row.author if row.author else "Unknown"
|
||||||
return row.title, au, human_readable(row.size), row.mime, cover
|
return row.title, au, TableView.human_readable(row.size), row.mime, cover
|
||||||
|
|
||||||
def sort(self, col, order):
|
def sort(self, col, order):
|
||||||
def getter(key, func): return lambda x : func(attrgetter(key)(x))
|
def getter(key, func): return lambda x : func(attrgetter(key)(x))
|
||||||
@ -653,30 +685,61 @@ class DeviceBooksModel(QAbstractTableModel):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceModel(QStandardItemModel):
|
class DeviceModel(QAbstractListModel):
|
||||||
def __init__(self, parent):
|
|
||||||
QStandardItemModel.__init__(self, parent)
|
memory_free = 0
|
||||||
root = self.invisibleRootItem()
|
card_free = 0
|
||||||
|
show_reader = False
|
||||||
|
show_card = False
|
||||||
|
|
||||||
|
def update_devices(self, reader=None, card=None):
|
||||||
|
if reader != None: self.show_reader = reader
|
||||||
|
if card != None: self.show_card = card
|
||||||
|
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.index(1), self.index(2))
|
||||||
|
|
||||||
|
def rowCount(self, parent): return 3
|
||||||
|
|
||||||
|
def update_free_space(self, reader, card):
|
||||||
|
self.memory_free = reader
|
||||||
|
self.card_free = card
|
||||||
|
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.index(1), self.index(2))
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
row = index.row()
|
||||||
|
data = NONE
|
||||||
|
if role == Qt.DisplayRole:
|
||||||
|
text = None
|
||||||
|
if row == 0: text = "Library"
|
||||||
|
if row == 1 and self.show_reader:
|
||||||
|
text = "Reader\n" + TableView.human_readable(self.memory_free) + " available"
|
||||||
|
elif row == 2 and self.show_card:
|
||||||
|
text = "Card\n" + TableView.human_readable(self.card_free) + " available"
|
||||||
|
if text: data = QVariant(text)
|
||||||
|
elif role == Qt.DecorationRole:
|
||||||
|
icon = None
|
||||||
|
if row == 0: icon = QIcon(":/library")
|
||||||
|
elif row == 1 and self.show_reader: icon = QIcon(":/reader")
|
||||||
|
elif self.show_card: icon = QIcon(":/card")
|
||||||
|
if icon: data = QVariant(icon)
|
||||||
|
elif role == Qt.SizeHintRole:
|
||||||
|
if row == 1: return QVariant(QSize(150, 70))
|
||||||
|
elif role == Qt.FontRole:
|
||||||
font = QFont()
|
font = QFont()
|
||||||
font.setBold(True)
|
font.setBold(True)
|
||||||
self.library = QStandardItem(QIcon(":/library"), QString("Library"))
|
data = QVariant(font)
|
||||||
self.reader = QStandardItem(QIcon(":/reader"), "SONY Reader")
|
return data
|
||||||
self.card = QStandardItem(QIcon(":/card"), "Storage Card")
|
|
||||||
self.library.setFont(font)
|
|
||||||
self.reader.setFont(font)
|
|
||||||
self.card.setFont(font)
|
|
||||||
self.blank = QStandardItem("")
|
|
||||||
self.blank.setFlags(Qt.ItemFlags())
|
|
||||||
root.appendRow(self.library)
|
|
||||||
root.appendRow(self.blank)
|
|
||||||
root.appendRow(self.reader)
|
|
||||||
root.appendRow(self.blank.clone())
|
|
||||||
root.appendRow(self.card)
|
|
||||||
self.library.appendRow(QStandardItem("Books"))
|
|
||||||
self.reader.appendRow(QStandardItem("Books"))
|
|
||||||
self.card.appendRow(QStandardItem("Books"))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def is_library(self, index): return index.row() == 0
|
||||||
|
def is_reader(self, index): return index.row() == 1
|
||||||
|
def is_card(self, index): return index.row() == 2
|
||||||
|
|
||||||
|
def files_dropped(self, files, index, ids):
|
||||||
|
ret = False
|
||||||
|
if self.is_library(index) and not ids:
|
||||||
|
self.emit(SIGNAL("books_dropped"), files)
|
||||||
|
ret = True
|
||||||
|
elif self.is_reader(index):
|
||||||
|
self.emit(SIGNAL("upload_books"), "reader", files, ids)
|
||||||
|
elif self.is_card(index):
|
||||||
|
self.emit(SIGNAL("upload_books"), "card", files, ids)
|
||||||
|
return ret
|
||||||
|
Loading…
x
Reference in New Issue
Block a user