Added support for progress reporting in libprs500. Only implemented in get_file

Created a rudimentary GUI that parses the media list on the device abd displays the books
Switched to setuptools
This commit is contained in:
Kovid Goyal 2006-11-21 04:52:47 +00:00
parent 87c82ab7d7
commit edbdd7e496
18 changed files with 983 additions and 406 deletions

View File

@ -1,6 +0,0 @@
include libprs500 *.py
include libprs500/gui *.py
include scripts *.py
include README
include docs/pdf/api.pdf
recursive-include docs/html *

222
ez_setup.py Normal file
View File

@ -0,0 +1,222 @@
#!python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import sys
DEFAULT_VERSION = "0.6c3"
DEFAULT_URL = "http://cheeseshop.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = {
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
}
import sys, os
def _validate_md5(egg_name, data):
if egg_name in md5_data:
from md5 import md5
digest = md5(data).hexdigest()
if digest != md5_data[egg_name]:
print >>sys.stderr, (
"md5 validation of %s failed! (Possible download problem?)"
% egg_name
)
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
download_delay=15
):
"""Automatically find/download setuptools and make it available on sys.path
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end with
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
it is not already available. If `download_delay` is specified, it should
be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
try:
import setuptools
if setuptools.__version__ == '0.0.1':
print >>sys.stderr, (
"You have an obsolete version of setuptools installed. Please\n"
"remove it from your system entirely before rerunning this script."
)
sys.exit(2)
except ImportError:
egg = download_setuptools(version, download_base, to_dir, download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
import pkg_resources
try:
pkg_resources.require("setuptools>="+version)
except pkg_resources.VersionConflict, e:
# XXX could we install in a subprocess here?
print >>sys.stderr, (
"The required version of setuptools (>=%s) is not available, and\n"
"can't be installed while this script is running. Please install\n"
" a more recent version first.\n\n(Currently using %r)"
) % (version, e.args[0])
sys.exit(2)
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
from distutils import log
if delay:
log.warn("""
---------------------------------------------------------------------------
This script requires setuptools version %s to run (even to display
help). I will attempt to download it for you (from
%s), but
you may need to enable firewall access for this script first.
I will start the download in %d seconds.
(Note: if this machine does not have network access, please obtain the file
%s
and place it in this directory before rerunning this script.)
---------------------------------------------------------------------------""",
version, download_base, delay, url
); from time import sleep; sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
import setuptools
except ImportError:
egg = None
try:
egg = download_setuptools(version, delay=0)
sys.path.insert(0,egg)
from setuptools.command.easy_install import main
return main(list(argv)+[egg]) # we're done here
finally:
if egg and os.path.exists(egg):
os.unlink(egg)
else:
if setuptools.__version__ == '0.0.1':
# tell the user to uninstall obsolete version
use_setuptools(version)
req = "setuptools>="+version
import pkg_resources
try:
pkg_resources.require(req)
except pkg_resources.VersionConflict:
try:
from setuptools.command.easy_install import main
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print "Setuptools version",version,"or greater has been installed."
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames):
"""Update our built-in md5 registry"""
import re
from md5 import md5
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print >>sys.stderr, "Internal error!"
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])

View File

@ -24,7 +24,7 @@ The public interface of libprs500 is in L{libprs500.communicate}. To use it
>>> dev.close() >>> dev.close()
There is also a script L{prs500} that provides a command-line interface to libprs500. See the script There is also a script L{prs500} that provides a command-line interface to libprs500. See the script
for more usage examples. for more usage examples. A GUI is available via the command prs500-gui.
The packet structure used by the SONY Reader USB protocol is defined in the module L{prstypes}. The communication logic The packet structure used by the SONY Reader USB protocol is defined in the module L{prstypes}. The communication logic
is defined in the module L{communicate}. is defined in the module L{communicate}.
@ -34,6 +34,6 @@ 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.2.1"
__docformat__ = "epytext" __docformat__ = "epytext"
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"

21
libprs500/cli/__init__.py Normal file
View File

@ -0,0 +1,21 @@
## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along
## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Provides a command-line interface to the SONY Reader PRS-500.
For usage information run the script.
"""
__docformat__ = "epytext"
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"

View File

@ -1,4 +1,3 @@
#!/usr/bin/env python
## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net ## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net
## This program is free software; you can redistribute it and/or modify ## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by ## it under the terms of the GNU General Public License as published by
@ -14,7 +13,7 @@
## 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.
""" """
Provides a command-line interface to the SONY Reader PRS-500. Provides a command-line and optional graphical interface to the SONY Reader PRS-500.
For usage information run the script. For usage information run the script.
""" """
@ -22,9 +21,9 @@ For usage information run the script.
import StringIO, sys, time, os import StringIO, sys, time, os
from optparse import OptionParser from optparse import OptionParser
from libprs500 import VERSION from libprs500 import __version__ as VERSION
from libprs500.communicate import PRS500Device from libprs500.communicate import PRS500Device
from libprs500.terminfo import TerminalController from terminfo import TerminalController
from libprs500.errors import ArgumentError, DeviceError from libprs500.errors import ArgumentError, DeviceError
@ -184,25 +183,13 @@ def main():
parser.add_option("--log-packets", help="print out packet stream to stdout. "+\ 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.", "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) dest="log_packets", action="store_true", default=False)
parser.add_option("--gui", help="Run a Graphical User Interface", dest="gui", action="store_true", default=False)
parser.remove_option("-h") parser.remove_option("-h")
parser.disable_interspersed_args() # Allow unrecognized options parser.disable_interspersed_args() # Allow unrecognized options
options, args = parser.parse_args() options, args = parser.parse_args()
if options.gui:
try:
from PyQt4.Qt import QApplication, QMainWindow
from libprs500.gui.main import MainWindow
app = QApplication(args)
window = QMainWindow()
mw = MainWindow(window, options.log_packets)
sys.exit(app.exec_())
except ImportError, e:
print >>sys.stderr, "You dont have PyQt4 installed:", e
sys.exit(1)
if len(args) < 1: if len(args) < 1:
parser.print_help() parser.print_help()
sys.exit(1) return 1
command = args[0] command = args[0]
args = args[1:] args = args[1:]
@ -242,7 +229,7 @@ def main():
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
sys.exit(1) return 1
print ls(dev, args[0], term, color=options.color, recurse=options.recurse, ll=options.ll, human_readable_size=options.hrs, cols=cols), print ls(dev, args[0], term, color=options.color, recurse=options.recurse, ll=options.ll, human_readable_size=options.hrs, cols=cols),
elif command == "info": elif command == "info":
info(dev) info(dev)
@ -257,7 +244,7 @@ def main():
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
if len(args) != 2: if len(args) != 2:
parser.print_help() parser.print_help()
sys.exit(1) return 1
if args[0].startswith("prs500:"): if args[0].startswith("prs500:"):
outfile = args[1] outfile = args[1]
path = args[0][7:] path = args[0][7:]
@ -269,7 +256,7 @@ def main():
except IOError, e: except IOError, e:
print >> sys.stderr, e print >> sys.stderr, e
parser.print_help() parser.print_help()
sys.exit(1) return 1
dev.get_file(path, outfile) dev.get_file(path, outfile)
outfile.close() outfile.close()
elif args[1].startswith("prs500:"): elif args[1].startswith("prs500:"):
@ -278,19 +265,19 @@ def main():
except IOError, e: except IOError, e:
print >> sys.stderr, e print >> sys.stderr, e
parser.print_help() parser.print_help()
sys.exit(1) return 1
dev.put_file(infile, args[1][7:]) dev.put_file(infile, args[1][7:])
infile.close() infile.close()
else: else:
parser.print_help() parser.print_help()
sys.exit(1) return 1
elif command == "cat": elif command == "cat":
outfile = sys.stdout outfile = sys.stdout
parser = OptionParser(usage="usage: %prog cat path\n\npath should point to a file on the device and must begin with /,a:/ or b:/") parser = OptionParser(usage="usage: %prog cat path\n\npath should point to a file on the device and must begin with /,a:/ or b:/")
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
sys.exit(1) return 1
if args[0].endswith("/"): path = args[0][:-1] if args[0].endswith("/"): path = args[0][:-1]
else: path = args[0] else: path = args[0]
outfile = sys.stdout outfile = sys.stdout
@ -302,7 +289,7 @@ def main():
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
sys.exit(1) return 1
dev.rm(args[0]) dev.rm(args[0])
elif command == "touch": elif command == "touch":
parser = OptionParser(usage="usage: %prog touch path\n\npath should point to a file on the device and must begin with /,a:/ or b:/\n\n"+ parser = OptionParser(usage="usage: %prog touch path\n\npath should point to a file on the device and must begin with /,a:/ or b:/\n\n"+
@ -310,17 +297,13 @@ def main():
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
sys.exit(1) return 1
dev.touch(args[0]) dev.touch(args[0])
else: else:
parser.print_help() parser.print_help()
if dev.handle: dev.close() if dev.handle: dev.close()
sys.exit(1) return 1
except (ArgumentError, DeviceError), e: except (ArgumentError, DeviceError), e:
print >>sys.stderr, e print >>sys.stderr, e
sys.exit(1) return 1
finally: return 0
if dev.handle: dev.close()
if __name__ == "__main__":
main()

View File

@ -14,6 +14,8 @@
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import sys, re import sys, re
""" Get information about the terminal we are running in """
class TerminalController: class TerminalController:
""" """
A class that can be used to portably generate formatted output to A class that can be used to portably generate formatted output to

View File

@ -73,8 +73,9 @@ class FindBooks(ContentHandler):
def __init__(self, src): def __init__(self, src):
dict.__init__(self, src) dict.__init__(self, src)
if not self.has_key("author") or len(self["author"].rstrip()) == 0: if not self.has_key("author") or len(self["author"].rstrip()) == 0:
self["author"] = "Unknown" self["author"] = u"Unknown"
self["title"] = self["title"].strip() self["title"] = unicode(self["title"].strip())
self["author"] = unicode(self["author"])
def __repr__(self): def __repr__(self):
return self["title"] + " by " + self["author"] + " at " + self["path"] return self["title"] + " by " + self["author"] + " at " + self["path"]
@ -98,7 +99,7 @@ class FindBooks(ContentHandler):
def startElement(self, name, attrs): def startElement(self, name, attrs):
if name == self.book_name: if name == self.book_name:
data = FindBooks.Book(attrs) data = FindBooks.Book(attrs)
data["path"] = self.root + data["path"] data["path"] = unicode(self.root + data["path"])
self.books.append(data) self.books.append(data)
self.in_book = True self.in_book = True
elif self.in_book and name == self.thumbnail_name: elif self.in_book and name == self.thumbnail_name:
@ -215,15 +216,23 @@ class PRS500Device(object):
return run_session return run_session
def __init__(self, log_packets=False) : def __init__(self, log_packets=False, report_progress=None) :
""" @param log_packets: If true the packet stream to/from the device is logged """ """
@param log_packets: If true the packet stream to/from the device is logged
@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, self.device_descriptor = DeviceDescriptor(PRS500Device.SONY_VENDOR_ID,
PRS500Device.PRS500_PRODUCT_ID, PRS500Device.PRS500_PRODUCT_ID,
PRS500Device.PRS500_INTERFACE_ID) PRS500Device.PRS500_INTERFACE_ID)
self.device = self.device_descriptor.getDevice() #: The actual device (PyUSB object) 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.handle = None #: Handle that is used to communicate with device. Setup in L{open}
self._log_packets = log_packets self._log_packets = log_packets
self.report_progress = report_progress
def is_connected(self):
return self.device_descriptor.getDevice() != None
@classmethod @classmethod
def _validate_response(cls, res, type=0x00, number=0x00): def _validate_response(cls, res, type=0x00, number=0x00):
""" Raise a ProtocolError if the type and number of C{res} is not the same as C{type} and C{number}. """ """ Raise a ProtocolError if the type and number of C{res} is not the same as C{type} and C{number}. """
@ -388,8 +397,8 @@ class PRS500Device(object):
if res.code != 0: if res.code != 0:
raise PathError("Unable to open " + path + " for reading. Response code: " + hex(res.code)) raise PathError("Unable to open " + path + " for reading. Response code: " + hex(res.code))
id = self._bulk_read(20, data_type=IdAnswer, command_number=FileOpen.NUMBER)[0].id id = self._bulk_read(20, data_type=IdAnswer, command_number=FileOpen.NUMBER)[0].id
bytes_left, chunk_size, pos = bytes, 0x8000, 0 bytes_left, chunk_size, pos = bytes, 0x8000, 0
while bytes_left > 0: while bytes_left > 0:
if chunk_size > bytes_left: chunk_size = bytes_left if chunk_size > bytes_left: chunk_size = bytes_left
res = self._send_validated_command(FileIO(id, pos, chunk_size)) res = self._send_validated_command(FileIO(id, pos, chunk_size))
if res.code != 0: if res.code != 0:
@ -405,6 +414,7 @@ class PRS500Device(object):
raise ArgumentError("File get operation failed. Could not write to local location: " + str(e)) raise ArgumentError("File get operation failed. Could not write to local location: " + str(e))
bytes_left -= chunk_size bytes_left -= chunk_size
pos += chunk_size pos += chunk_size
if self.report_progress: self.report_progress(int(100*((1.*pos)/bytes)))
self._send_validated_command(FileClose(id)) self._send_validated_command(FileClose(id))
# Not going to check response code to see if close was successful as there's not much we can do if it wasnt # Not going to check response code to see if close was successful as there's not much we can do if it wasnt
@ -478,11 +488,12 @@ class PRS500Device(object):
@return: A list of tuples. Each tuple has form ("location", free space, total space) @return: A list of tuples. Each tuple has form ("location", free space, total space)
""" """
data = [] data = []
if self.report_progress: self.report_progress(-1)
for path in ("/Data/", "a:/", "b:/"): for path in ("/Data/", "a:/", "b:/"):
res = self._send_validated_command(FreeSpaceQuery(path),timeout=5000) # Timeout needs to be increased as it takes time to read card res = self._send_validated_command(FreeSpaceQuery(path),timeout=5000) # Timeout needs to be increased as it takes time to read card
buffer_size = 16 + res.data[2] buffer_size = 16 + res.data[2]
pkt = self._bulk_read(buffer_size, data_type=FreeSpaceAnswer, command_number=FreeSpaceQuery.NUMBER)[0] pkt = self._bulk_read(buffer_size, data_type=FreeSpaceAnswer, command_number=FreeSpaceQuery.NUMBER)[0]
data.append( (path, pkt.free_space, pkt.total) ) data.append( (path, pkt.free_space, pkt.total) )
return data return data
def _exists(self, path): def _exists(self, path):

View File

@ -12,77 +12,295 @@
## 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 main_gui import Ui_MainWindow
from libprs500.communicate import PRS500Device as device from libprs500.communicate import PRS500Device as device
from libprs500.errors import * from libprs500.errors import *
from PyQt4.Qt import QThread import images
from PyQt4.QtCore import Qt, SIGNAL
from PyQt4.Qt import QObject, QThread, QCoreApplication, QEventLoop, QString, QStandardItem, QStandardItemModel, QStatusBar, QVariant, QAbstractTableModel, \
QAbstractItemView, QImage, QPixmap, QIcon, QSize
from PyQt4 import uic
import sys, pkg_resources, re, string
from operator import itemgetter
NONE = QVariant()
class DeviceDetector(QThread): def human_readable(size):
def __init__(self, detected_slot): """ Convert a size in bytes into a human readle form """
QThread.__init__(self) if size < 1024: divisor, suffix = 1, "B"
self.dev = None elif size < 1024*1024: divisor, suffix = 1024., "KB"
self.detected_slot = detected_slot elif size < 1024*1024*1024: divisor, suffix = 1024*1024, "MB"
self.removed = False 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
class DeviceBooksModel(QAbstractTableModel):
def __init__(self, parent, data):
QAbstractTableModel.__init__(self, parent)
self._data = data
self._orig_data = data
def set_data(self, data):
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self._data = data
self._orig_data = data
self.sort(0, Qt.DescendingOrder)
def run(self): def rowCount(self, parent): return len(self._data)
wait = 1 def columnCount(self, parent): return 3
while None == self.msleep(wait):
wait = 1000 def headerData(self, section, orientation, role):
if self.removed or not self.dev: if role != Qt.DisplayRole:
self.dev = device() return NONE
self.removed = False text = ""
self.detected_slot() if orientation == Qt.Horizontal:
if section == 0: text = "Title"
elif section == 1: text = "Author(s)"
elif section == 2: text = "Size"
return QVariant(self.trUtf8(text))
else: return QVariant(str(1+section))
def data(self, index, role):
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"]))
return QVariant(text)
elif role == Qt.TextAlignmentRole and index.column() == 2:
return QVariant(Qt.AlignRight)
elif role == Qt.DecorationRole and index.column() == 0:
book = self._data[index.row()]
if book.has_key("thumbnail"):
return QVariant(book["thumbnail"])
return NONE
def info(self, row):
row = self._data[row]
return row["title"], row["author"], human_readable(int(row["size"])), row["mime"], row["thumbnail"].pixmap(60, 80, QIcon.Normal, QIcon.On)
def sort(self, col, order):
def getter(key, func): return lambda x : func(itemgetter(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 == 2: key, func = "size", int
descending = order != Qt.AscendingOrder
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self._data.sort(key=getter(key, func))
if descending: self._data.reverse()
self.emit(SIGNAL("layoutChanged()"))
self.emit(SIGNAL("sorted()"))
def search(self, query):
queries = unicode(query).lower().split()
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self._data = []
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
else:
match = False
break
if match: self._data.append(book)
self.emit(SIGNAL("layoutChanged()"))
self.emit(SIGNAL("searched()"))
class MainWindow(Ui_MainWindow): Ui_MainWindow, bclass = uic.loadUiType(pkg_resources.resource_stream(__name__, "main.ui"))
class MainWindow(QObject, Ui_MainWindow):
def safe(func): def tree_clicked(self, index):
def call_func(*args, **kwargs): item = self.tree.itemFromIndex(index)
window = args[0] text = str(item.text())
res = None if text == "Library":
try: print "Library Clicked"
res = func(*args, **kwargs) elif text == "SONY Reader":
except DeviceError: self.set_data(self.main_books + self.card_books)
window.device_removed() elif text == "Main Memory":
except TimeoutError, e: self.set_data(self.main_books)
print e elif text == "Storage Card":
window.timeout_error() self.set_data(self.card_books)
return res elif text == "Books":
return call_func text = str(item.parent().text())
if text == "Library":
print "Library --> Books Clicked"
elif text == "Main Memory":
self.set_data(self.main_books)
elif text == "Storage Card":
self.set_data(self.card_books)
@apply def set_data(self, data):
def dev(): self.model.set_data(data)
def fget(self): self.table_view.resizeColumnsToContents()
return self.detector.dev
return property(**locals())
def __init__(self, window, log_packets): def data_sorted(self):
Ui_MainWindow.__init__(self) self.table_view.resizeRowsToContents()
self.log_packets = log_packets self.table_view.clearSelection()
self.detector = DeviceDetector(self.establish_connection) self.book_cover.hide()
self.detector.start() self.book_info.hide()
def show_book(self, current, previous):
title, author, size, mime, thumbnail = current.model().info(current.row())
self.book_info.setText(self.BOOK_TEMPLATE.arg(title).arg(size).arg(author).arg(mime))
self.book_cover.setPixmap(thumbnail)
self.book_cover.show()
self.book_info.show()
def searched(self):
self.table_view.clearSelection()
self.book_cover.hide()
self.book_info.hide()
self.table_view.resizeRowsToContents()
def clear(self, checked): self.search.setText("")
def __init__(self, window):
QObject.__init__(self)
Ui_MainWindow.__init__(self)
self.dev = device(report_progress=self.progress)
self.is_connected = False
self.setupUi(window) self.setupUi(window)
self.card = None
# Create Tree
self.tree = QStandardItemModel()
library = QStandardItem(QString("Library"))
library.setIcon(QIcon(":/images/mycomputer.png"))
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(":/images/reader.png"))
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 Table
self.model = DeviceBooksModel(window, [])
QObject.connect(self.model, SIGNAL("sorted()"), self.data_sorted)
self.table_view.setModel(self.model)
self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table_view.setSortingEnabled(True)
QObject.connect(self.table_view.selectionModel(), SIGNAL("currentChanged(QModelIndex, QModelIndex)"), self.show_book)
QObject.connect(self.search, SIGNAL("textChanged(QString)"), self.model.search)
QObject.connect(self.model, SIGNAL("searched()"), self.searched)
self.clearButton.setIcon(QIcon(":/images/clear.png"))
QObject.connect(self.clearButton, SIGNAL("clicked(bool)"), self.clear)
# Setup book display
self.BOOK_TEMPLATE = self.book_info.text()
self.BOOK_IMAGE = QPixmap(":/images/book.png")
self.book_cover.hide()
self.book_info.hide()
self.device_detector = self.startTimer(1000)
self.splitter.setStretchFactor(0,0)
self.splitter.setStretchFactor(1,100)
self.search.setFocus(Qt.OtherFocusReason)
self.window = window
window.show() window.show()
def timerEvent(self, e):
if e.timerId() == self.device_detector:
is_connected = self.dev.is_connected()
if is_connected and not self.is_connected:
self.establish_connection()
elif not is_connected and self.is_connected:
self.device_removed()
def device_removed(self, timeout=False): def device_removed(self, timeout=False):
""" @todo: implement this """ """ @todo: only reset stuff if library is not shown """
self.detector.removed = True 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.model.set_data([])
self.book_cover.hide()
self.book_info.hide()
def timeout_error(self): def timeout_error(self):
""" @todo: update status bar """ """ @todo: display error dialog """
self.detector.sleep(10) pass
self.device_removed(timeout=True)
def progress(self, val):
if val < 0:
self.progress_bar.setMaximum(0)
else: self.progress_bar.setValue(val)
QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
def status(self, msg):
self.progress_bar.setMaximum(100)
self.progress_bar.reset()
self.progress_bar.setFormat(msg + ": %p%")
QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
@safe
def establish_connection(self): def establish_connection(self):
mb, cb, mx, cx = self.dev.books() 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()
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.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")
mb, cb, mx, cx = self.dev.books()
for x in (mb, cb):
for book in x:
if book.has_key("thumbnail"):
book["thumbnail"] = QIcon(QPixmap.fromImage(QImage.fromData(book["thumbnail"])))
else: book["thumbnail"] = QIcon(self.BOOK_IMAGE)
if "&" in book["author"]:
book["author"] = re.sub(r"&\s*", r"\n", book["author"])
self.main_books = mb self.main_books = mb
self.card_books = cb self.card_books = cb
self.main_xml = mx self.main_xml = mx
self.cache_xml = cx self.cache_xml = cx
print self.main_books + self.card_books
def main():
from PyQt4.Qt import QApplication, QMainWindow
app = QApplication(sys.argv)
window = QMainWindow()
gui = MainWindow(window)
return app.exec_()

View File

@ -1,49 +1,41 @@
## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along
## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
<ui version="4.0" > <ui version="4.0" >
<author>Kovid Goyal</author>
<class>MainWindow</class> <class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow" > <widget class="QMainWindow" name="MainWindow" >
<property name="geometry" > <property name="geometry" >
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>885</width> <width>871</width>
<height>631</height> <height>631</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>5</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle" > <property name="windowTitle" >
<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="0" column="1" > <item>
<layout class="QVBoxLayout" > <widget class="QSplitter" name="splitter" >
<property name="margin" > <property name="orientation" >
<number>0</number> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="spacing" > <widget class="QWidget" name="" >
<number>6</number> <layout class="QVBoxLayout" >
</property>
<item>
<layout class="QHBoxLayout" >
<property name="margin" > <property name="margin" >
<number>0</number> <number>0</number>
</property> </property>
@ -51,77 +43,195 @@
<number>6</number> <number>6</number>
</property> </property>
<item> <item>
<widget class="QToolButton" name="clear" > <widget class="QTreeView" name="treeView" >
<property name="text" > <property name="sizePolicy" >
<string>...</string> <sizepolicy>
<hsizetype>1</hsizetype>
<vsizetype>7</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="search" >
<property name="acceptDrops" > <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> <bool>false</bool>
</property> </property>
<property name="autoFillBackground" > <property name="alternatingRowColors" >
<bool>false</bool> <bool>true</bool>
</property> </property>
<property name="text" > <property name="indentation" >
<string>Search title and author</string> <number>15</number>
</property> </property>
<property name="frame" > <property name="uniformRowHeights" >
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QLabel" name="df" >
<property name="autoFillBackground" >
<bool>false</bool>
</property>
<property name="text" >
<string>Main Memory:&lt;br>&lt;br>Storage card:</string>
</property>
<property name="textFormat" >
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
</layout> </layout>
</item> </widget>
<item> <widget class="QWidget" name="" >
<widget class="QListView" name="listView" > <layout class="QVBoxLayout" >
<property name="sizePolicy" > <property name="margin" >
<sizepolicy> <number>0</number>
<hsizetype>5</hsizetype>
<vsizetype>7</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="frameShadow" > <property name="spacing" >
<enum>QFrame::Plain</enum> <number>6</number>
</property> </property>
<property name="autoScroll" > <item>
<bool>false</bool> <layout class="QVBoxLayout" >
</property> <property name="margin" >
</widget> <number>0</number>
</item> </property>
</layout> <property name="spacing" >
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLabel" name="label" >
<property name="text" >
<string>&amp;Search:</string>
</property>
<property name="buddy" >
<cstring>search</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="search" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="acceptDrops" >
<bool>false</bool>
</property>
<property name="toolTip" >
<string>Search the list of books by title or author&lt;br>&lt;br>Words separated by spaces are ANDed</string>
</property>
<property name="whatsThis" >
<string>Search the list of books by title or author&lt;br>&lt;br>Words separated by spaces are ANDed</string>
</property>
<property name="autoFillBackground" >
<bool>false</bool>
</property>
<property name="text" >
<string/>
</property>
<property name="frame" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="clearButton" >
<property name="toolTip" >
<string>Reset Quick Search</string>
</property>
<property name="text" >
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="table_view" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>7</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="alternatingRowColors" >
<bool>true</bool>
</property>
<property name="showGrid" >
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLabel" name="book_cover" >
<property name="text" >
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<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" >
<string>&lt;table>&lt;tr>&lt;td>&lt;b>Title: &lt;/b>%1&lt;/td>&lt;td>&lt;b>&amp;nbsp;Size:&lt;/b> %2&lt;/td>&lt;/tr>&lt;tr>&lt;td>&lt;b>Author: &lt;/b>%3&lt;/td>&lt;td>&lt;b>&amp;nbsp;Type: &lt;/b>%4&lt;/td>&lt;/tr></string>
</property>
<property name="textFormat" >
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item> </item>
<item row="0" column="0" > <item>
<layout class="QVBoxLayout" > <widget class="QProgressBar" name="progress_bar" >
<property name="margin" > <property name="value" >
<number>0</number> <number>100</number>
</property> </property>
<property name="spacing" > <property name="orientation" >
<number>6</number> <enum>Qt::Horizontal</enum>
</property> </property>
<item> </widget>
<widget class="QTreeView" name="treeView" >
<property name="frameShadow" >
<enum>QFrame::Plain</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="df" >
<property name="text" >
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QStatusBar" name="statusbar" />
<widget class="QToolBar" name="toolBar" > <widget class="QToolBar" name="toolBar" >
<property name="orientation" > <property name="orientation" >
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@ -131,6 +241,7 @@
</attribute> </attribute>
</widget> </widget>
</widget> </widget>
<includes/>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>

View File

@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'main.ui'
#
# Created: Thu Nov 16 20:48:21 2006
# by: PyQt4 UI code generator 4-snapshot-20061112
#
# WARNING! All changes made in this file will be lost!
import sys
from PyQt4 import QtCore, QtGui
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(QtCore.QSize(QtCore.QRect(0,0,885,631).size()).expandedTo(MainWindow.minimumSizeHint()))
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridlayout = QtGui.QGridLayout(self.centralwidget)
self.gridlayout.setMargin(9)
self.gridlayout.setSpacing(6)
self.gridlayout.setObjectName("gridlayout")
self.vboxlayout = QtGui.QVBoxLayout()
self.vboxlayout.setMargin(0)
self.vboxlayout.setSpacing(6)
self.vboxlayout.setObjectName("vboxlayout")
self.hboxlayout = QtGui.QHBoxLayout()
self.hboxlayout.setMargin(0)
self.hboxlayout.setSpacing(6)
self.hboxlayout.setObjectName("hboxlayout")
self.clear = QtGui.QToolButton(self.centralwidget)
self.clear.setObjectName("clear")
self.hboxlayout.addWidget(self.clear)
self.search = QtGui.QLineEdit(self.centralwidget)
self.search.setAcceptDrops(False)
self.search.setAutoFillBackground(False)
self.search.setFrame(True)
self.search.setObjectName("search")
self.hboxlayout.addWidget(self.search)
self.vboxlayout.addLayout(self.hboxlayout)
self.listView = QtGui.QListView(self.centralwidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Policy(5),QtGui.QSizePolicy.Policy(7))
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.listView.sizePolicy().hasHeightForWidth())
self.listView.setSizePolicy(sizePolicy)
self.listView.setFrameShadow(QtGui.QFrame.Plain)
self.listView.setAutoScroll(False)
self.listView.setObjectName("listView")
self.vboxlayout.addWidget(self.listView)
self.gridlayout.addLayout(self.vboxlayout,0,1,1,1)
self.vboxlayout1 = QtGui.QVBoxLayout()
self.vboxlayout1.setMargin(0)
self.vboxlayout1.setSpacing(6)
self.vboxlayout1.setObjectName("vboxlayout1")
self.treeView = QtGui.QTreeView(self.centralwidget)
self.treeView.setFrameShadow(QtGui.QFrame.Plain)
self.treeView.setObjectName("treeView")
self.vboxlayout1.addWidget(self.treeView)
self.df = QtGui.QLabel(self.centralwidget)
self.df.setObjectName("df")
self.vboxlayout1.addWidget(self.df)
self.gridlayout.addLayout(self.vboxlayout1,0,0,1,1)
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtGui.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.toolBar = QtGui.QToolBar(MainWindow)
self.toolBar.setOrientation(QtCore.Qt.Horizontal)
self.toolBar.setObjectName("toolBar")
MainWindow.addToolBar(self.toolBar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
self.clear.setText(QtGui.QApplication.translate("MainWindow", "...", None, QtGui.QApplication.UnicodeUTF8))
self.search.setText(QtGui.QApplication.translate("MainWindow", "Search title and author", None, QtGui.QApplication.UnicodeUTF8))
self.df.setText(QtGui.QApplication.translate("MainWindow", "TextLabel", None, QtGui.QApplication.UnicodeUTF8))

View File

@ -0,0 +1,8 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>images/book.png</file>
<file>images/mycomputer.png</file>
<file>images/reader.png</file>
<file>images/clear.png</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,126 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-3.9.dtd">
<!-- Project file for project prs-500 -->
<!-- Saved: 2006-11-07, 00:09:05 -->
<!-- Copyright (C) 2006 Kovid Goyal, kovid@kovidgoyal.net -->
<Project version="3.9">
<ProgLanguage mixed="0">Python</ProgLanguage>
<UIType>Console</UIType>
<Description>Library to communicate with the Sony Reader PRS-500 via USB</Description>
<Version></Version>
<Author>Kovid Goyal</Author>
<Email>kovid@kovidgoyal.net</Email>
<Sources>
<Source>
<Dir>libprs500</Dir>
<Name>communicate.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Name>terminfo.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Name>prstypes.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Name>errors.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Name>__init__.py</Name>
</Source>
<Source>
<Name>setup.py</Name>
</Source>
<Source>
<Dir>scripts</Dir>
<Name>prs500.py</Name>
</Source>
</Sources>
<Forms>
</Forms>
<Translations>
</Translations>
<Interfaces>
</Interfaces>
<Others>
<Other>
<Name>epydoc.conf</Name>
</Other>
<Other>
<Name>epydoc-pdf.conf</Name>
</Other>
</Others>
<MainScript>
<Dir>scripts</Dir>
<Name>prs500.py</Name>
</MainScript>
<Vcs>
<VcsType>Subversion</VcsType>
<VcsOptions>(dp0
S'status'
p1
(lp2
S''
p3
asS'log'
p4
(lp5
g3
asS'global'
p6
(lp7
g3
asS'update'
p8
(lp9
g3
asS'remove'
p10
(lp11
g3
asS'add'
p12
(lp13
g3
asS'tag'
p14
(lp15
g3
asS'export'
p16
(lp17
g3
asS'commit'
p18
(lp19
g3
asS'diff'
p20
(lp21
g3
asS'checkout'
p22
(lp23
g3
asS'history'
p24
(lp25
g3
as.</VcsOptions>
<VcsOtherData>(dp0
S'standardLayout'
p1
I01
s.</VcsOtherData>
</Vcs>
<FiletypeAssociations>
<FiletypeAssociation pattern="*.ui.h" type="FORMS" />
<FiletypeAssociation pattern="*.ui" type="FORMS" />
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
<FiletypeAssociation pattern="*.ptl" type="SOURCES" />
<FiletypeAssociation pattern="*.py" type="SOURCES" />
</FiletypeAssociations>
</Project>

211
prs-500.e4p Normal file
View File

@ -0,0 +1,211 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-4.0.dtd">
<!-- eric4 project file for project prs-500 -->
<!-- Saved: 2006-11-20, 20:22:57 -->
<!-- Copyright (C) 2006 Kovid Goyal, kovid@kovidgoyal.net -->
<Project version="4.0">
<ProgLanguage mixed="0">Python</ProgLanguage>
<UIType>Qt4</UIType>
<Description>Library to communicate with the Sony Reader PRS-500 via USB</Description>
<Version></Version>
<Author>Kovid Goyal</Author>
<Email>kovid@kovidgoyal.net</Email>
<Sources>
<Source>
<Dir>libprs500</Dir>
<Name>communicate.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Name>prstypes.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Name>errors.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Name>__init__.py</Name>
</Source>
<Source>
<Name>setup.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Dir>cli</Dir>
<Name>terminfo.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Dir>cli</Dir>
<Name>main.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Dir>cli</Dir>
<Name>__init__.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Dir>gui</Dir>
<Name>main.py</Name>
</Source>
<Source>
<Dir>libprs500</Dir>
<Dir>gui</Dir>
<Name>__init__.py</Name>
</Source>
</Sources>
<Forms>
<Form>
<Dir>libprs500</Dir>
<Dir>gui</Dir>
<Name>main.ui</Name>
</Form>
</Forms>
<Translations>
</Translations>
<Resources>
</Resources>
<Interfaces>
</Interfaces>
<Others>
<Other>
<Name>epydoc.conf</Name>
</Other>
<Other>
<Name>epydoc-pdf.conf</Name>
</Other>
<Other>
<Dir>libprs500</Dir>
<Dir>gui</Dir>
<Dir>resources</Dir>
<Name>images.qrc</Name>
</Other>
</Others>
<MainScript>
<Dir>libprs500</Dir>
<Dir>gui</Dir>
<Name>main.py</Name>
</MainScript>
<Vcs>
<VcsType>Subversion</VcsType>
<VcsOptions>
<dict>
<key>
<string>status</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
<key>
<string>log</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
<key>
<string>global</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
<key>
<string>update</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
<key>
<string>remove</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
<key>
<string>add</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
<key>
<string>tag</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
<key>
<string>export</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
<key>
<string>commit</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
<key>
<string>diff</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
<key>
<string>checkout</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
<key>
<string>history</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
</dict>
</VcsOptions>
<VcsOtherData>
<dict>
<key>
<string>standardLayout</string>
</key>
<value>
<bool>True</bool>
</value>
</dict>
</VcsOtherData>
</Vcs>
<FiletypeAssociations>
<FiletypeAssociation pattern="*.ui.h" type="FORMS" />
<FiletypeAssociation pattern="*.ui" type="FORMS" />
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
<FiletypeAssociation pattern="*.py" type="SOURCES" />
<FiletypeAssociation pattern="*.ptl" type="SOURCES" />
</FiletypeAssociations>
</Project>

View File

@ -13,26 +13,42 @@
## 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.
#!/usr/bin/env python #!/usr/bin/env python
from distutils.core import setup import ez_setup
from libprs500 import VERSION ez_setup.use_setuptools()
from setuptools import setup, find_packages
from libprs500 import __version__ as VERSION
# TODO: Dependencies
setup(name='libprs500', setup(name='libprs500',
entry_points = {
'console_scripts': [ 'prs500 = libprs500.cli.main:main' ],
'gui_scripts' : [ 'prs500-gui = libprs500.gui.main:main']
},
include_package_data = True,
version=VERSION, version=VERSION,
description='Library to interface with the Sony Portable Reader 500 over USB.', description='Library to interface with the Sony Portable Reader 500 over USB.',
long_description = long_description =
""" """
libprs500 is library to interface with the _Sony Portable Reader:http://Sony.com/reader over USB. libprs500 is library to interface with the `SONY Portable Reader`_ over USB_.
It provides methods to list the contents of the file system on the device as well as It provides methods to list the contents of the file system on the device,
copy files from and to the device. It also provides a command line and a graphical user interface via the script prs500.py. as well as copy files from and to the device.
""", It also provides a command line and a graphical user interface via the script prs500.py.
For SVN access: svn co https://kovidgoyal.net/svn/code/prs-500
.. _SONY Portable Reader: http://Sony.com/reader
.. _USB: http://www.usb.org
""",
author='Kovid Goyal', author='Kovid Goyal',
author_email='kovid@kovidgoyal.net', author_email='kovid@kovidgoyal.net',
provides=['libprs500'], provides=['libprs500'],
requires=['pyusb'], packages = find_packages(),
packages = ['libprs500', 'libprs500.gui'], license = 'GPL',
scripts = ['scripts/prs500.py'], url = 'http://www.python.org/pypi/libprs500/',
classifiers = [ classifiers = [
'Development Status :: 3 - Alpha', 'Development Status :: 2 - Pre-Alpha',
'Environment :: Console', 'Environment :: Console',
'Environment :: X11 Applications :: Qt', 'Environment :: X11 Applications :: Qt',
'Intended Audience :: Developers', 'Intended Audience :: Developers',