version 0.4.4

1) Add support for Sup, Sub and Space
2) Fix laying out of ImageBlocks in a Canvas
3) Integrate lrfviewer into main GUI
This commit is contained in:
Kovid Goyal 2007-09-24 19:51:12 +00:00
parent a050a683c1
commit 4815ecf0ae
10 changed files with 2254 additions and 85 deletions

View File

@ -13,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.
''' E-book management software''' ''' E-book management software'''
__version__ = "0.4.3" __version__ = "0.4.4"
__docformat__ = "epytext" __docformat__ = "epytext"
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
__appname__ = 'libprs500' __appname__ = 'libprs500'

View File

@ -32,6 +32,9 @@ class LRFDocument(LRFMetaFile):
self.page_trees = [] self.page_trees = []
self.font_map = {} self.font_map = {}
self.image_map = {} self.image_map = {}
self.keep_parsing = True
def parse(self):
self._parse_objects() self._parse_objects()
self.toc = None self.toc = None
self.metadata = LRFDocument.temp() self.metadata = LRFDocument.temp()
@ -53,9 +56,13 @@ class LRFDocument(LRFMetaFile):
if ord(array.array("i",[1]).tostring()[0])==0: #big-endian if ord(array.array("i",[1]).tostring()[0])==0: #big-endian
obj_array.byteswap() obj_array.byteswap()
for i in range(self.number_of_objects): for i in range(self.number_of_objects):
if not self.keep_parsing:
break
objid, objoff, objsize = obj_array[i*4:i*4+3] objid, objoff, objsize = obj_array[i*4:i*4+3]
self._parse_object(objid, objoff, objsize) self._parse_object(objid, objoff, objsize)
for obj in self.objects.values(): for obj in self.objects.values():
if not self.keep_parsing:
break
if hasattr(obj, 'initialize'): if hasattr(obj, 'initialize'):
obj.initialize() obj.initialize()
@ -155,6 +162,7 @@ def main(args=sys.argv, logger=None):
o.write(u'<?xml version="1.0" encoding="UTF-8"?>\n') o.write(u'<?xml version="1.0" encoding="UTF-8"?>\n')
logger.info('Parsing LRF...') logger.info('Parsing LRF...')
d = LRFDocument(open(args[1], 'rb')) d = LRFDocument(open(args[1], 'rb'))
d.parse()
logger.info('Creating XML...') logger.info('Creating XML...')
o.write(d.to_xml()) o.write(d.to_xml())
logger.info('LRS written to '+opts.out) logger.info('LRS written to '+opts.out)

View File

@ -47,5 +47,6 @@
<file>images/sd.svg</file> <file>images/sd.svg</file>
<file>images/sync.svg</file> <file>images/sync.svg</file>
<file>images/trash.svg</file> <file>images/trash.svg</file>
<file>images/view.svg</file>
</qresource> </qresource>
</RCC> </RCC>

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 73 KiB

View File

@ -108,12 +108,9 @@ class _Canvas(QGraphicsRectItem):
canvas.setParentItem(self) canvas.setParentItem(self)
canvas.setPos(x, y) canvas.setPos(x, y)
canvas.has_content = False canvas.has_content = False
oy = self.current_y canvas.put_objects()
for block, x, y in canvas.items: self.current_y += canvas.max_y
self.layout_block(block, x, oy+y)
self.current_y = oy + canvas.max_y
def layout_text_block(self, block, x, y): def layout_text_block(self, block, x, y):
textwidth = block.bs.blockwidth - block.bs.sidemargin textwidth = block.bs.blockwidth - block.bs.sidemargin
if block.max_y == 0 or not block.lines: # Empty block skipping if block.max_y == 0 or not block.lines: # Empty block skipping
@ -148,7 +145,11 @@ class _Canvas(QGraphicsRectItem):
rl.has_content = False rl.has_content = False
def layout_image_block(self, ib, x, y): def layout_image_block(self, ib, x, y):
if self.current_y + ib.height > self.max_y-y and self.current_y < 5: mw, mh = self.max_x - x, self.max_y - y
print self, mw, mh
if ib.width > mw or ib.height > mh:
ib.resize(mw, mh)
if self.current_y + ib.height > self.max_y-y and self.current_y > 5:
self.is_full = True self.is_full = True
else: else:
br = ib.boundingRect() br = ib.boundingRect()
@ -201,6 +202,10 @@ class Canvas(_Canvas, ContentObject):
if item: if item:
self.items.append((item, po.x, po.y)) self.items.append((item, po.x, po.y))
def put_objects(self):
for block, x, y in self.items:
self.layout_block(block, x, y)
def layout_block(self, block, x, y): def layout_block(self, block, x, y):
block.reset() block.reset()
_Canvas.layout_block(self, block, x, y) _Canvas.layout_block(self, block, x, y)

View File

@ -33,6 +33,7 @@ class RenderWorker(QThread):
def __init__(self, parent, lrf_stream, logger, opts): def __init__(self, parent, lrf_stream, logger, opts):
QThread.__init__(self, parent) QThread.__init__(self, parent)
self.stream, self.logger, self.opts = lrf_stream, logger, opts self.stream, self.logger, self.opts = lrf_stream, logger, opts
self.aborted = False
self.lrf = None self.lrf = None
self.document = None self.document = None
self.exception = None self.exception = None
@ -40,25 +41,36 @@ class RenderWorker(QThread):
def run(self): def run(self):
try: try:
self.lrf = LRFDocument(self.stream) self.lrf = LRFDocument(self.stream)
self.lrf.parse()
self.stream.close() self.stream.close()
self.stream = None self.stream = None
if self.aborted:
self.lrf = None
except Exception, err: except Exception, err:
self.lrf, self.stream = None, None
self.exception = err self.exception = err
self.formatted_traceback = traceback.format_exc() self.formatted_traceback = traceback.format_exc()
self.emit(SIGNAL('parsed()'))
def abort(self):
if self.lrf is not None:
self.aborted = True
self.lrf.keep_parsing = False
class Main(QObject, Ui_MainWindow, MainWindow): class Main(MainWindow, Ui_MainWindow):
def __init__(self, window, stream, logger, opts):
QObject.__init__(self) def __init__(self, stream, logger, opts, parent=None):
MainWindow.__init__(self, parent)
Ui_MainWindow.__init__(self) Ui_MainWindow.__init__(self)
self.setupUi(window) self.setupUi(self)
self.window = window self.setAttribute(Qt.WA_DeleteOnClose)
self.setWindowTitle(__appname__ + ' - LRF Viewer')
self.logger = logger self.logger = logger
self.file_name = os.path.basename(stream.name) if hasattr(stream, 'name') else '' self.file_name = os.path.basename(stream.name) if hasattr(stream, 'name') else ''
self.opts = opts self.opts = opts
self.document = None self.document = None
self.renderer = RenderWorker(self, stream, logger, opts) self.renderer = RenderWorker(self, stream, logger, opts)
QObject.connect(self.renderer, SIGNAL('parsed()'), self.parsed, Qt.QueuedConnection) QObject.connect(self.renderer, SIGNAL('finished()'), self.parsed, Qt.QueuedConnection)
self.document = Document(self.logger, self.opts) self.document = Document(self.logger, self.opts)
QObject.connect(self.document, SIGNAL('chapter_rendered(int)'), self.chapter_rendered) QObject.connect(self.document, SIGNAL('chapter_rendered(int)'), self.chapter_rendered)
QObject.connect(self.document, SIGNAL('page_changed(PyQt_PyObject)'), self.page_changed) QObject.connect(self.document, SIGNAL('page_changed(PyQt_PyObject)'), self.page_changed)
@ -85,6 +97,9 @@ class Main(QObject, Ui_MainWindow, MainWindow):
self.graphics_view.setRenderHint(QPainter.TextAntialiasing, True) self.graphics_view.setRenderHint(QPainter.TextAntialiasing, True)
self.graphics_view.setRenderHint(QPainter.SmoothPixmapTransform, True) self.graphics_view.setRenderHint(QPainter.SmoothPixmapTransform, True)
self.closed = False
def page_changed(self, num): def page_changed(self, num):
self.slider.setValue(num) self.slider.setValue(num)
self.spin_box.setValue(num) self.spin_box.setValue(num)
@ -98,13 +113,13 @@ class Main(QObject, Ui_MainWindow, MainWindow):
try: try:
self.document.search(search) self.document.search(search)
except StopIteration: except StopIteration:
error_dialog(self.window, 'No matches found', '<b>No matches</b> for the search phrase <i>%s</i> were found.'%(search,)).exec_() error_dialog(self, 'No matches found', '<b>No matches</b> for the search phrase <i>%s</i> were found.'%(search,)).exec_()
def parsed(self, *args): def parsed(self):
if self.renderer.lrf is not None: if not self.renderer.aborted and self.renderer.lrf is not None:
self.graphics_view.setMinimumSize(self.renderer.lrf.device_info.width+5, self.graphics_view.setMinimumSize(self.renderer.lrf.device_info.width+5,
self.renderer.lrf.device_info.height) self.renderer.lrf.device_info.height)
self.window.setWindowTitle(self.renderer.lrf.metadata.title + ' - ' + __appname__) self.setWindowTitle(self.renderer.lrf.metadata.title + ' - ' + __appname__)
self.document_title = self.renderer.lrf.metadata.title self.document_title = self.renderer.lrf.metadata.title
if self.opts.profile: if self.opts.profile:
import cProfile import cProfile
@ -123,7 +138,7 @@ class Main(QObject, Ui_MainWindow, MainWindow):
self.spin_box.setSuffix(' of %d'%(self.document.num_of_pages,)) self.spin_box.setSuffix(' of %d'%(self.document.num_of_pages,))
self.spin_box.updateGeometry() self.spin_box.updateGeometry()
self.stack.setCurrentIndex(0) self.stack.setCurrentIndex(0)
else: elif self.renderer.exception is not None:
exception = self.renderer.exception exception = self.renderer.exception
print >>sys.stderr, 'Error rendering document' print >>sys.stderr, 'Error rendering document'
print >>sys.stderr, exception print >>sys.stderr, exception
@ -132,7 +147,7 @@ class Main(QObject, Ui_MainWindow, MainWindow):
msg += u'<p>Failed to render document</p>' msg += u'<p>Failed to render document</p>'
msg += u'<p>Detailed <b>traceback</b>:<pre>' msg += u'<p>Detailed <b>traceback</b>:<pre>'
msg += self.renderer.formatted_traceback + '</pre>' msg += self.renderer.formatted_traceback + '</pre>'
d = ConversionErrorDialog(self.window, 'Error while rendering file', msg) d = ConversionErrorDialog(self, 'Error while rendering file', msg)
d.exec_() d.exec_()
def chapter_rendered(self, num): def chapter_rendered(self, num):
@ -159,14 +174,22 @@ class Main(QObject, Ui_MainWindow, MainWindow):
def back(self, triggered): def back(self, triggered):
self.document.back() self.document.back()
def closeEvent(self, event):
if self.renderer.isRunning():
self.renderer.abort()
self.renderer.wait()
self.emit(SIGNAL('viewer_closed(PyQt_PyObject)'), self)
event.accept()
def file_renderer(stream, logger, opts): def file_renderer(stream, opts, parent=None, logger=None):
from PyQt4.Qt import QMainWindow if logger is None:
window = QMainWindow() level = logging.DEBUG if opts.verbose else logging.INFO
window.setAttribute(Qt.WA_DeleteOnClose) logger = logging.getLogger('lrfviewer')
window.setWindowTitle(__appname__ + ' - LRF Viewer') setup_cli_handlers(logger, level)
return Main(window, stream, logger, opts)
return Main(stream, logger, opts, parent=parent)
def option_parser(): def option_parser():
@ -191,18 +214,14 @@ def main(args=sys.argv, logger=None):
if len(args) != 2: if len(args) != 2:
parser.print_help() parser.print_help()
return 1 return 1
if logger is None: pid = os.fork() if islinux else -1
level = logging.DEBUG if opts.verbose else logging.INFO
logger = logging.getLogger('lrf2lrs')
setup_cli_handlers(logger, level)
pid = os.fork() if False and islinux else -1
if pid <= 0: if pid <= 0:
app = QApplication(args) app = QApplication(args)
QCoreApplication.setOrganizationName(ORG_NAME) QCoreApplication.setOrganizationName(ORG_NAME)
QCoreApplication.setApplicationName(APP_UID) QCoreApplication.setApplicationName(APP_UID)
main = file_renderer(open(args[1], 'rb'), logger, opts) main = file_renderer(open(args[1], 'rb'), opts, logger=logger)
sys.excepthook = main.unhandled_exception sys.excepthook = main.unhandled_exception
main.window.show() main.show()
main.render() main.render()
return app.exec_() return app.exec_()
return 0 return 0

View File

@ -42,6 +42,11 @@ class PixmapItem(QGraphicsPixmapItem):
self.height, self.width = ysize, xsize self.height, self.width = ysize, xsize
self.setTransformationMode(Qt.SmoothTransformation) self.setTransformationMode(Qt.SmoothTransformation)
self.setShapeMode(QGraphicsPixmapItem.BoundingRectShape) self.setShapeMode(QGraphicsPixmapItem.BoundingRectShape)
def resize(self, width, height):
p = self.pixmap()
self.setPixmap(p.scaled(width, height, Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
self.width, self.height = width, height
class Plot(PixmapItem): class Plot(PixmapItem):
@ -213,7 +218,10 @@ class TextBlock(object):
def end_link(self): def end_link(self):
if self.current_line is not None: if self.current_line is not None:
self.current_line.end_link() self.current_line.end_link()
def close_valign(self):
if self.current_line is not None:
self.current_line.valign = None
def populate(self, tb): def populate(self, tb):
self.create_line() self.create_line()
@ -260,10 +268,13 @@ class TextBlock(object):
self.end_line() self.end_line()
self.create_line() self.create_line()
self.current_line.add_plot(plot) self.current_line.add_plot(plot)
elif i.name == 'Sup': elif i.name in ['Sup', 'Sub']:
open_containers.append((('current_style', self.current_style.copy()),)) if self.current_line is None:
elif i.name == 'Sub': self.create_line()
open_containers.append((('current_style', self.current_style.copy()),)) self.current_line.valign = i.name
open_containers.append(((self.close_valign, []),))
elif i.name == 'Space':
self.current_line.add_space(i.attrs['xsize'])
elif i.name == 'EmpLine': elif i.name == 'EmpLine':
if i.attrs: if i.attrs:
open_containers.append((('current_style', self.current_style.copy()),)) open_containers.append((('current_style', self.current_style.copy()),))
@ -356,6 +367,7 @@ class Line(QGraphicsItem):
self.height, self.descent = 0, 0 self.height, self.descent = 0, 0
self.links = collections.deque() self.links = collections.deque()
self.current_link = None self.current_link = None
self.valign = None
def start_link(self, refobj, slot): def start_link(self, refobj, slot):
self.current_link = [self.current_width, sys.maxint, refobj, slot] self.current_link = [self.current_width, sys.maxint, refobj, slot]
@ -380,6 +392,8 @@ class Line(QGraphicsItem):
processed = False processed = False
matches = self.__class__.whitespace.finditer(phrase) matches = self.__class__.whitespace.finditer(phrase)
font = QFont(ts.font) font = QFont(ts.font)
if self.valign is not None:
font.setPixelSize(font.pixelSize()/1.5)
fm = QFontMetrics(font) fm = QFontMetrics(font)
single_space_width = fm.width(' ') single_space_width = fm.width(' ')
height, descent = fm.height(), fm.descent() height, descent = fm.height(), fm.descent()
@ -423,7 +437,7 @@ class Line(QGraphicsItem):
return phrase_pos, False return phrase_pos, False
def commit(self, word, width, height, descent, ts, font): def commit(self, word, width, height, descent, ts, font):
self.tokens.append(Word(word, width, height, ts, font)) self.tokens.append(Word(word, width, height, ts, font, self.valign))
self.current_width += width self.current_width += width
self.height = max(self.height, height) self.height = max(self.height, height)
self.descent = max(self.descent, descent) self.descent = max(self.descent, descent)
@ -483,7 +497,6 @@ class Line(QGraphicsItem):
x += tok x += tok
elif isinstance(tok, Word): elif isinstance(tok, Word):
painter.setFont(tok.font) painter.setFont(tok.font)
p = painter.pen()
if tok.highlight: if tok.highlight:
painter.save() painter.save()
painter.setPen(QPen(Qt.NoPen)) painter.setPen(QPen(Qt.NoPen))
@ -491,8 +504,12 @@ class Line(QGraphicsItem):
painter.drawRect(x, 0, tok.width, tok.height) painter.drawRect(x, 0, tok.width, tok.height)
painter.restore() painter.restore()
painter.setPen(QPen(tok.text_color)) painter.setPen(QPen(tok.text_color))
painter.drawText(x, y, tok.string) if tok.valign is None:
painter.setPen(p) painter.drawText(x, y, tok.string)
elif tok.valign == 'Sub':
painter.drawText(x+1, y+self.descent/1.5, tok.string)
elif tok.valign == 'Sup':
painter.drawText(x+1, y-2.*self.descent, tok.string)
x += tok.width x += tok.width
else: else:
painter.drawPixmap(x, 0, tok.pixmap()) painter.drawPixmap(x, 0, tok.pixmap())
@ -546,11 +563,12 @@ class Line(QGraphicsItem):
class Word(object): class Word(object):
def __init__(self, string, width, height, ts, font): def __init__(self, string, width, height, ts, font, valign):
self.string, self.width, self.height = QString(string), width, height self.string, self.width, self.height = QString(string), width, height
self.font = font self.font = font
self.text_color = ts.textcolor self.text_color = ts.textcolor
self.highlight = False self.highlight = False
self.valign = valign
def main(args=sys.argv): def main(args=sys.argv):
return 0 return 0

View File

@ -12,7 +12,7 @@
## 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.Warning ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.Warning
import os, sys, textwrap import os, sys, textwrap, cStringIO, collections
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \ from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \
QSettings, QVariant, QSize, QThread QSettings, QVariant, QSize, QThread
@ -41,8 +41,10 @@ from libprs500.gui2.dialogs.jobs import JobsDialog
from libprs500.gui2.dialogs.conversion_error import ConversionErrorDialog from libprs500.gui2.dialogs.conversion_error import ConversionErrorDialog
from libprs500.gui2.dialogs.lrf_single import LRFSingleDialog from libprs500.gui2.dialogs.lrf_single import LRFSingleDialog
from libprs500.gui2.dialogs.password import PasswordDialog from libprs500.gui2.dialogs.password import PasswordDialog
from libprs500.gui2.lrf_renderer.main import file_renderer
from libprs500.gui2.lrf_renderer.main import option_parser as lrfviewerop
class Main(QObject, Ui_MainWindow, MainWindow): class Main(MainWindow, Ui_MainWindow):
def set_default_thumbnail(self, height): def set_default_thumbnail(self, height):
r = QSvgRenderer(':/images/book.svg') r = QSvgRenderer(':/images/book.svg')
@ -53,24 +55,25 @@ class Main(QObject, Ui_MainWindow, MainWindow):
p.end() p.end()
self.default_thumbnail = (pixmap.width(), pixmap.height(), pixmap_to_data(pixmap)) self.default_thumbnail = (pixmap.width(), pixmap.height(), pixmap_to_data(pixmap))
def __init__(self, window): def __init__(self, parent=None):
QObject.__init__(self) MainWindow.__init__(self, parent)
Ui_MainWindow.__init__(self) Ui_MainWindow.__init__(self)
self.window = window self.setupUi(self)
self.setupUi(window) self.setWindowTitle(__appname__)
self.read_settings() self.read_settings()
self.job_manager = JobManager() self.job_manager = JobManager()
self.jobs_dialog = JobsDialog(self.window, self.job_manager) self.jobs_dialog = JobsDialog(self, self.job_manager)
self.device_manager = None self.device_manager = None
self.upload_memory = {} self.upload_memory = {}
self.delete_memory = {} self.delete_memory = {}
self.conversion_jobs = {} self.conversion_jobs = {}
self.persistent_files = [] self.persistent_files = []
self.default_thumbnail = None self.default_thumbnail = None
self.device_error_dialog = ConversionErrorDialog(self.window, 'Error communicating with device', ' ') self.device_error_dialog = ConversionErrorDialog(self, 'Error communicating with device', ' ')
self.device_error_dialog.setModal(Qt.NonModal) self.device_error_dialog.setModal(Qt.NonModal)
self.tb_wrapper = textwrap.TextWrapper(width=40) self.tb_wrapper = textwrap.TextWrapper(width=40)
self.device_connected = False self.device_connected = False
self.viewers = collections.deque()
####################### Location View ######################## ####################### Location View ########################
QObject.connect(self.location_view, SIGNAL('location_selected(PyQt_PyObject)'), QObject.connect(self.location_view, SIGNAL('location_selected(PyQt_PyObject)'),
self.location_selected) self.location_selected)
@ -83,7 +86,7 @@ class Main(QObject, Ui_MainWindow, MainWindow):
####################### Status Bar ##################### ####################### Status Bar #####################
self.status_bar = StatusBar(self.jobs_dialog) self.status_bar = StatusBar(self.jobs_dialog)
self.window.setStatusBar(self.status_bar) self.setStatusBar(self.status_bar)
QObject.connect(self.job_manager, SIGNAL('job_added(int)'), self.status_bar.job_added) QObject.connect(self.job_manager, SIGNAL('job_added(int)'), self.status_bar.job_added)
QObject.connect(self.job_manager, SIGNAL('job_done(int)'), self.status_bar.job_done) QObject.connect(self.job_manager, SIGNAL('job_done(int)'), self.status_bar.job_done)
@ -105,6 +108,7 @@ class Main(QObject, Ui_MainWindow, MainWindow):
QObject.connect(sm.actions()[0], SIGNAL('triggered(bool)'), self.sync_to_main_memory) QObject.connect(sm.actions()[0], SIGNAL('triggered(bool)'), self.sync_to_main_memory)
QObject.connect(sm.actions()[1], SIGNAL('triggered(bool)'), self.sync_to_card) QObject.connect(sm.actions()[1], SIGNAL('triggered(bool)'), self.sync_to_card)
QObject.connect(self.action_save, SIGNAL("triggered(bool)"), self.save_to_disk) QObject.connect(self.action_save, SIGNAL("triggered(bool)"), self.save_to_disk)
QObject.connect(self.action_view, SIGNAL("triggered(bool)"), self.view_book)
self.action_sync.setMenu(sm) self.action_sync.setMenu(sm)
self.action_edit.setMenu(md) self.action_edit.setMenu(md)
nm = QMenu() nm = QMenu()
@ -141,8 +145,7 @@ class Main(QObject, Ui_MainWindow, MainWindow):
self.memory_view.connect_dirtied_signal(self.upload_booklists) self.memory_view.connect_dirtied_signal(self.upload_booklists)
self.card_view.connect_dirtied_signal(self.upload_booklists) self.card_view.connect_dirtied_signal(self.upload_booklists)
window.closeEvent = self.close_event self.show()
window.show()
self.stack.setCurrentIndex(0) self.stack.setCurrentIndex(0)
self.library_view.migrate_database() self.library_view.migrate_database()
self.library_view.sortByColumn(3, Qt.DescendingOrder) self.library_view.sortByColumn(3, Qt.DescendingOrder)
@ -254,7 +257,7 @@ class Main(QObject, Ui_MainWindow, MainWindow):
''' '''
Add books from the local filesystem to either the library or the device. Add books from the local filesystem to either the library or the device.
''' '''
books = choose_files(self.window, 'add books dialog dir', 'Select books', books = choose_files(self, 'add books dialog dir', 'Select books',
filters=[('Books', BOOK_EXTENSIONS)]) filters=[('Books', BOOK_EXTENSIONS)])
if not books: if not books:
return return
@ -312,7 +315,7 @@ class Main(QObject, Ui_MainWindow, MainWindow):
if isinstance(exception, FreeSpaceError): if isinstance(exception, FreeSpaceError):
where = 'in main memory.' if 'memory' in str(exception) else 'on the storage card.' where = 'in main memory.' if 'memory' in str(exception) else 'on the storage card.'
titles = '\n'.join(['<li>'+mi['title']+'</li>' for mi in metadata]) titles = '\n'.join(['<li>'+mi['title']+'</li>' for mi in metadata])
d = error_dialog(self.window, 'No space on device', d = error_dialog(self, 'No space on device',
'<p>Cannot upload books to device there is no more free space available '+where+ '<p>Cannot upload books to device there is no more free space available '+where+
'</p>\n<ul>%s</ul>'%(titles,)) '</p>\n<ul>%s</ul>'%(titles,))
d.exec_() d.exec_()
@ -380,12 +383,12 @@ class Main(QObject, Ui_MainWindow, MainWindow):
''' '''
rows = self.library_view.selectionModel().selectedRows() rows = self.library_view.selectionModel().selectedRows()
if not rows or len(rows) == 0: if not rows or len(rows) == 0:
d = error_dialog(self.window, 'Cannot edit metadata', 'No books selected') d = error_dialog(self, 'Cannot edit metadata', 'No books selected')
d.exec_() d.exec_()
return return
changed = False changed = False
for row in rows: for row in rows:
if MetadataSingleDialog(self.window, row.row(), if MetadataSingleDialog(self, row.row(),
self.library_view.model().db).changed: self.library_view.model().db).changed:
changed = True changed = True
@ -399,10 +402,10 @@ class Main(QObject, Ui_MainWindow, MainWindow):
''' '''
rows = [r.row() for r in self.library_view.selectionModel().selectedRows()] rows = [r.row() for r in self.library_view.selectionModel().selectedRows()]
if not rows or len(rows) == 0: if not rows or len(rows) == 0:
d = error_dialog(self.window, 'Cannot edit metadata', 'No books selected') d = error_dialog(self, 'Cannot edit metadata', 'No books selected')
d.exec_() d.exec_()
return return
if MetadataBulkDialog(self.window, rows, self.library_view.model().db).changed: if MetadataBulkDialog(self, rows, self.library_view.model().db).changed:
self.library_view.model().resort(reset=False) self.library_view.model().resort(reset=False)
self.library_view.model().research() self.library_view.model().research()
@ -451,7 +454,7 @@ class Main(QObject, Ui_MainWindow, MainWindow):
self.status_bar.showMessage('Sending books to device.', 5000) self.status_bar.showMessage('Sending books to device.', 5000)
if bad: if bad:
bad = '\n'.join('<li>%s</li>'%(i,) for i in bad) bad = '\n'.join('<li>%s</li>'%(i,) for i in bad)
d = warning_dialog(self.window, 'No suitable formats', d = warning_dialog(self, 'No suitable formats',
'Could not upload the following books to the device, as no suitable formats were found:<br><ul>%s</ul>'%(bad,)) 'Could not upload the following books to the device, as no suitable formats were found:<br><ul>%s</ul>'%(bad,))
d.exec_() d.exec_()
@ -462,10 +465,10 @@ class Main(QObject, Ui_MainWindow, MainWindow):
def save_to_disk(self, checked): def save_to_disk(self, checked):
rows = self.current_view().selectionModel().selectedRows() rows = self.current_view().selectionModel().selectedRows()
if not rows or len(rows) == 0: if not rows or len(rows) == 0:
d = error_dialog(self.window, 'Cannot save to disk', 'No books selected') d = error_dialog(self, 'Cannot save to disk', 'No books selected')
d.exec_() d.exec_()
return return
dir = choose_dir(self.window, 'save to disk dialog', 'Choose destination directory') dir = choose_dir(self, 'save to disk dialog', 'Choose destination directory')
if not dir: if not dir:
return return
if self.current_view() == self.library_view: if self.current_view() == self.library_view:
@ -515,7 +518,7 @@ class Main(QObject, Ui_MainWindow, MainWindow):
self.fetch_news('newsweek', 'Newsweek') self.fetch_news('newsweek', 'Newsweek')
def fetch_news_nytimes(self, checked): def fetch_news_nytimes(self, checked):
d = PasswordDialog(self.window, 'nytimes info dialog', d = PasswordDialog(self, 'nytimes info dialog',
'<p>Please enter your username and password for nytimes.com<br>If you do not have, you can <a href="http://www.nytimes.com/gst/regi.html">register</a> for free.<br>Without a registration, some articles will not be downloaded correctly. Click OK to proceed.') '<p>Please enter your username and password for nytimes.com<br>If you do not have, you can <a href="http://www.nytimes.com/gst/regi.html">register</a> for free.<br>Without a registration, some articles will not be downloaded correctly. Click OK to proceed.')
d.exec_() d.exec_()
if d.result() == QDialog.Accepted: if d.result() == QDialog.Accepted:
@ -526,18 +529,18 @@ class Main(QObject, Ui_MainWindow, MainWindow):
############################### Convert #################################### ############################### Convert ####################################
def convert_bulk(self, checked): def convert_bulk(self, checked):
d = error_dialog(self.window, 'Cannot convert', 'Not yet implemented.') d = error_dialog(self, 'Cannot convert', 'Not yet implemented.')
d.exec_() d.exec_()
def convert_single(self, checked): def convert_single(self, checked):
rows = self.library_view.selectionModel().selectedRows() rows = self.library_view.selectionModel().selectedRows()
if not rows or len(rows) == 0: if not rows or len(rows) == 0:
d = error_dialog(self.window, 'Cannot convert', 'No books selected') d = error_dialog(self, 'Cannot convert', 'No books selected')
d.exec_() d.exec_()
changed = False changed = False
for row in [r.row() for r in rows]: for row in [r.row() for r in rows]:
d = LRFSingleDialog(self.window, self.library_view.model().db, row) d = LRFSingleDialog(self, self.library_view.model().db, row)
if d.selected_format: if d.selected_format:
d.exec_() d.exec_()
if d.result() == QDialog.Accepted: if d.result() == QDialog.Accepted:
@ -573,6 +576,40 @@ class Main(QObject, Ui_MainWindow, MainWindow):
data.close() data.close()
self.status_bar.showMessage(description + ' completed', 2000) self.status_bar.showMessage(description + ' completed', 2000)
#############################View book######################################
def view_book(self, triggered):
rows = self.library_view.selectionModel().selectedRows()
if not rows or len(rows) == 0:
d = error_dialog(self, 'Cannot view', 'No book selected')
d.exec_()
return
row = rows[0].row()
formats = self.library_view.model().db.formats(row)
title = self.library_view.model().db.title(row)
id = self.library_view.model().db.id(row)
if 'LRF' not in formats.upper():
d = error_dialog(self, 'Cannot view', '%s is not available in LRF format. Please convert it first.'%(title,))
d.exec_()
return
data = cStringIO.StringIO(self.library_view.model().db.format(row, 'LRF'))
parser = lrfviewerop()
opts = parser.parse_args(['lrfviewer'])[0]
viewer = file_renderer(data, opts)
viewer.libprs500_db_id = id
viewer.show()
viewer.render()
self.viewers.append(viewer)
QObject.connect(viewer, SIGNAL('viewer_closed(PyQt_PyObject)'), self.viewer_closed)
def viewer_closed(self, viewer):
self.viewers.remove(viewer)
############################################################################
############################################################################ ############################################################################
def location_selected(self, location): def location_selected(self, location):
''' '''
@ -626,13 +663,13 @@ class Main(QObject, Ui_MainWindow, MainWindow):
msg += formatted_traceback + '</pre>' msg += formatted_traceback + '</pre>'
msg += '<p><b>Log:</b></p><pre>' msg += '<p><b>Log:</b></p><pre>'
msg += log msg += log
ConversionErrorDialog(self.window, 'Conversion Error', msg, show=True) ConversionErrorDialog(self, 'Conversion Error', msg, show=True)
def read_settings(self): def read_settings(self):
settings = QSettings() settings = QSettings()
settings.beginGroup("Main Window") settings.beginGroup("Main Window")
self.window.resize(settings.value("size", QVariant(QSize(800, 600))).toSize()) self.resize(settings.value("size", QVariant(QSize(800, 600))).toSize())
settings.endGroup() settings.endGroup()
self.database_path = settings.value("database path", self.database_path = settings.value("database path",
QVariant(os.path.join(os.path.expanduser('~'),'library1.db'))).toString() QVariant(os.path.join(os.path.expanduser('~'),'library1.db'))).toString()
@ -640,7 +677,7 @@ class Main(QObject, Ui_MainWindow, MainWindow):
def write_settings(self): def write_settings(self):
settings = QSettings() settings = QSettings()
settings.beginGroup("Main Window") settings.beginGroup("Main Window")
settings.setValue("size", QVariant(self.window.size())) settings.setValue("size", QVariant(self.size()))
settings.endGroup() settings.endGroup()
settings.beginGroup('Book Views') settings.beginGroup('Book Views')
self.library_view.write_settings() self.library_view.write_settings()
@ -648,7 +685,7 @@ class Main(QObject, Ui_MainWindow, MainWindow):
self.memory_view.write_settings() self.memory_view.write_settings()
settings.endGroup() settings.endGroup()
def close_event(self, e): def closeEvent(self, e):
msg = 'There are active jobs. Are you sure you want to quit?' msg = 'There are active jobs. Are you sure you want to quit?'
if self.job_manager.has_device_jobs(): if self.job_manager.has_device_jobs():
msg = '<p>'+__appname__ + ' is communicating with the device!<br>'+\ msg = '<p>'+__appname__ + ' is communicating with the device!<br>'+\
@ -656,7 +693,7 @@ class Main(QObject, Ui_MainWindow, MainWindow):
'Are you sure you want to quit?' 'Are you sure you want to quit?'
if self.job_manager.has_jobs(): if self.job_manager.has_jobs():
d = QMessageBox(QMessageBox.Warning, 'WARNING: Active jobs', msg, d = QMessageBox(QMessageBox.Warning, 'WARNING: Active jobs', msg,
QMessageBox.Yes|QMessageBox.No, self.window) QMessageBox.Yes|QMessageBox.No, self)
d.setIconPixmap(QPixmap(':/images/dialog_warning.svg')) d.setIconPixmap(QPixmap(':/images/dialog_warning.svg'))
d.setDefaultButton(QMessageBox.No) d.setDefaultButton(QMessageBox.No)
if d.exec_() != QMessageBox.Yes: if d.exec_() != QMessageBox.Yes:
@ -669,18 +706,15 @@ class Main(QObject, Ui_MainWindow, MainWindow):
def main(args=sys.argv): def main(args=sys.argv):
from PyQt4.Qt import QApplication, QMainWindow from PyQt4.Qt import QApplication
pid = os.fork() if islinux else -1 pid = os.fork() if islinux else -1
if pid <= 0: if pid <= 0:
app = QApplication(args) app = QApplication(args)
window = QMainWindow()
window.setWindowTitle(__appname__)
QCoreApplication.setOrganizationName(ORG_NAME) QCoreApplication.setOrganizationName(ORG_NAME)
QCoreApplication.setApplicationName(APP_UID) QCoreApplication.setApplicationName(APP_UID)
initialize_file_icon_provider() initialize_file_icon_provider()
main = Main(window) main = Main()
sys.excepthook = main.unhandled_exception sys.excepthook = main.unhandled_exception
QObject.connect(app, SIGNAL('lastWindowClosed()'), app.quit)
return app.exec_() return app.exec_()
return 0 return 0

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>787</width> <width>865</width>
<height>822</height> <height>822</height>
</rect> </rect>
</property> </property>
@ -316,8 +316,8 @@
</property> </property>
<property name="iconSize" > <property name="iconSize" >
<size> <size>
<width>64</width> <width>48</width>
<height>64</height> <height>48</height>
</size> </size>
</property> </property>
<property name="toolButtonStyle" > <property name="toolButtonStyle" >
@ -338,6 +338,7 @@
<addaction name="separator" /> <addaction name="separator" />
<addaction name="action_news" /> <addaction name="action_news" />
<addaction name="action_convert" /> <addaction name="action_convert" />
<addaction name="action_view" />
</widget> </widget>
<widget class="QStatusBar" name="statusBar" > <widget class="QStatusBar" name="statusBar" >
<property name="mouseTracking" > <property name="mouseTracking" >
@ -421,6 +422,14 @@
<string>Convert E-books</string> <string>Convert E-books</string>
</property> </property>
</action> </action>
<action name="action_view" >
<property name="icon" >
<iconset resource="images.qrc" >:/images/view.svg</iconset>
</property>
<property name="text" >
<string>View</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -15,9 +15,13 @@
import StringIO, traceback, sys import StringIO, traceback, sys
from PyQt4.QtGui import QMainWindow
from libprs500.gui2.dialogs.conversion_error import ConversionErrorDialog from libprs500.gui2.dialogs.conversion_error import ConversionErrorDialog
class MainWindow(object): class MainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
def unhandled_exception(self, type, value, tb): def unhandled_exception(self, type, value, tb):
sio = StringIO.StringIO() sio = StringIO.StringIO()
@ -26,5 +30,5 @@ class MainWindow(object):
print >>sys.stderr, fe print >>sys.stderr, fe
msg = '<p><b>' + unicode(str(value), 'utf8', 'replace') + '</b></p>' msg = '<p><b>' + unicode(str(value), 'utf8', 'replace') + '</b></p>'
msg += '<p>Detailed <b>traceback</b>:<pre>'+fe+'</pre>' msg += '<p>Detailed <b>traceback</b>:<pre>'+fe+'</pre>'
d = ConversionErrorDialog(self.window, 'ERROR: Unhandled exception', msg) d = ConversionErrorDialog(self, 'ERROR: Unhandled exception', msg)
d.exec_() d.exec_()