Fixed connection errors due to timeouts in the GUI (Basically, increased the timeout in PRS500Device.open to 10s to allow device to initialize USB handler.

Refactored device display code to work with changed implementation of book list parsing in libprs500
This commit is contained in:
Kovid Goyal 2006-12-04 05:25:56 +00:00
parent a591a6d788
commit ddc2e5bbdd
8 changed files with 484 additions and 180 deletions

View File

@ -16,6 +16,7 @@
from xml.dom.ext import PrettyPrint as PrettyPrint
import xml.dom.minidom as dom
from base64 import b64decode as decode
import time
class book_metadata_field(object):
def __init__(self, attr, formatter=None):
@ -27,7 +28,7 @@ class book_metadata_field(object):
class Book(object):
title = book_metadata_field("title")
author = book_metadata_field("author")
author = book_metadata_field("author", formatter=lambda x: x if x.strip() else "Unknown")
mime = book_metadata_field("mime")
rpath = book_metadata_field("path")
id = book_metadata_field("id", formatter=int)

View File

@ -133,17 +133,22 @@ class PRS500Device(object):
PRS500_BULK_OUT_EP = 0x02 #: Endpoint for Bulk writes
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
device_descriptor = DeviceDescriptor(SONY_VENDOR_ID, PRS500_PRODUCT_ID, PRS500_INTERFACE_ID)
def safe(func):
"""
Decorator that wraps a call to C{func} to ensure that exceptions are handled correctly.
It also calls L{open} to claim the interface and initialize the Reader if needed.
As a convenience, C{safe} automatically sends the a L{USBConnect} after calling func, unless func has
As a convenience, C{safe} automatically sends the a L{EndSession} after calling func, unless func has
a keyword argument named C{end_session} set to C{False}.
An L{ArgumentError} will cause the L{USBConnect} command to be sent to the device, unless end_session is set to C{False}.
An L{ArgumentError} will cause the L{EndSession} command to be sent to the device, unless end_session is set to C{False}.
An L{usb.USBError} will cause the library to release control of the USB interface via a call to L{close}.
@todo: Fix handling of timeout errors
"""
def run_session(*args, **kwargs):
dev = args[0]
@ -153,17 +158,21 @@ class PRS500Device(object):
res = func(*args, **kwargs)
except ArgumentError, e:
if not kwargs.has_key("end_session") or kwargs["end_session"]:
dev._send_validated_command(USBConnect())
dev._send_validated_command(EndSession())
raise e
except usb.USBError, e:
if "No such device" in str(e):
raise DeviceError()
elif "Connection timed out" in str(e):
elif "Connection timed out" in str(e):
dev.close()
raise TimeoutError(func.__name__)
elif "Protocol error" in str(e):
dev.close()
raise ProtocolError("There was an unknown error in the protocol. Contact the developer.")
dev.close()
raise e
if not kwargs.has_key("end_session") or kwargs["end_session"]:
dev._send_validated_command(USBConnect())
dev._send_validated_command(EndSession())
return res
return run_session
@ -174,16 +183,23 @@ class PRS500Device(object):
@param: report_progress: Function that is called with a % progress (number between 0 and 100) for various tasks
If it is called with -1 that means that the task does not have any progress information
"""
self.device_descriptor = DeviceDescriptor(PRS500Device.SONY_VENDOR_ID,
PRS500Device.PRS500_PRODUCT_ID,
PRS500Device.PRS500_INTERFACE_ID)
self.device = self.device_descriptor.getDevice() #: The actual device (PyUSB object)
self.handle = None #: Handle that is used to communicate with device. Setup in L{open}
self._log_packets = log_packets
self.report_progress = report_progress
def is_connected(self):
return self.device_descriptor.getDevice() != None
def reconnect(self):
self.device = self.device_descriptor.getDevice()
self.handle = None
@classmethod
def is_connected(cls):
"""
This method checks to see whether the device is physically connected.
It does not return any information about the validity of the software connection. You may need to call L{reconnect} if you keep
getting L{DeviceError}.
"""
return cls.device_descriptor.getDevice() != None
@classmethod
def _validate_response(cls, res, type=0x00, number=0x00):
@ -205,7 +221,7 @@ class PRS500Device(object):
raise DeviceError()
self.handle = self.device.open()
self.handle.claimInterface(self.device_descriptor.interface_id)
res = self._send_validated_command(GetUSBProtocolVersion())
res = self._send_validated_command(GetUSBProtocolVersion(), timeout=10000) # Large timeout as device mat still be initializing
if res.code != 0: raise ProtocolError("Unable to get USB Protocol version.")
version = self._bulk_read(24, data_type=USBProtocolVersion)[0].version
if version not in KNOWN_USB_PROTOCOL_VERSIONS:
@ -216,12 +232,12 @@ class PRS500Device(object):
if res.code != 0:
raise ProtocolError("Unlocking of device not implemented. Remove locking and retry.")
def close(self):
""" Release device interface """
self.handle.reset()
self.handle.releaseInterface()
self.handle, self.device = None, None
def _send_command(self, command, response_type=Response, timeout=1000):
"""
Send L{command<Command>} to device and return its L{response<Response>}.

View File

@ -3,6 +3,7 @@
<file alias="default_cover">images/cherubs.jpg</file>
<file alias="library">images/mycomputer.png</file>
<file alias="reader">images/reader.png</file>
<file alias="card">images/memory_stick_unmount.png</file>
<file>images/clear.png</file>
<file>images/minus.png</file>
<file>images/plus.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -1,6 +1,6 @@
# Resource object code
#
# Created: Fri Nov 24 20:24:55 2006
# Created: Sun Dec 3 17:57:59 2006
# by: The Resource Compiler for PyQt (Qt v4.1.4)
#
# WARNING! All changes made in this file will be lost!
@ -8,6 +8,298 @@
from PyQt4 import QtCore
qt_resource_data = "\
\x00\x00\x12\x16\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x40\x00\x00\x00\x40\x08\x06\x00\x00\x00\xaa\x69\x71\xde\
\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\
\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x72\
\x67\x9b\xee\x3c\x1a\x00\x00\x11\xa8\x49\x44\x41\x54\x78\x9c\xed\
\x9a\x79\x70\x5c\x47\x9d\xc7\x3f\xfd\xde\xdc\x33\xd2\xe8\x96\x2d\
\xf9\x92\x7c\x61\x72\x40\x0c\x09\xc4\xd9\x84\x6c\x2a\x50\x40\x41\
\x0c\x0b\x01\xb6\x08\x47\xc1\xd6\x2e\x57\xb1\xbb\x50\x5b\x0b\x45\
\xed\x16\xd9\x9b\x64\xc3\x86\x2c\x09\x6c\x0e\x08\x39\x21\x4e\x48\
\x70\x5c\xb1\xc9\x69\x12\x5b\x8b\x15\x47\x2b\x5f\xf2\x25\xd9\x3a\
\xac\x6b\x66\xa4\x39\xde\x5c\xef\xe8\xee\xfd\x63\x24\xd9\x96\x95\
\xd8\x49\x9c\xc8\x14\xf9\x4d\xa9\x66\x9e\xde\xf4\xaf\x7f\xdf\x6f\
\xff\xae\xd7\x3d\x42\x6b\xcd\x1f\xb3\x18\xf3\x6d\xc0\x7c\xcb\x5b\
\x04\xcc\xb7\x01\xf3\x2d\x6f\x11\x30\xdf\x06\xcc\xb7\xbc\x45\xc0\
\x7c\x1b\xf0\x7a\x64\xe3\xc6\x8d\x17\x6e\xde\xbc\x39\xf8\x7a\x74\
\x88\x3f\xc4\x3e\x60\xcb\x96\x2d\xcb\x94\x52\x77\xbb\xae\xbb\xce\
\xf3\x3c\x03\xb7\xf0\x8b\x4f\x7c\xe6\xf3\x5f\x7e\x2d\xba\xfe\xa0\
\x3c\xe0\xfa\xeb\xaf\x37\x9e\x7c\xf2\xc9\x6f\x2a\xa5\xf6\x29\xa5\
\x2e\xa9\x31\x52\xfe\x4b\x0b\xb7\x9a\xae\xa7\xbe\x70\xd3\x4d\x37\
\xbd\xfd\xb5\xe8\xf4\x9d\x6d\x23\xdf\x28\xd9\xba\x75\xeb\xdb\x2e\
\xbd\xf4\xd2\xfb\xa4\x94\xab\x0c\x43\x47\x96\x5b\x1b\x59\x91\x7b\
\x84\xb1\xbc\x9f\x70\xe0\x25\xb3\x2a\xb4\xf4\x3f\x80\x6b\x00\x84\
\x10\x7e\xad\xb5\x7b\x26\x7a\xcf\xf9\x10\xd8\xba\x75\xab\x4f\x29\
\xf5\x1d\x29\xe5\xf7\xa4\x94\x46\xdc\xed\x0f\x9c\x37\x71\x2b\x66\
\x71\x98\xe1\x42\x94\xe6\x98\x47\xd2\x6b\xe4\x59\xff\x5f\xc9\xd1\
\xf1\xd4\x92\xef\x7f\xff\xfb\x35\xc0\x32\xe0\xb7\x5a\x6b\x79\x3a\
\xfd\xe7\xb4\x07\xbc\xf0\xc2\x0b\x6b\xb5\xd6\xf7\x2b\xa5\x5a\x85\
\x76\x42\xab\x33\xbf\x64\x49\x6e\x13\x23\x85\x30\x4e\x3e\x46\x55\
\x59\x12\xab\x55\x94\x64\x1a\x7f\xbe\xcf\xb0\x0b\xea\x0e\xe0\xce\
\xa9\xe1\xcb\x80\xbe\xd3\xcd\x71\x4e\x12\xb0\x75\xeb\xd6\x90\x69\
\x9a\xff\xe4\x79\xde\x37\xa5\x94\x66\xbc\x7c\xd0\xb7\x3a\x75\x07\
\xba\x30\x42\xaf\x15\x83\x01\x81\x90\x50\xbf\x56\x22\x0c\x41\xa6\
\x1c\x60\xa1\xf3\xa2\x98\x6c\xf8\xf0\x07\x6a\x6b\x6b\xef\x4b\xa7\
\xd3\x65\x60\x85\x10\xe2\x88\x3e\x8d\x8b\x9f\x73\x49\xb0\xb3\xb3\
\xf3\x72\xbf\xdf\x7f\x40\x29\xf5\x15\xbc\x72\xb0\x7d\xf2\x61\xdf\
\x79\xe3\x3f\x26\x99\xb1\x99\x48\x47\xa1\xcf\xa0\x2a\xae\x58\xf1\
\x5e\x07\x5f\x50\xe0\x28\x93\xbc\x6b\x50\x2f\x12\x84\xd4\xa4\xf1\
\xa9\x4f\x7e\xfc\x63\x53\xaa\x22\x40\xeb\xe9\xe6\x3b\x67\x3c\x60\
\xfb\xf6\xed\x55\x3e\x9f\xef\x46\xd7\x75\xbf\xe0\xba\x6e\xa0\xaa\
\x78\xd8\x58\x36\xb9\x11\x3b\x9f\xa2\xaf\x14\xa3\x6a\xc2\xc2\xa7\
\x05\x4b\x2e\x95\x08\x3c\x32\xe3\x26\x13\x83\x3e\xec\x2a\x1f\xd4\
\x9a\xe8\x96\x3f\xa5\xbd\xfa\x42\xa3\xc8\xc0\x35\x81\x40\x60\x83\
\xe3\x38\x12\x58\x09\x1c\x7b\xa5\x79\xcf\x09\x0f\xe8\xea\xea\xfa\
\xa0\xcf\xe7\x3b\xec\xba\xee\x67\xb5\x9d\x0f\x2d\x9a\x78\xca\x58\
\x9a\x7c\x9c\x91\xb4\x4b\x59\x47\x08\x1c\xc9\x93\xea\xb1\x31\x43\
\x7e\x86\xf7\x07\xd8\xfb\x74\x88\x23\x9d\x01\x32\x09\x41\xb1\xb6\
\x09\x75\xd1\x77\xd0\x6f\xfb\x22\x75\x2d\xab\x88\x04\xcd\xc0\xa7\
\xae\xfd\xc4\xd5\x53\xaa\x6b\x84\x10\x8d\xaf\x34\xf7\xbc\x7a\x40\
\x4f\x4f\x4f\x5d\xb1\x58\xbc\xd5\xf3\xbc\xf5\xae\xeb\x86\x22\xd6\
\x11\xb1\x20\xdb\x49\xde\x4a\x33\x50\x0a\xb0\x28\x2e\xc8\xed\x1a\
\xc7\x1a\x2e\x03\x30\xd2\xed\x50\xb5\xc0\x04\x2a\x61\xed\xff\x93\
\x75\xb8\xeb\xbe\x06\x46\x10\xb4\x46\x6b\xcd\xd2\xa5\x6d\x46\xb1\
\x64\x7f\x1a\xf8\xed\xd4\x34\x2b\x81\xe4\xcb\xd9\x30\x6f\x1e\xb0\
\x67\xcf\x9e\x6b\xcb\xe5\x72\x9f\xe7\x79\xd7\xc8\xa2\x15\x6e\x4c\
\xed\x14\xf5\x93\xdd\x0c\x4d\xd8\xd8\x83\x29\x9a\x95\x8b\x70\x0d\
\x72\xfd\x59\x7c\x11\x1f\xf5\xab\xab\x59\xfe\x81\x7a\x5a\x2e\x8e\
\x60\x86\x43\x34\x7e\xee\xeb\xc8\x2b\xbe\x8d\x30\x43\x00\x08\x21\
\x30\x0c\x83\xc6\xd6\x36\x02\xa1\x70\xf5\xfa\xf5\xeb\x2f\x99\x9a\
\xaa\x59\x08\x11\x7f\x39\x3b\xde\xf4\x3e\x60\xff\xfe\xfd\x0b\x5d\
\xd7\xbd\xd3\xf3\xbc\xf7\x95\xcb\xe5\x68\x20\x37\x4a\x6d\xee\x28\
\xd9\xdc\x24\x65\xbb\x48\x7c\x60\x1f\xa5\xc3\xbd\xc4\x56\x2e\xa6\
\xfe\x9d\x4b\xd0\x25\x0b\x33\xe0\x61\x62\x53\x1c\xb5\x28\x7b\xad\
\x54\xff\xd9\xf7\x18\x75\x03\x38\x8e\x73\x92\x6e\xa5\x14\x42\x08\
\x86\x8e\x1e\xe4\xe0\x81\x9e\x91\xef\xfe\xe3\xbf\x7d\x63\xea\xd6\
\x90\xd6\x7a\xe7\x5c\xf6\xbc\xa9\x1e\xd0\xd3\xd3\xf3\x25\xcf\xf3\
\x0e\xd9\xb6\x7d\x55\xd9\xca\x45\x63\xc9\x41\xc2\xe9\x51\x06\x27\
\x0a\x98\xfe\x10\xd5\x7d\x7b\x29\x1d\xee\x05\xa0\x38\x38\x86\xeb\
\x00\xa1\x08\x4a\xf9\x18\xdf\x53\xa6\x14\xbc\x84\xa6\xaf\xdc\x4e\
\x8a\x28\x9e\xe7\x61\x18\x06\x42\x88\xe3\x60\x8c\x0a\x9c\x96\x25\
\x2b\x08\x46\xe2\x0b\xae\xbc\xf2\xca\xd5\x53\xb7\x16\x09\x21\xc2\
\x73\xd9\xf4\xa6\x10\x70\xf0\xe0\xc1\xb6\xfd\xfb\xf7\x3f\xef\x79\
\xde\x2d\x85\x42\x21\xa6\xd3\xb9\x50\xd5\xc4\x24\x99\x89\x0c\x89\
\xbc\x43\x4b\x63\x03\x11\x21\xb0\x8f\x0d\xce\x8c\x51\xb6\x4b\xea\
\x85\x3d\xd8\x59\x49\xc1\x5a\xc8\xc2\xaf\xdd\x49\xcb\x5f\xde\x40\
\x22\x93\xa3\x5c\x2e\xcf\x80\x15\x42\xcc\x90\xa0\xb5\x46\x08\x81\
\x69\x9a\x2c\x6a\x69\x36\xae\xba\xfc\x92\x2f\x4e\xa9\x13\xc0\x8a\
\xb9\x6c\x7b\xa3\x09\x30\x7a\x7b\x7b\xff\x46\x6b\xbd\xaf\x5c\x2e\
\x5f\x52\x48\x17\xa2\xfe\x94\x44\x67\xca\x0c\x25\xd3\x84\xc3\x31\
\x16\xd4\x37\x81\x08\x52\xdc\xf9\x3c\xcc\x0a\xc7\x50\xfb\x6a\x6a\
\xae\xb9\x9e\x05\x7f\x71\x33\xbe\xa6\x25\x8c\x8c\x8c\x60\x59\x16\
\x70\x1c\x2c\x1c\x8f\xff\x13\x49\x59\xdc\xb6\x9a\x68\xbc\x61\xd5\
\xda\xb5\x6b\xa7\x7b\x81\x65\x42\x08\xff\x29\x06\xbe\x51\xc8\x07\
\x06\x06\xd6\xf4\xf6\xf6\xee\x74\x1c\xe7\x9f\x73\xb9\x5c\xd8\x4b\
\x7b\xc1\x80\x15\x20\x91\xca\x90\x29\xda\xb4\x36\x2f\x22\x1c\xae\
\x46\x0b\x3f\xde\xf0\x51\xbc\x91\x7e\x22\x57\x7c\x14\xb3\xb6\x11\
\x21\x0c\x1a\xd6\x7f\x9e\x25\xdf\xba\x1b\x7f\x43\x1b\x9e\xe7\x31\
\x3c\x3c\x4c\xb1\x58\x9c\x59\xe1\xe9\x95\x9f\x0d\x7c\xfa\xda\x74\
\xd3\x34\x18\x29\xf1\xe1\xf7\xbf\xef\xba\x29\x93\x7c\x40\xdb\x6c\
\x3b\xcf\x7a\x19\x7c\xe9\xa5\x97\xfc\xf5\xf5\xf5\xdf\x95\x52\x7e\
\xb7\x50\x28\xf8\xec\xbc\xed\x0b\x96\x83\x14\xad\x22\xa9\x74\x8a\
\xba\xea\x3a\xc2\x01\x03\xad\xcb\x68\xe5\x21\xf3\x59\x9c\xae\xa7\
\x08\xbd\xff\xf3\x18\xf5\xf5\xd4\x2e\x5d\x45\xb8\xba\x99\xf8\x7b\
\x3f\x04\x80\xeb\xba\x8c\x8e\x8e\xe2\xba\xee\x0c\xe8\xe9\x64\x37\
\xfd\x19\x2a\xf1\xaf\xb5\x46\x4b\x49\xb9\x77\x13\xf9\xfd\x8f\x50\
\x27\x22\xc4\xeb\x3f\x76\x71\x7b\x7b\x7b\xcd\x91\x23\x47\x32\xc0\
\x72\x21\x44\xaf\xd6\x5a\x4d\xdb\x7b\x56\xab\xc0\xe8\xe8\xe8\xbb\
\x6c\xdb\x7e\xc0\x71\x9c\x16\x2b\x67\xc5\x7c\x8e\x0f\xd3\x36\x19\
\x4b\x8e\x21\xb4\xa6\x76\xe4\x25\x74\xa2\x0f\xac\x14\x58\xa3\x68\
\x2b\x05\xa6\x89\xff\x8a\xeb\xf0\x2f\x6e\xa7\x7e\xd5\x1a\xe2\x6d\
\x6b\x60\x0a\x5c\xb9\x5c\x66\x7c\x7c\x1c\x29\x25\xd3\x76\xea\xa9\
\x7a\x3f\xdb\x6e\xa5\x14\xde\xe4\x21\xb2\xbf\xff\x09\xe5\xb1\x61\
\x3c\x17\x30\x04\xe3\x8b\x3e\xa0\x0e\x8c\xa9\x27\x7f\xf8\xa3\x5b\
\x6f\x9f\xfa\xea\xff\x69\xad\xfb\xa7\xc7\x9d\x15\x0f\xe8\xef\xef\
\x0f\x05\x83\xc1\x7f\x71\x5d\xf7\xeb\x96\x65\x05\xbc\x92\x67\xc4\
\x88\x61\x15\x2c\xc6\x27\xc7\x69\x88\x45\x61\xcb\x8d\xa8\x63\xbb\
\x4e\x1e\x18\x88\x60\x5c\xfd\x55\x22\xab\x2e\x60\xc1\x05\xe7\xe3\
\x8f\x44\x67\xe2\x3a\x9f\xcf\x33\x31\x31\x31\x03\xf4\xc4\x6c\x2f\
\x84\x98\xf9\xbf\x52\x0a\xe5\x96\xc8\xbc\xf0\x53\x26\x3b\xb6\xe3\
\x15\x05\x20\x40\x54\xde\x6b\xc3\xbb\x8c\x85\x2d\x1f\xba\xba\xa1\
\xa1\xe1\xde\x54\x2a\x55\xa2\x92\x0c\x67\x08\x78\xdd\x1e\x90\x4c\
\x26\xaf\x70\x1c\xe7\x7e\xdb\xb6\x6b\x73\xd9\x5c\x34\x44\x08\x9f\
\xf4\x31\x32\x36\x82\xdf\xf4\x13\x37\x15\xe5\x87\xff\x01\x95\x3c\
\x5a\x99\xb0\xf5\x02\x88\x35\xa3\x63\x0d\x88\x65\x6b\x59\x78\xc9\
\xbb\xa9\x5b\xb2\xf0\xa4\x4c\x9e\xcd\x66\xc9\xe5\x72\x33\xd7\xd3\
\xef\x27\xae\xfc\xf4\xe7\xd2\xb1\x9d\x24\x1e\xbf\x15\xd7\xca\x61\
\x84\x40\x18\x0a\x27\x0b\xaa\x64\xe0\x6f\xd5\x44\x9a\xab\xe9\x0f\
\x5e\x2e\xbb\xfa\x72\x0f\xfc\xf4\xf6\xbb\x1e\x9d\x32\xfb\xf7\x5a\
\xeb\x51\x78\x1d\x1e\x90\x4a\xa5\xaa\xb4\xd6\x3f\x74\x1c\xe7\xba\
\x6c\x36\x1b\x54\xae\x12\x75\xc1\x3a\x32\x99\x0c\xe3\xe9\x71\x9a\
\x1a\x9b\x30\x9d\x12\xd6\x9d\x5f\x45\xe7\x12\x33\xe3\x44\xdb\x25\
\xe8\x35\x1f\x22\x56\x1b\xa5\x75\x4d\x0b\x81\xf0\xf1\x3d\x4d\xd7\
\x75\x49\xa7\xd3\xa7\xc4\xfb\x34\x60\xc3\x30\x66\xae\xbd\x62\x9a\
\x89\xae\x5f\x92\xed\xd8\x8a\xca\x39\x04\xd7\x84\xd0\xca\x41\x39\
\x12\x03\x08\x2e\x31\x89\xc6\x63\x04\x83\x21\x5c\x1d\xa7\xa9\x56\
\x2e\x3d\xc1\xfc\x95\xc0\x6b\x27\x20\x9d\x4e\x7f\x58\x29\x75\x77\
\xb1\x58\x8c\x5a\x39\x2b\x54\x15\xaa\xc2\x30\x0c\x8e\x1d\x3b\x46\
\x24\x1c\x61\xc9\xe2\x25\x28\xa9\xd0\xa6\x1f\x73\xd1\x79\x78\x3d\
\xc7\x09\x50\x3b\xee\xa7\xe5\x83\xd7\x52\xb7\x7c\xe9\x49\x6e\x5d\
\x2c\x16\xc9\xe5\x72\x33\x09\x6e\x3a\xa9\x4d\x7f\xe7\xf8\x35\x64\
\x0e\x6d\x63\x62\xef\x26\xbc\xc3\x43\xc8\x44\x09\xfc\x02\x7b\xd8\
\x05\x34\x46\xcc\x47\x55\x4b\x98\x70\xc0\x20\x11\x58\xa3\x8f\x7a\
\x2b\xdc\xee\xee\xee\x9f\xdc\xf3\xc0\x23\xbf\x3b\x01\x42\xbd\x10\
\xa2\x4e\x6b\x3d\xf9\xaa\x08\xb0\x2c\xab\xde\xf3\xbc\xdb\x1c\xc7\
\xf9\xe8\xe4\xe4\x64\xd8\x14\x26\xcd\x75\xcd\xa4\x92\x29\x72\xd9\
\x1c\x2d\x2d\x2d\x98\x86\x89\xf4\x24\x42\x0b\x94\x54\x04\xaf\xfa\
\x32\xde\xe1\x0e\x70\x6d\x44\xb8\x8a\x25\xdf\xbe\x95\xea\x15\xcb\
\x8e\x13\xa2\x14\xb9\x5c\x0e\xdb\xb6\x4f\x02\x0a\x9c\xe2\x01\x4e\
\x76\x9c\xe1\x8e\x0d\xd8\x89\x23\x08\xe1\xa1\x52\x16\x42\x80\x36\
\x05\x98\x06\xfe\xe6\x28\xb1\x58\x00\x33\x10\xa4\x8b\x2b\xa5\x95\
\x49\xef\xf9\xe9\xdd\x77\xdc\x3c\x38\x38\x98\x9b\x05\x45\x03\x55\
\xc0\xe4\x19\xe7\x80\x7c\x3e\xff\x29\xcf\xf3\x6e\xcf\xe5\x72\xc1\
\x7c\x3e\x1f\xaa\x8b\xd7\x81\x82\xa1\xa1\x21\xe2\xd5\x71\xaa\x22\
\x55\x48\x29\x51\x52\xa1\xa4\x42\x7a\x95\xcf\xd2\x95\x78\x7b\x9e\
\xc4\xeb\x78\x80\xb6\xef\xdd\x45\x68\xd1\x8a\x19\x70\x8e\xe3\x60\
\x59\x16\x4a\xa9\x99\x98\x3e\x11\xf0\x34\x41\x5a\xba\x8c\xee\xfa\
\x1d\x93\xcf\x3d\x8c\x1e\x3c\x8a\x00\xbc\x0b\x56\x62\x28\x1b\x59\
\x2e\x21\xa2\x3e\xa2\xd1\x30\x11\xbf\x64\xd4\xb7\x4a\x0f\x7b\x2d\
\xe5\x8e\x1d\x3b\x6f\x79\xf8\x91\xdf\xec\x98\x03\x4a\x16\xe8\xd2\
\x5a\x67\xe0\x0c\x92\x60\xb1\x58\x6c\x51\x4a\xfd\xcc\xb6\xed\xcb\
\x53\xa9\x54\x24\x1c\x0e\x53\x53\x5d\xc3\xe8\xc8\x28\xa5\x52\x89\
\x85\xcd\x0b\x2b\x86\xba\x0a\xad\x34\x9e\xeb\xa1\x95\x46\xba\x12\
\x29\x25\xe1\x58\x98\xba\xd6\x3a\x70\xcb\x98\xe1\xe8\x89\x7a\x29\
\x97\xcb\xa7\x24\xb5\x13\x09\xd0\x5a\x93\x1d\x19\x60\xe8\x99\x5f\
\xc1\x9e\xed\xe8\x82\x85\x16\x06\xb2\xb1\x01\x1d\xf4\x21\xe3\x61\
\x82\xb1\x30\xb1\x88\x0f\x65\x86\xe8\x91\x17\xc8\xcc\xe4\xf8\x8b\
\xb7\xde\xf9\xe0\x8f\x13\x89\x44\x71\x16\x14\x05\x1c\x00\x0e\x9d\
\xb8\x4d\xf6\x8a\x21\x50\x2e\x97\xbf\xac\xb5\xfe\x51\x36\x9b\xf5\
\x17\x0a\x85\x40\x73\x73\x33\x4a\x29\x8e\xf6\x1f\xa5\xa1\xae\x81\
\xa6\xc6\xa6\x99\x55\x17\x42\xa0\x50\x95\xae\x5b\x80\x16\x9a\x68\
\x6d\x94\xfa\x96\xfa\xa9\x99\x2a\x25\xce\xf3\x3c\x8a\xc5\x22\x52\
\xca\x99\xa4\x36\xbb\xad\x05\xf0\x6c\x9b\xfe\x97\x3a\xb0\x5e\x78\
\x14\x23\x3d\x82\x76\xbd\x4a\xdb\xaa\x15\x48\x8d\x6c\x68\xa2\xaa\
\x26\x46\xd0\xf4\x18\x62\x89\x9a\xb0\x63\xc5\xad\xdb\xb6\xdd\xb4\
\x71\xd3\x13\xbb\xe6\x80\x32\x41\xa5\xfe\x5b\xb3\x6f\xbc\x9c\x07\
\xb4\x95\x4a\xa5\xfb\x1c\xc7\x79\x47\x22\x91\x88\x46\xa3\x51\x6a\
\x6a\x6a\x18\x19\x19\x41\x4a\xc9\x82\xe6\x05\x08\x04\x9e\xeb\x4d\
\xd5\x61\x35\x43\x84\xf4\xe4\x8c\xfb\x6b\xa5\x69\x5d\xd5\x8a\x69\
\x9a\x00\xd8\xb6\x8d\xe3\x38\x73\xae\xf4\x74\xf2\x53\x4a\x31\xda\
\x77\x84\x81\xee\x9d\xf8\xbb\x1e\xc7\x9f\x3c\x8a\xbd\x70\x25\xfe\
\xd1\x5e\x64\x20\x88\xdd\xda\x8e\x7f\x51\x0b\xb1\xb0\x89\x2d\x02\
\x1c\x76\x5b\x65\x2a\x31\xfa\xfc\x6d\x77\xdc\x73\xc7\xd4\x66\xe8\
\x89\xe2\x01\xfb\xb4\xd6\x47\x5e\x6e\x91\x4f\x21\x40\x4a\xf9\xe7\
\x9e\xe7\xfd\x2c\x9d\x4e\x9b\x85\x42\xc1\xdf\xd8\xd8\x88\xe3\x38\
\x8c\x8e\x8e\xd2\xd4\xd4\x44\x2c\x16\xab\x80\x9e\x8a\x75\xcf\xf3\
\x2a\x04\x94\x0a\x58\x2f\x6e\x21\x7c\xf1\x47\x2a\x71\xef\x79\x68\
\xa9\x89\x37\xc6\x89\x37\xc4\x71\x1c\xa7\x32\xee\x84\x78\x3f\xb1\
\xae\x2b\xa5\xc8\x67\xf3\x1c\xde\xb9\x97\xcc\xf8\x08\xd1\x3d\x8f\
\xe1\x4f\x1c\x02\x04\x1a\x28\x2d\x5a\x83\xbd\xa8\x9d\x78\x75\x84\
\x80\xe9\x31\xe4\xd5\xca\xb4\x6d\xe4\x36\x3f\xf5\xdc\x8d\xcf\x3c\
\xf3\xcc\x81\x39\xb0\x8d\x03\xdd\x5a\xeb\xd9\xa1\x70\x92\x9c\x12\
\x02\x52\xca\x92\x6d\xdb\xd2\xb2\xac\xd0\xe2\xc5\x8b\x19\x1c\x1c\
\x44\x08\x41\x5b\x5b\xdb\x8c\xa1\x15\xea\xc0\xcd\xa7\xb1\x3a\x36\
\x81\x08\x50\xe8\xdc\x8c\xb9\x60\x19\xea\x5d\x1f\x44\x4f\x85\x81\
\x42\x91\x1a\x4f\x11\x8a\x85\xa6\x9a\xb3\xe3\xe5\x6d\x7a\xc5\xb5\
\xd6\x48\x4f\xd2\xdf\xd3\x4f\xff\xfe\x7e\xa4\xeb\x10\x3b\xf4\x0c\
\xa1\xe4\x61\x14\xa0\x4c\x3f\xd6\x92\x77\xe0\x6b\x7f\x3b\x75\x41\
\x83\xa2\x86\x43\x76\x44\x8e\x8d\x0c\xff\xf6\xb6\xdb\xef\xfa\x45\
\x3e\x9f\x9f\x7d\x02\xe4\x00\x7b\xb4\xd6\x83\x9c\x81\x9c\x42\x40\
\x20\x10\xd8\x58\x2a\x95\xd2\x5a\xeb\xe8\x74\x69\xaa\xad\xad\x3d\
\x09\xbc\x52\x0a\x55\xb4\x28\x74\x3c\x4e\xf8\x82\xcb\x71\xc6\x87\
\x11\xa6\x41\xf8\x3d\x1f\xc5\xda\xf0\x03\x22\x1f\xfb\xbb\x0a\x30\
\x25\x71\xca\x0e\x99\x54\x86\xba\xe6\xba\x93\xfa\xf7\x69\x22\xd2\
\xa9\x34\x87\x3b\xf7\x20\xbb\x37\x53\x95\x3e\x86\xb5\x78\x1d\x32\
\x50\x85\x36\x0c\x9c\x68\x13\xd6\xea\x2b\xa9\x6e\x6a\xc6\x34\x5c\
\xfa\x4b\x48\xab\xec\x4e\x6e\xda\xb2\xe9\x86\xe7\x9f\x7f\x7e\xae\
\x43\x8f\x61\x60\x97\xd6\xda\x3e\x13\xf0\xf0\x32\x39\x20\x9d\x4e\
\x7f\xb5\x50\x28\xdc\x50\x2e\x97\x63\x4d\x4d\x4d\x0c\x0f\x0f\xd3\
\xda\xda\x7a\x92\xdb\x66\x9f\x7e\x10\x59\xca\x63\x0f\x1c\xa2\xb4\
\x77\x3b\xb1\xab\x3f\x87\x6f\xd9\xf9\x78\xe5\x22\xce\xbe\xdf\x23\
\x96\x5d\x88\x5e\x71\x31\x9e\xe3\x01\xb0\xfc\xbc\xe5\x08\xe3\x78\
\xbb\xeb\xd8\x0e\x7d\x7b\xfb\x48\x6d\xdf\x42\x68\xf7\xa3\x18\xa5\
\x2c\x4e\x75\x0b\xe3\x6b\xbf\x84\x2b\x35\x3e\x7b\x82\x68\x5d\x2d\
\x61\xbf\x81\xe5\x95\x18\x29\xda\x72\x68\x70\xe0\x37\xff\x73\xe7\
\x5d\x0f\x96\x4a\xa5\xd9\x47\x5e\x65\x2a\xee\x3e\x7a\xa6\xc0\xa7\
\x65\xce\x2a\x50\x28\x14\xee\x16\x42\xfc\x20\x97\xcb\xd1\xdc\xdc\
\x8c\x61\x18\xb8\xae\x3b\xb3\x6a\xe5\xc3\xdd\xf8\x16\xb6\x31\x71\
\xcb\xdf\x62\xd6\x35\xe3\x5b\xb0\x0c\x55\xb6\xc8\xde\xf7\x4f\xa8\
\xdc\x04\x00\xe1\x0b\xaf\x42\x53\x71\x7b\xd7\x71\x99\x4c\x4c\xd2\
\xd8\xd2\x88\x52\x8a\xe4\x48\x92\x23\xdd\xbb\x11\xdb\xee\x21\x3a\
\xdc\x83\xf6\x6c\xca\x0b\xce\x67\x72\xf5\x47\x90\xc2\xc0\xf4\x0b\
\xe2\x75\xed\x18\x86\xc7\xd1\xec\xb8\x57\x28\x15\x13\x8f\x6e\xdc\
\x78\xc3\x8e\x1d\x3b\xe6\x72\xeb\x7e\x60\xef\x99\x1e\x86\x9e\x11\
\x01\x8b\x16\x2d\x2a\x0d\x0d\x0d\xdd\x1c\x0a\x85\xbe\x35\x36\x36\
\x16\x6d\x6a\x6a\x22\x95\x4a\x51\x5f\x5f\x8f\x52\x0a\x67\x62\x0c\
\xff\xe2\x55\x68\xd7\xc6\x1b\xaf\xd8\xe4\x0e\x56\xf2\x90\x08\x86\
\xf1\x5d\x78\x15\x46\xeb\x4a\xa4\xeb\xa2\xb4\x42\x69\x45\x62\x38\
\x41\xb4\x2a\x4a\xff\xe1\x7e\x26\xf7\x77\x13\x79\xf6\x36\x8a\x6d\
\xef\x81\xfa\x3c\xd6\xe2\xcb\x28\x34\xac\x40\x79\x8a\x78\x55\x8c\
\xa0\x2f\xc8\x44\x61\x82\x64\x21\xe9\xf5\x1d\xe9\x7b\xe8\xae\xbb\
\xee\xfa\xb5\xe3\x38\x6a\x96\x99\x05\x2a\xa5\xed\x65\xb7\xbc\x5f\
\x33\x01\x00\x8e\xe3\xfc\xd8\x34\xcd\xbf\x4f\x24\x12\xb4\xb4\xb4\
\x20\x65\x25\xb3\xcf\x3c\x8a\x06\x2b\x7b\x8c\xc2\x30\xf1\xb5\x9d\
\x87\x88\xd5\x10\xbc\xfc\x93\xc8\xc9\x04\xe2\x6d\xeb\x2a\xa5\x50\
\x29\xf4\xd4\xcb\x75\x5d\xf6\x74\xed\x81\xe4\x10\xd1\xa7\x6e\x06\
\xbb\x40\xb8\xff\x45\x26\x2f\xbe\x0e\x3b\xb6\x90\x90\xe9\x23\x16\
\x8d\xa1\xa4\xa2\x37\xd1\xeb\xe5\x8b\xf9\x63\x1b\x1e\xde\x70\x63\
\x77\x77\xf7\x6c\xb7\xd6\x40\x2f\xb0\xff\x4c\x4e\x7f\x5f\x33\x01\
\xcb\x97\x2f\x4f\xec\xdd\xbb\xf7\x7e\x9f\xcf\xf7\x99\xb1\xb1\xb1\
\x70\x63\x63\x23\x99\x4c\x86\x68\x34\x8a\x51\x5d\x8f\xd3\xbf\x1f\
\x84\xa0\xea\xd3\xdf\xc6\x5c\xd8\x8e\x3b\x39\x8e\xb2\x26\x31\xda\
\xdf\x89\xd2\x1a\xa5\x4f\x68\x6f\x75\x85\x08\x23\x3b\x8e\x7f\xf3\
\x7f\x22\xec\x02\x1a\x50\xca\xc3\xf0\x6c\x6a\x6b\x6b\x09\xfa\x83\
\x0c\x4f\x0c\xab\xb1\xf4\x98\x3c\x70\xf0\xc0\x3d\x3f\xff\xf9\xcf\
\x9f\x90\x52\xce\x4e\x50\x39\x2a\x6d\x6c\xfa\xf5\x02\x3f\x2d\x01\
\x00\x4a\xa9\x1f\xf8\x7c\xbe\xcf\x0e\x0d\x0d\xd1\xd2\xd2\x42\x32\
\x99\x44\x4a\x89\xd9\x76\x3e\x99\x5b\xfe\x9a\xd8\xc7\xbf\x81\xa8\
\x69\x46\x79\x0e\x66\x4b\x3b\xce\xc1\x2e\x44\x30\x7c\xd2\xea\x4b\
\x55\x59\x24\x85\xc2\xdf\xf9\x6b\xcc\x92\x85\x12\x50\x5e\xfa\x6e\
\xec\xf7\x7c\x86\xba\xfa\x05\x94\xcb\x65\x76\xf7\xef\x76\x2d\xcb\
\xea\x7b\xf0\x57\x0f\xfe\xd7\xbe\x7d\xfb\x66\xbb\xb5\x02\x0e\x52\
\x69\x63\x67\x87\xc2\xeb\x92\xd3\x3e\x0b\x74\x76\x76\x6e\xb1\x2c\
\xeb\xaa\xf6\xf6\x76\x7f\x34\x1a\xc5\xb2\x2c\xfc\x7e\x3f\x85\xa7\
\x1f\x00\xc3\x44\xfb\xfc\x68\x61\x60\x54\xd5\x21\x56\xbe\xbb\xd2\
\x18\x79\x0a\xd7\x75\x2b\x8d\x92\xeb\x21\x3d\x89\xe7\x7a\xb8\xe5\
\x12\xbe\xae\x4d\x78\xf1\x16\xc2\xe7\xbf\x8f\x80\x3f\x40\xdf\x60\
\x9f\x1c\x4e\x0e\xcb\xdd\xbb\x77\xdf\x71\xef\xbd\xf7\x3e\x33\x87\
\x09\x93\x54\x56\xfd\x94\x36\xf6\x4d\x21\xa0\xa3\xa3\xe3\x0a\xd7\
\x75\x37\xbb\xae\x1b\xb9\xec\xb2\xcb\x18\x18\x18\x20\x18\x0c\xa2\
\x95\x44\x69\x90\xae\x06\xa1\x51\xba\xb2\x6f\x37\x03\xf8\x04\x22\
\x66\x08\x70\x5c\x22\xe1\x08\x55\xb1\x2a\xf2\xf9\x3c\x5d\x3d\x5d\
\x6e\x26\x93\xd9\x7f\xdf\xfd\xf7\xfd\xa8\xb7\xb7\x77\xb6\x5b\x4b\
\x60\x1f\x70\xda\x33\xfe\x37\x94\x00\x80\x67\x9f\x7d\x76\x97\x65\
\x59\x17\x5c\x74\xd1\x45\xc2\xef\xf7\x63\x59\xd6\x4c\x32\x9c\xd8\
\xd6\x83\xeb\x4a\xea\x2e\x5b\x8d\x52\xc7\x9f\x09\x5c\xd7\x45\xba\
\xb2\xe2\x09\x9e\x42\x20\xa8\xa9\xa9\x21\xe4\x0f\xb1\xab\x67\x97\
\x37\x3c\x36\xec\x76\xbe\xd8\x79\xdb\x43\x0f\x3d\xb4\x7d\x8e\x29\
\x13\x54\x32\xfc\x2b\xb6\xb1\x67\x43\xce\x68\x43\x44\x29\xf5\xaf\
\x3e\x9f\xef\x8e\x43\x87\x0e\x55\xaf\x5b\xb7\x6e\x66\xe7\x46\x29\
\x85\xb3\xaf\x03\x99\xcb\xa2\x2e\x5d\x59\x01\xaf\x14\xae\xe7\xce\
\xdc\x47\x43\x3c\x1e\x27\x16\x8b\x91\xc9\x64\xd8\xda\xb1\xd5\xcb\
\xa4\x33\x2f\xdd\xfd\x8b\xbb\x7f\x32\xc7\x46\x85\x0b\xec\x3e\xd3\
\x36\xf6\x6c\xc8\x19\x11\x90\x4e\xa7\x1f\x09\x06\x83\x37\x27\x12\
\x89\xea\x6c\x36\x4b\x24\x12\xa9\xec\xe2\x58\x0e\x66\xff\xff\x12\
\x92\xe3\x4c\x76\x7d\x84\xe8\x79\x0b\x66\x1e\x76\xa4\x94\x18\xa6\
\x41\x43\x43\x03\xe1\x60\x98\x8e\x1d\x1d\xee\xe8\xd8\xa8\xbd\x6d\
\xfb\xb6\xff\x7e\xec\xb1\xc7\x5e\x9c\x63\x9a\x57\xdd\xc6\x9e\x0d\
\x39\xa3\x93\xa1\x6b\xaf\xbd\x56\x2a\xa5\x7e\x60\x9a\x66\xba\xa7\
\xa7\x87\xba\xba\x3a\x4c\xd3\x24\xbd\x63\x3f\x41\x35\x8c\x29\x5c\
\xdc\xce\xa7\xf1\x3c\x0f\xcf\xf3\x90\x52\x12\xaf\x89\xb3\x78\xc9\
\x62\xf2\x85\x3c\x8f\x6e\x7a\xd4\xdb\xdb\xb3\xb7\xe3\x86\x1b\x6f\
\xf8\xca\x1c\xe0\xcb\xc0\x0e\xad\x75\xe7\x9b\x0d\x1e\x5e\xc5\xb6\
\xf8\x86\x0d\x1b\x62\x9e\xe7\x8d\x16\x8b\xc5\xd8\xfa\xf5\xeb\x29\
\x16\x8b\x24\xc6\x93\x58\x7d\x29\xe4\xc8\x00\x85\xb1\x71\xe4\xda\
\x0b\x21\x50\x09\x83\x42\xa1\xe0\x64\xb3\x59\x1c\xc7\x29\x3e\xf7\
\xdc\x73\x37\x3f\xf1\xc4\x13\xbb\xe7\x50\x3b\x40\xe5\xc9\xed\x35\
\xb5\xb1\x67\x43\x5e\xd5\xb9\xc0\xbd\xf7\xde\xfb\xef\xae\xeb\x7e\
\xa9\xb1\xb1\x31\x57\x53\x53\xb3\x62\x78\x78\x58\x67\x32\x19\xaf\
\x58\x2c\xfa\xa6\xea\x73\xce\xb6\xed\xd1\xf1\xf1\xf1\xfd\xc9\x64\
\x72\x68\x64\x64\x64\xa4\xbb\xbb\x7b\x30\x9b\xcd\x3a\xb3\x54\x9d\
\x95\x36\xf6\x6c\xc8\xab\xda\x15\xb6\x6d\xfb\x66\x80\x91\x91\x91\
\x45\xc9\x64\x72\x5b\x2e\x97\xdb\x27\xa5\xdc\xb7\x61\xc3\x06\x6f\
\xc7\x8e\x1d\xb1\xd3\x0c\x97\x54\x3a\xb9\x24\x70\xe0\x6c\xb4\xb1\
\x67\x43\xce\xca\xd9\xa0\x10\x22\x0a\xbc\x9f\xca\x8e\xa0\x0b\x58\
\x27\xfc\xe5\x00\xeb\xcd\x28\x69\xaf\x45\xce\xda\xe1\xa8\x10\xa2\
\x16\x28\x6b\xad\x4b\x67\x45\xe1\x9b\x24\xe7\xfc\x6f\x85\xdf\x68\
\x39\x27\x7e\x27\x38\x9f\xf2\x16\x01\xf3\x6d\xc0\x7c\xcb\x5b\x04\
\xcc\xb7\x01\xf3\x2d\x6f\x11\x30\xdf\x06\xcc\xb7\xfc\xd1\x13\xf0\
\xff\xdb\x1a\x8f\x55\x76\x8a\x2b\x80\x00\x00\x00\x00\x49\x45\x4e\
\x44\xae\x42\x60\x82\
\x00\x00\x07\x8d\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@ -1778,6 +2070,10 @@ qt_resource_data = "\
"
qt_resource_name = "\
\x00\x04\
\x00\x06\x98\x84\
\x00\x63\
\x00\x61\x00\x72\x00\x64\
\x00\x06\
\x07\x8b\x7a\xc2\
\x00\x72\
@ -1817,16 +2113,17 @@ qt_resource_name = "\
"
qt_resource_struct = "\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x01\
\x00\x00\x00\x24\x00\x00\x00\x00\x00\x01\x00\x00\x07\x91\
\x00\x00\x00\x12\x00\x02\x00\x00\x00\x05\x00\x00\x00\x05\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x00\x38\x00\x00\x00\x00\x00\x01\x00\x00\x0d\xd8\
\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x00\x68\xd3\
\x00\x00\x00\x58\x00\x00\x00\x00\x00\x01\x00\x00\x59\x89\
\x00\x00\x00\xa6\x00\x00\x00\x00\x00\x01\x00\x00\x68\x02\
\x00\x00\x00\x70\x00\x00\x00\x00\x00\x01\x00\x00\x5b\xa1\
\x00\x00\x00\x8e\x00\x00\x00\x00\x00\x01\x00\x00\x64\x5d\
\x00\x00\x00\x32\x00\x00\x00\x00\x00\x01\x00\x00\x19\xab\
\x00\x00\x00\x20\x00\x02\x00\x00\x00\x05\x00\x00\x00\x06\
\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x12\x1a\
\x00\x00\x00\x46\x00\x00\x00\x00\x00\x01\x00\x00\x1f\xf2\
\x00\x00\x00\xca\x00\x00\x00\x00\x00\x01\x00\x00\x7a\xed\
\x00\x00\x00\x66\x00\x00\x00\x00\x00\x01\x00\x00\x6b\xa3\
\x00\x00\x00\xb4\x00\x00\x00\x00\x00\x01\x00\x00\x7a\x1c\
\x00\x00\x00\x7e\x00\x00\x00\x00\x00\x01\x00\x00\x6d\xbb\
\x00\x00\x00\x9c\x00\x00\x00\x00\x00\x01\x00\x00\x76\x77\
"
def qInitResources():

View File

@ -20,7 +20,7 @@ from database import LibraryDatabase
from editbook import EditBookDialog
from PyQt4.QtCore import Qt, SIGNAL
from PyQt4.Qt import QObject, QThread, QCoreApplication, QEventLoop, QString, QStandardItem, QStandardItemModel, QStatusBar, QVariant, QAbstractTableModel, \
from PyQt4.Qt import QObject, QThread, QCoreApplication, QEventLoop, QString, QTreeWidgetItem, QStandardItemModel, QStatusBar, QVariant, QAbstractTableModel, \
QAbstractItemView, QImage, QPixmap, QIcon, QSize, QMessageBox, QSettings, QFileDialog, QErrorMessage, QDialog, QSpinBox,\
QPainterPath, QItemDelegate, QPainter, QPen, QColor, QLinearGradient, QBrush, QStyle,\
qInstallMsgHandler, qDebug, qFatal, qWarning, qCritical
@ -31,7 +31,7 @@ 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, attrgetter
from math import sin, cos, pi
DEFAULT_BOOK_COVER = None
@ -309,18 +309,15 @@ class LibraryBooksModel(QAbstractTableModel):
self.db.commit()
class DeviceBooksModel(QAbstractTableModel):
TIME_READ_FMT = "%a, %d %b %Y %H:%M:%S %Z"
def __init__(self, parent, data):
QAbstractTableModel.__init__(self, parent)
self._data = data
self._orig_data = data
self.image_file = None
def set_data(self, data):
def __init__(self, parent):
QAbstractTableModel.__init__(self, parent)
self._data = []
self._orig_data = []
def set_data(self, book_list):
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self._data = data
self._orig_data = data
self.sort(0, Qt.DescendingOrder)
self._data = book_list
self._orig_data = book_list
def rowCount(self, parent): return len(self._data)
def columnCount(self, parent): return 4
@ -341,10 +338,10 @@ class DeviceBooksModel(QAbstractTableModel):
if role == Qt.DisplayRole:
row, col = index.row(), index.column()
book = self._data[row]
if col == 0: text = book["title"]
elif col == 1: text = book["author"]
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))
if col == 0: text = wrap(book.title, width=40)
elif col == 1: text = re.sub("&\s*","\n", book.author)
elif col == 2: text = human_readable(book.size)
elif col == 3: text = time.strftime(TIME_WRITE_FMT, book.datetime)
return QVariant(text)
elif role == Qt.TextAlignmentRole and index.column() in [2,3]:
return QVariant(Qt.AlignRight)
@ -353,7 +350,7 @@ class DeviceBooksModel(QAbstractTableModel):
def info(self, row):
row = self._data[row]
try:
cover = row["thumbnail"]
cover = row.thumbnail
pix = QPixmap()
self.image_file = NamedTemporaryFile()
self.image_file.write(cover)
@ -363,14 +360,14 @@ class DeviceBooksModel(QAbstractTableModel):
except Exception, e:
self.image_file = None
cover = DEFAULT_BOOK_COVER
return row["title"], row["author"], human_readable(int(row["size"])), row["mime"], cover
return row.title, row.author, human_readable(row.size), row.mime, cover
def sort(self, col, order):
def getter(key, func): return lambda x : func(itemgetter(key)(x))
def getter(key, func): return lambda x : func(attrgetter(key)(x))
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 == 3: key, func = "date", lambda x: time.mktime(time.strptime(x, self.TIME_READ_FMT))
if col == 3: key, func = "datetime", lambda x: x
descending = order != Qt.AscendingOrder
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self._data.sort(key=getter(key, func))
@ -385,7 +382,7 @@ class DeviceBooksModel(QAbstractTableModel):
for book in self._orig_data:
match = True
for q in queries:
if q in book["title"].lower() or q in book["author"].lower(): continue
if q in book.title.lower() or q in book.author.lower(): continue
else:
match = False
break
@ -425,43 +422,34 @@ class MainWindow(QObject, Ui_MainWindow):
self.current_view = self.device_view
else:
self.device_view.hide(), self.library_view.show()
self.current_view = self.device_view
self.current_view = self.library_view
self.current_view.sortByColumn(3, Qt.DescendingOrder)
def tree_clicked(self, index):
show_device = self.show_device
item = self.tree.itemFromIndex(index)
text = str(item.text())
if text == "Library":
show_device(False)
elif text == "SONY Reader":
show_device(True)
self.set_device_data(self.main_books + self.card_books)
elif text == "Main Memory":
show_device(True)
self.set_device_data(self.main_books)
elif text == "Storage Card":
show_device(True)
self.set_device_data(self.card_books)
elif text == "Books":
text = str(item.parent().text())
if text == "Library":
show_device(False)
elif text == "Main Memory":
show_device(True)
self.set_device_data(self.main_books)
elif text == "Storage Card":
show_device(True)
self.set_data(self.card_books)
def tree_clicked(self, item, col):
if item:
text = str(item.text(0))
if text == "Books": text = str(item.parent().text(0))
if "Library" in text:
self.show_device(False)
elif "SONY Reader" in text:
self.device_view.setModel(self.reader_model)
QObject.connect(self.device_view.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.show_book)
self.show_device(True)
elif "Storage Card" in text:
self.device_view.setModel(self.card_model)
QObject.connect(self.device_view.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.show_book)
self.show_device(True)
def set_device_data(self, data):
self.device_model.set_data(data)
model.set_data(data)
self.device_view.resizeColumnsToContents()
def model_modified(self):
if self.library_view.isVisible(): view = self.library_view
else: view = self.device_view
view.clearSelection()
view.clearSelection()
view.resizeColumnsToContents()
self.book_cover.hide()
self.book_info.hide()
@ -518,7 +506,7 @@ class MainWindow(QObject, Ui_MainWindow):
else: self.cache_xml = file
for path in paths:
self.dev.del_file(path)
self.device_model.delete_by_path(path)
model.delete_by_path(path)
self.cache_xml.seek(0)
self.main_xml.seek(0)
self.status("Files deleted. Updating media list on device")
@ -588,11 +576,11 @@ class MainWindow(QObject, Ui_MainWindow):
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, log_packets):
QObject.__init__(self)
Ui_MainWindow.__init__(self)
self.dev = device(report_progress=self.progress)
self.dev = device(report_progress=self.progress, log_packets=log_packets)
self.is_connected = False
self.setupUi(window)
self.card = None
@ -618,52 +606,58 @@ class MainWindow(QObject, Ui_MainWindow):
QObject.connect(self.library_model, SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.resize_columns)
self.library_view.resizeColumnsToContents()
# Create Device list
self.tree = QStandardItemModel()
library = QStandardItem(QString("Library"))
library.setIcon(QIcon(":/library"))
font = library.font()
font.setBold(True)
self.tree.appendRow(library)
library.setFont(font)
library.appendRow(QStandardItem(QString("Books")))
blank = QStandardItem(" ")
blank.setEnabled(False)
self.tree.appendRow(blank)
self.reader = QStandardItem(QString("SONY Reader"))
mm = QStandardItem(QString("Main Memory"))
mm.appendRow(QStandardItem(QString("Books")))
self.reader.appendRow(mm)
mc = QStandardItem(QString("Storage Card"))
mc.appendRow(QStandardItem(QString("Books")))
self.reader.appendRow(mc)
self.reader.setIcon(QIcon(":/reader"))
self.tree.appendRow(self.reader)
self.reader.setFont(font)
self.treeView.setModel(self.tree)
self.treeView.header().hide()
self.treeView.setExpanded(library.index(), True)
self.treeView.setExpanded(self.reader.index(), True)
self.treeView.setExpanded(mm.index(), True)
self.treeView.setExpanded(mc.index(), True)
self.treeView.setRowHidden(2, self.tree.invisibleRootItem().index(), True)
QObject.connect(self.treeView, SIGNAL("activated(QModelIndex)"), self.tree_clicked)
QObject.connect(self.treeView, SIGNAL("clicked(QModelIndex)"), self.tree_clicked)
# Create Device tree
QObject.connect(self.device_tree, SIGNAL("itemClicked ( QTreeWidgetItem *, int )"), self.tree_clicked)
QObject.connect(self.device_tree, SIGNAL("itemActivated ( QTreeWidgetItem *, int )"), self.tree_clicked)
self.device_tree.header().hide()
library = QTreeWidgetItem(self.device_tree, QTreeWidgetItem.Type)
library.setData(0, Qt.DisplayRole, QVariant("Library"))
library.setData(0, Qt.DecorationRole, QVariant(QIcon(":/library")))
books =QTreeWidgetItem(library, QTreeWidgetItem.Type)
books.setData(0, Qt.DisplayRole, QVariant("Books"))
self.device_tree.expandItem(library)
buffer = QTreeWidgetItem(self.device_tree, QTreeWidgetItem.Type)
buffer.setFlags(Qt.ItemFlags())
library = QTreeWidgetItem(self.device_tree, QTreeWidgetItem.Type)
library.setData(0, Qt.DisplayRole, QVariant("SONY Reader"))
library.setData(0, Qt.DecorationRole, QVariant(QIcon(":/reader")))
books =QTreeWidgetItem(library, QTreeWidgetItem.Type)
books.setData(0, Qt.DisplayRole, QVariant("Books"))
self.device_tree.expandItem(library)
buffer = QTreeWidgetItem(self.device_tree, QTreeWidgetItem.Type)
buffer.setFlags(Qt.ItemFlags())
library = QTreeWidgetItem(self.device_tree, QTreeWidgetItem.Type)
library.setData(0, Qt.DisplayRole, QVariant("Storage Card"))
library.setData(0, Qt.DecorationRole, QVariant(QIcon(":/card")))
books =QTreeWidgetItem(library, QTreeWidgetItem.Type)
books.setData(0, Qt.DisplayRole, QVariant("Books"))
self.device_tree.expandItem(library)
self.device_tree.reader = self.device_tree.topLevelItem(2)
self.device_tree.card = self.device_tree.topLevelItem(4)
def hider(i):
def do(s, x): s.topLevelItem(i).setHidden(x), s.topLevelItem(i+1).setHidden(x)
return do
self.device_tree.hide_reader = hider(1)
self.device_tree.hide_card = hider(3)
self.device_tree.hide_reader(self.device_tree, True)
self.device_tree.hide_card(self.device_tree, True)
# Create Device Book list
self.device_model = DeviceBooksModel(window, [])
self.device_view.setModel(self.device_model)
self.reader_model = DeviceBooksModel(window)
self.card_model = DeviceBooksModel(window)
self.device_view.setModel(self.reader_model)
self.device_view.setSelectionBehavior(QAbstractItemView.SelectRows)
self.device_view.setSortingEnabled(True)
self.device_view.contextMenuEvent = self.list_context_event
QObject.connect(self.device_model, SIGNAL("layoutChanged()"), self.device_view.resizeRowsToContents)
QObject.connect(self.device_view.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.show_book)
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)
QObject.connect(self.device_model, SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.resize_columns)
self.device_view.hide()
for model in (self.reader_model, self. card_model):
QObject.connect(model, SIGNAL("layoutChanged()"), self.device_view.resizeRowsToContents)
QObject.connect(self.search, SIGNAL("textChanged(QString)"), model.search)
QObject.connect(model, SIGNAL("sorted()"), self.model_modified)
QObject.connect(model, SIGNAL("searched()"), self.model_modified)
QObject.connect(model, SIGNAL("deleted()"), self.model_modified)
QObject.connect(model, SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.resize_columns)
# Setup book display
self.BOOK_TEMPLATE = self.book_info.text()
@ -679,6 +673,7 @@ class MainWindow(QObject, Ui_MainWindow):
self.device_detector = self.startTimer(1000)
self.splitter.setStretchFactor(1,100)
self.search.setFocus(Qt.OtherFocusReason)
self.show_device(False)
window.show()
def timerEvent(self, e):
@ -692,13 +687,13 @@ class MainWindow(QObject, Ui_MainWindow):
def device_removed(self, timeout=False):
""" @todo: only reset stuff if library is not shown """
self.is_connected = False
self.df.setText("Main memory: <br><br>Storage card:")
self.card = None
self.treeView.setRowHidden(2, self.tree.invisibleRootItem().index(), True)
self.device_model.set_data([])
self.df.setText("SONY Reader: <br><br>Storage card:")
self.device_tree.hide_reader(self.device_tree, True)
self.device_tree.hide_card(self.device_tree, True)
self.book_cover.hide()
self.book_info.hide()
self.device_view.hide()
self.library_view.show()
def timeout_error(self):
@ -715,6 +710,7 @@ class MainWindow(QObject, Ui_MainWindow):
self.progress_bar.setMaximum(100)
self.progress_bar.reset()
self.progress_bar.setFormat(msg + ": %p%")
self.progress(0)
QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
def establish_connection(self):
@ -722,39 +718,38 @@ class MainWindow(QObject, Ui_MainWindow):
self.status("Connecting to device")
try:
space = self.dev.available_space()
except TimeoutError:
c = 0
self.status("Waiting for device to initialize")
while c < 100: # Delay for 10s while device is initializing
if c % 10 == c/10:
self.progress(c)
QThread.currentThread().msleep(100)
c += 1
space = self.dev.available_space()
except DeviceError:
self.dev.reconnect()
return
except ProtocolError, e:
traceback.print_exc(e)
print >> sys.stderr, "Unable to connect device. Please try unplugiing and reconnecting it"
qFatal("Unable to connect device. Please try unplugiing and reconnecting it")
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("SONY Reader: " + human_readable(space[0][1]) + "<br><br>Storage card: " + human_readable(sc))
self.is_connected = True
if space[1][2] > 0: self.card = "a:"
elif space[2][2] > 0: self.card = "b:"
else: self.card = None
if self.card: self.treeView.setRowHidden(1, self.reader.index(), False)
else: self.treeView.setRowHidden(1, self.reader.index(), True)
self.treeView.setRowHidden(2, self.tree.invisibleRootItem().index(), False)
self.status("Loading media list from device")
mb, cb, mx, cx = self.dev.books()
for x in (mb, cb):
for book in x:
if "&" in book["author"]:
book["author"] = re.sub(r"&\s*", r"\n", book["author"])
self.main_books = mb
self.card_books = cb
self.main_xml = mx
self.cache_xml = cx
if space[1][2] > 0: card = "a:"
elif space[2][2] > 0: card = "b:"
else: card = None
if card: self.device_tree.hide_card(self.device_tree, False)
else: self.device_tree.hide_card(self.device_tree, True)
self.device_tree.hide_reader(self.device_tree, False)
self.status("Loading media list from SONY Reader")
self.reader_model.set_data(self.dev.books())
if card: self.status("Loading media list from Storage Card")
self.card_model.set_data(self.dev.books(oncard=True))
self.progress(100)
self.window.setCursor(Qt.ArrowCursor)
def main():
from optparse import OptionParser
from libprs500 import __version__ as VERSION
parser = OptionParser(usage="usage: %prog [options]", version=VERSION)
parser.add_option("--log-packets", help="print out packet stream to stdout. "+\
"The numbers in the left column are byte offsets that allow the packet size to be read off easily.", \
dest="log_packets", action="store_true", default=False)
options, args = parser.parse_args()
from PyQt4.Qt import QApplication, QMainWindow
app = QApplication(sys.argv)
global DEFAULT_BOOK_COVER
@ -771,7 +766,7 @@ def main():
handler = QErrorMessage.qtHandler()
handler.resize(600, 400)
handler.setModal(True)
gui = MainWindow(window)
gui = MainWindow(window, options.log_packets)
ret = app.exec_()
return ret

View File

@ -53,36 +53,30 @@
<number>6</number>
</property>
<item>
<widget class="QTreeView" name="treeView" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>1</hsizetype>
<vsizetype>7</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops" >
<bool>true</bool>
</property>
<property name="frameShadow" >
<enum>QFrame::Plain</enum>
</property>
<property name="verticalScrollBarPolicy" >
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="autoScroll" >
<bool>false</bool>
<widget class="QTreeWidget" name="device_tree" >
<property name="dragDropMode" >
<enum>QAbstractItemView::DropOnly</enum>
</property>
<property name="alternatingRowColors" >
<bool>true</bool>
</property>
<property name="indentation" >
<number>15</number>
<property name="rootIsDecorated" >
<bool>false</bool>
</property>
<property name="uniformRowHeights" >
<property name="animated" >
<bool>true</bool>
</property>
<property name="allColumnsShowFocus" >
<bool>true</bool>
</property>
<property name="columnCount" >
<number>1</number>
</property>
<column>
<property name="text" >
<string>1</string>
</property>
</column>
</widget>
</item>
<item>
@ -299,7 +293,7 @@
<string>Add files to Library</string>
</property>
<property name="shortcut" >
<string>a</string>
<string>A</string>
</property>
<property name="autoRepeat" >
<bool>false</bool>
@ -313,7 +307,7 @@
<string>Delete selected items</string>
</property>
<property name="shortcut" >
<string>Delete</string>
<string>Del</string>
</property>
</action>
<action name="action_edit" >
@ -324,7 +318,7 @@
<string>Edit meta-information for the currently selected items</string>
</property>
<property name="shortcut" >
<string>e</string>
<string>E</string>
</property>
<property name="autoRepeat" >
<bool>false</bool>

View File

@ -204,7 +204,7 @@ class Command(TransferBuffer):
Command numbers are:
0 GetUsbProtocolVersion
1 ReqUsbConnect
1 ReqEndSession
10 FskFileOpen
11 FskFileClose
@ -343,11 +343,11 @@ class DirClose(ShortCommand):
""" @param id: The identifier returned as a result of a L{DirOpen} command """
ShortCommand.__init__(self, number=DirClose.NUMBER, type=0x01, command=id)
class USBConnect(ShortCommand):
class EndSession(ShortCommand):
""" Ask device to change status to 'USB connected' i.e., tell the device that the present sequence of commands is complete """
NUMBER=0x1 #: Command number
def __init__(self):
ShortCommand.__init__(self, number=USBConnect.NUMBER, type=0x01, command=0x00)
ShortCommand.__init__(self, number=EndSession.NUMBER, type=0x01, command=0x00)
class GetUSBProtocolVersion(ShortCommand):
""" Get USB Protocol version used by device """