diff --git a/src/libprs500/__init__.py b/src/libprs500/__init__.py index 462faed504..2888c20dd8 100644 --- a/src/libprs500/__init__.py +++ b/src/libprs500/__init__.py @@ -13,7 +13,7 @@ ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ''' E-book management software''' -__version__ = "0.3.86" +__version__ = "0.3.87" __docformat__ = "epytext" __author__ = "Kovid Goyal " __appname__ = 'libprs500' diff --git a/src/libprs500/devices/prs500/driver.py b/src/libprs500/devices/prs500/driver.py index b5d7953973..3ef19e2f39 100755 --- a/src/libprs500/devices/prs500/driver.py +++ b/src/libprs500/devices/prs500/driver.py @@ -905,7 +905,7 @@ class PRS500(Device): space = self.free_space(end_session=False) mspace = space[0] cspace = space[1] if space[1] >= space[2] else space[2] - if oncard and size > cspace - 1024*1024: + if oncard and size > cspace - 1024*1024: raise FreeSpaceError("There is insufficient free space "+\ "on the storage card") if not oncard and size > mspace - 1024*1024: diff --git a/src/libprs500/gui2/device.py b/src/libprs500/gui2/device.py index 3c91d6234b..3a24b92797 100644 --- a/src/libprs500/gui2/device.py +++ b/src/libprs500/gui2/device.py @@ -12,9 +12,16 @@ ## 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.Warning +import sys + from PyQt4.QtCore import QThread, SIGNAL, QObject -from libprs500.devices.prs500.driver import PRS500 +_libusb_available = False +try: + from libprs500.devices.prs500.driver import PRS500 + _libusb_available = True +except OSError, err: #libusb not availabe + print >>sys.stderr, err class DeviceDetector(QThread): ''' @@ -27,14 +34,20 @@ class DeviceDetector(QThread): @param sleep_time: Time to sleep between device probes in millisecs @type sleep_time: integer ''' - self.devices = ([PRS500, False],) + self.devices = [] + if _libusb_available: + for dclass in (PRS500,): + self.devices.append([dclass, False]) self.sleep_time = sleep_time QThread.__init__(self) def run(self): while True: for device in self.devices: - connected = device[0].is_connected() + try: + connected = device[0].is_connected() + except: + connected = False if connected and not device[1]: self.emit(SIGNAL('connected(PyQt_PyObject, PyQt_PyObject)'), device[0], True) device[1] ^= True diff --git a/src/libprs500/gui2/jobs.py b/src/libprs500/gui2/jobs.py index 66cbcc37f2..d76f8663a0 100644 --- a/src/libprs500/gui2/jobs.py +++ b/src/libprs500/gui2/jobs.py @@ -222,7 +222,11 @@ class JobManager(QAbstractTableModel): def status_update(self, id, progress): keys = self.jobs.keys() keys.sort() - row = keys.index(id) - index = self.index(row, 2) - self.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'), index, index) + try: + row = keys.index(id) + index = self.index(row, 2) + self.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'), index, index) + except ValueError: + pass + \ No newline at end of file diff --git a/src/libprs500/gui2/library.py b/src/libprs500/gui2/library.py index d1730ba7a3..7addd58bf7 100644 --- a/src/libprs500/gui2/library.py +++ b/src/libprs500/gui2/library.py @@ -12,8 +12,7 @@ ## 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. -from libprs500.ptempfile import PersistentTemporaryFile -import os, textwrap, traceback, time, re +import os, textwrap, traceback, time, re, sre_constants from datetime import timedelta, datetime from operator import attrgetter from math import cos, sin, pi @@ -24,6 +23,7 @@ from PyQt4.QtGui import QTableView, QProgressDialog, QAbstractItemView, QColor, from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, QString, \ QCoreApplication, SIGNAL, QObject, QSize, QModelIndex +from libprs500.ptempfile import PersistentTemporaryFile from libprs500.library.database import LibraryDatabase from libprs500.gui2 import NONE @@ -118,7 +118,15 @@ class BooksModel(QAbstractTableModel): text = text.replace('"'+quot.group(1)+'"', '') quot = re.search('"(.*?)"', text) tokens += text.split(' ') - return [re.compile(i, re.IGNORECASE) for i in tokens] + ans = [] + for i in tokens: + try: + ans.append(re.compile(i, re.IGNORECASE)) + except sre_constants.error: + continue + return ans + + def search(self, text, refinement): tokens = self.search_tokens(text) diff --git a/src/libprs500/gui2/main.py b/src/libprs500/gui2/main.py index 1cab2f2ed4..3d1c376086 100644 --- a/src/libprs500/gui2/main.py +++ b/src/libprs500/gui2/main.py @@ -12,15 +12,14 @@ ## 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.Warning -from libprs500.ptempfile import PersistentTemporaryFile -import os, tempfile, sys, traceback +import os, sys, traceback, StringIO, textwrap from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \ QSettings, QVariant, QSize, QThread from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox from PyQt4.QtSvg import QSvgRenderer -from libprs500 import __version__, __appname__ +from libprs500 import __version__, __appname__, iswindows, isosx from libprs500.ebooks.metadata.meta import get_metadata from libprs500.devices.errors import FreeSpaceError from libprs500.devices.interface import Device @@ -30,7 +29,7 @@ from libprs500.gui2 import APP_TITLE, warning_dialog, choose_files, error_dialog from libprs500.gui2.main_ui import Ui_MainWindow from libprs500.gui2.device import DeviceDetector, DeviceManager from libprs500.gui2.status import StatusBar -from libprs500.gui2.jobs import JobManager, JobException +from libprs500.gui2.jobs import JobManager from libprs500.gui2.dialogs.metadata_single import MetadataSingleDialog from libprs500.gui2.dialogs.metadata_bulk import MetadataBulkDialog from libprs500.gui2.dialogs.jobs import JobsDialog @@ -61,6 +60,7 @@ class Main(QObject, Ui_MainWindow): self.default_thumbnail = None self.device_error_dialog = error_dialog(self.window, 'Error communicating with device', ' ') self.device_error_dialog.setModal(Qt.NonModal) + self.tb_wrapper = textwrap.TextWrapper(width=40) ####################### Location View ######################## QObject.connect(self.location_view, SIGNAL('location_selected(PyQt_PyObject)'), self.location_selected) @@ -436,6 +436,12 @@ class Main(QObject, Ui_MainWindow): self.current_view().setCurrentIndex(self.current_view().model().index(0, 0)) + def wrap_traceback(self, tb): + tb = unicode(tb, 'utf8', 'replace') + if iswindows or isosx: + tb = '\n'.join(self.tb_wrapper.wrap(tb)) + return tb + def device_job_exception(self, id, description, exception, formatted_traceback): ''' Handle exceptions in threaded jobs. @@ -444,10 +450,11 @@ class Main(QObject, Ui_MainWindow): print >>sys.stderr, exception print >>sys.stderr, formatted_traceback if not self.device_error_dialog.isVisible(): - msg = '

%s: '%(exception.__class__.__name__,) + str(exception) + '

' - msg += '

Failed to perform job: '+description - msg += '

Further device related error messages will not be shown while this message is visible.' - msg += '

Detailed traceback:

'+formatted_traceback+'
' + msg = u'

%s: '%(exception.__class__.__name__,) + unicode(str(exception), 'utf8', 'replace') + u'

' + msg += u'

Failed to perform job: '+description + msg += u'

Further device related error messages will not be shown while this message is visible.' + msg += u'

Detailed traceback:

'
+            msg += self.wrap_traceback(formatted_traceback)
             self.device_error_dialog.setText(msg)
             self.device_error_dialog.show()
         
@@ -456,10 +463,10 @@ class Main(QObject, Ui_MainWindow):
     def read_settings(self):
         settings = QSettings()
         settings.beginGroup("MainWindow")
-        self.window.resize(settings.value("size", QVariant(QSize(1000, 700))).toSize())
+        self.window.resize(settings.value("size", QVariant(QSize(800, 600))).toSize())
         settings.endGroup()
-        self.database_path = settings.value("database path", QVariant(os.path\
-                                    .expanduser("~/library1.db"))).toString()
+        self.database_path = settings.value("database path", 
+                QVariant(os.path.join(os.path.expanduser('~'),'library1.db'))).toString()
     
     def write_settings(self):
         settings = QSettings()
@@ -485,13 +492,15 @@ class Main(QObject, Ui_MainWindow):
                 e.ignore()
                 
     def unhandled_exception(self, type, value, tb):
-        traceback.print_exception(type, value, tb, file=sys.stderr)
+        sio = StringIO.StringIO()
+        traceback.print_exception(type, value, tb, file=sio)
+        fe = sio.getvalue()
+        print >>sys.stderr, fe
         if type == KeyboardInterrupt:
             self.window.close()
             self.window.thread().exit(0)
-        fe = '\n'.join(traceback.format_exception(type, value, tb))
-        msg = '

' + str(value) + '

' - msg += '

Detailed traceback:

'+fe+'
' + msg = '

' + unicode(str(value), 'utf8', 'replace') + '

' + msg += '

Detailed traceback:

'+self.wrap_traceback(fe)+'
' d = error_dialog(self.window, 'ERROR: Unhandled exception', msg) d.exec_() @@ -502,6 +511,7 @@ def main(): window.setWindowTitle(APP_TITLE) QCoreApplication.setOrganizationName("KovidsBrain") QCoreApplication.setApplicationName(APP_TITLE) + initialize_file_icon_provider() main = Main(window) sys.excepthook = main.unhandled_exception diff --git a/src/libprs500/library/database.py b/src/libprs500/library/database.py index ec9de2e581..655942aa5f 100644 --- a/src/libprs500/library/database.py +++ b/src/libprs500/library/database.py @@ -90,7 +90,10 @@ class LibraryDatabase(object): obj = conn.execute('INSERT INTO books(title, timestamp) VALUES (?,?)', (book['title'], book['timestamp'])) id = obj.lastrowid - authors = book['authors'].split('&') + authors = book['authors'] + if not authors: + authors = 'Unknown' + authors = authors.split('&') for a in authors: author = conn.execute('SELECT id from authors WHERE name=?', (a,)).fetchone() if author: @@ -644,7 +647,7 @@ class LibraryDatabase(object): '''Cover as a data string or None''' id = self.id(index) data = self.conn.execute('SELECT data FROM covers WHERE book=?', (id,)).fetchone() - if not data: + if not data or not data[0]: return None return(decompress(data[0])) diff --git a/src/libprs500/ptempfile.py b/src/libprs500/ptempfile.py index bc3ac963b7..84353c14a7 100644 --- a/src/libprs500/ptempfile.py +++ b/src/libprs500/ptempfile.py @@ -58,5 +58,5 @@ def PersistentTemporaryFile(suffix="", prefix="", dir=None): prefix = "" fd, name = tempfile.mkstemp(suffix, __appname__+"_"+ __version__+"_" + prefix, dir=dir) - _file = os.fdopen(fd, "wb") + _file = os.fdopen(fd, 'w+b') return _TemporaryFileWrapper(_file, name) diff --git a/windows_installer.py b/windows_installer.py index a85c26591e..8645721b5c 100644 --- a/windows_installer.py +++ b/windows_installer.py @@ -355,7 +355,8 @@ r''' class BuildEXE(build_exe): - manifest_resource_id = 0 + manifest_resource_id = 0 + QT_PREFIX = r'C:\\Qt\\4.3.0' MANIFEST_TEMPLATE = ''' @@ -393,7 +394,20 @@ class BuildEXE(build_exe): print 'Adding', qtxmldll shutil.copyfile(qtxmldll, os.path.join(self.dist_dir, os.path.basename(qtxmldll))) + print 'Adding plugins...', + qt_prefix = self.QT_PREFIX + if qtsvgdll: + qt_prefix = os.path.dirname(os.path.dirname(qtsvgdll)) + plugdir = os.path.join(qt_prefix, 'plugins') + for d in ('imageformats', 'codecs', 'iconengines'): + print d, + imfd = os.path.join(plugdir, d) + tg = os.path.join(self.dist_dir, d) + if os.path.exists(tg): + shutil.rmtree(tg) + shutil.copytree(imfd, tg) + print print print 'Building Installer' installer = NSISInstaller(APPNAME, self.dist_dir, 'dist')