+ images/back.svg
images/book.svg
images/chapters.svg
images/clear_left.svg
@@ -9,6 +10,7 @@
images/dialog_warning.svg
images/document_open.svg
images/edit_input.svg
+ images/forward.svg
images/jobs-animated.mng
images/jobs.svg
images/library.png
@@ -36,8 +38,10 @@
images/news/bbc.png
images/news/newsweek.png
images/news/nytimes.png
+ images/next.svg
images/page.svg
images/plus.svg
+ images/previous.svg
images/reader.svg
images/save.svg
images/sd.svg
diff --git a/src/libprs500/gui2/lrf_renderer/document.py b/src/libprs500/gui2/lrf_renderer/document.py
index 861b200021..6e94a1a225 100644
--- a/src/libprs500/gui2/lrf_renderer/document.py
+++ b/src/libprs500/gui2/lrf_renderer/document.py
@@ -14,16 +14,22 @@
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
''''''
-import operator, collections, copy, re
+import operator, collections, copy, re, sys
-from PyQt4.QtCore import Qt, QByteArray
+from PyQt4.QtCore import Qt, QByteArray, SIGNAL, QVariant, QUrl
from PyQt4.QtGui import QGraphicsRectItem, QGraphicsScene, QPen, \
- QBrush, QColor, QGraphicsSimpleTextItem, QFontDatabase, \
- QFont, QGraphicsItem
+ QBrush, QColor, QGraphicsTextItem, QFontDatabase, \
+ QFont, QGraphicsItem, QGraphicsLineItem, QPixmap, \
+ QGraphicsPixmapItem, QTextCharFormat, QTextFrameFormat, \
+ QTextBlockFormat, QTextCursor, QTextImageFormat, \
+ QTextDocument
from libprs500.ebooks.lrf.fonts import FONT_MAP
from libprs500.gui2 import qstring_to_unicode
from libprs500.ebooks.hyphenate import hyphenate_word
+from libprs500.ebooks.BeautifulSoup import Tag
+from libprs500.ebooks.lrf.objects import RuledLine as _RuledLine
+from libprs500.ebooks.lrf.objects import Canvas as __Canvas
class Color(QColor):
def __init__(self, color):
@@ -38,124 +44,273 @@ WEIGHT_MAP = lambda wt : int((wt/10.)-1)
class FontLoader(object):
+ font_map = {
+ 'Swis721 BT Roman' : 'Liberation Sans',
+ 'Dutch801 Rm BT Roman' : 'Liberation Serif',
+ 'Courier10 BT Roman' : 'Liberation Mono',
+ }
+
def __init__(self, font_map, dpi):
self.face_map = {}
self.cache = {}
self.dpi = dpi
-
- for font in font_map:
- weight = WEIGHT_MAP(800) if 'Bold' in font else WEIGHT_MAP(400)
- style = QFont.StyleItalic if 'Italic' in font else QFont.StyleNormal
- self.face_map[font] = [font_map[font], weight, style]
+ self.face_map = font_map
- def font(self, text_style, face=None, size=None, weight=None, style=QFont.StyleNormal):
- face = self.face_map[text_style.fontfacename if face is None else face]
- sz = text_style.fontsize if size is None else size
- wt = text_style.fontweight if weight is None else weight
- face[1], face[2] = wt, style
- font = tuple(face) + (sz,)
+ def font(self, text_style):
+ device_font = text_style.fontfacename in FONT_MAP
+ if device_font:
+ face = self.font_map[text_style.fontfacename]
+ else:
+ face = self.face_map[text_style.fontfacename]
+
+ sz = text_style.fontsize
+ wt = text_style.fontweight
+ style = text_style.fontstyle
+ font = (face, wt, style, sz,)
if font in self.cache:
- return self.cache[font]
- italic = font[2] == QFont.StyleItalic
- qfont = QFont(font[0], font[3], font[1], italic)
- qfont.setPixelSize(font[3])
- self.cache[font] = qfont
+ rfont = self.cache[font]
+ else:
+ italic = font[2] == QFont.StyleItalic
+ rfont = QFont(font[0], font[3], font[1], italic)
+ rfont.setPixelSize(font[3])
+ rfont.setBold(wt>=69)
+ self.cache[font] = rfont
+ qfont = rfont
+ if text_style.underline or text_style.overline:
+ qfont = QFont(rfont)
+ qfont.setOverline(text_style.overline)
+ qfont.setUnderline(text_style.underline)
return qfont
class ParSkip(object):
def __init__(self, parskip):
self.height = parskip
+ def __str__(self):
+ return 'Parskip: '+str(self.height)
+
+class PixmapItem(QGraphicsPixmapItem):
+ def __init__(self, data, encoding, x0, y0, x1, y1, xsize, ysize):
+ p = QPixmap()
+ p.loadFromData(data, encoding, Qt.AutoColor)
+ w, h = p.width(), p.height()
+ p = p.copy(x0, y0, min(w, x1-x0), min(h, y1-y0))
+ if p.width() != xsize or p.height() != ysize:
+ p = p.scaled(xsize, ysize, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
+ QGraphicsPixmapItem.__init__(self, p)
+ self.height, self.width = ysize, xsize
+ self.setTransformationMode(Qt.SmoothTransformation)
+ self.setShapeMode(QGraphicsPixmapItem.BoundingRectShape)
+
+
+class Plot(PixmapItem):
-
+ def __init__(self, plot, dpi):
+ img = plot.refobj
+ xsize, ysize = dpi*plot.attrs['xsize']/720., dpi*plot.attrs['xsize']/720.
+ x0, y0, x1, y1 = img.x0, img.y0, img.x1, img.y1
+ data, encoding = img.data, img.encoding
+ PixmapItem.__init__(self, data, encoding, x0, y0, x1, y1, xsize, ysize)
+
+
class Line(QGraphicsRectItem):
whitespace = re.compile(r'\s+')
+ no_pen = QPen(Qt.NoPen)
+ inactive_brush = QBrush(QColor(0x00, 0x00, 0x00, 0x09))
+ active_brush = QBrush(QColor(0x00, 0x00, 0x00, 0x59))
- def __init__(self, offset, linespace, linelength, align, hyphenate):
+ line_map = {
+ 'none' : QTextCharFormat.NoUnderline,
+ 'solid' : QTextCharFormat.SingleUnderline,
+ 'dotted' : QTextCharFormat.DotLine,
+ 'dashed' : QTextCharFormat.DashUnderline,
+ 'double' : QTextCharFormat.WaveUnderline,
+ }
+
+
+ def __init__(self, offset, linespace, linelength, align, hyphenate, ts, block_id):
QGraphicsRectItem.__init__(self, 0, 0, 0, 0)
self.offset, self.line_space, self.line_length = offset, linespace, linelength
- self.linepos = 0
self.align = align
- self.hyphenate = hyphenate
+ self.do_hyphenation = hyphenate
+ self.setPen(self.__class__.no_pen)
+ self.is_empty = True
+ self.highlight_rect = None
+ self.cursor = None
+ self.item = None
+ self.plot_counter = 0
+ self.create_text_item(ts)
+ self.block_id = block_id
- def hyphenate_word(self, word):
- if self.hyphenate:
- tokens = hyphenate_word(word)
- for i in range(len(tokens)-2, -1, -1):
- part = ''.join(tokens[0:i+1])
- rword = QGraphicsSimpleTextItem(part+'-')
- length = rword.boundingRect().width()
- if length <= self.line_length - self.linepos:
- return len(part), rword, length
- if self.linepos == 0:
- return self.force_hyphenate_word(word)
- return 0, None, 0
-
- def force_hyphenate_word(self, word):
- for i in range(len(word)-5, 0, -5):
- part = word[:i]
- rword = QGraphicsSimpleTextItem(part+'-')
- length = rword.boundingRect().width()
- if length <= self.line_length - self.linepos:
- return len(part), rword, length
-
- def populate(self, phrase, font, wordspace):
+ def hoverEnterEvent(self, event):
+ if self.highlight_rect is not None:
+ self.highlight_rect.setBrush(self.__class__.active_brush)
+
+ def hoverLeaveEvent(self, event):
+ if self.highlight_rect is not None:
+ self.highlight_rect.setBrush(self.__class__.inactive_brush)
+
+ def mousePressEvent(self, event):
+ if self.highlight_rect is not None:
+ self.hoverLeaveEvent(None)
+ self.link[1](self.link[0])
+
+ def create_link(self, pos, in_link):
+ if not self.acceptsHoverEvents():
+ self.setAcceptsHoverEvents(True)
+ self.highlight_rect = QGraphicsRectItem(pos, 0, 0, 0, self)
+ self.highlight_rect.setCursor(Qt.PointingHandCursor)
+ self.link = in_link
+ self.link_end = sys.maxint
+
+ def end_link(self):
+ self.link_end = self.item.boundingRect().width() - self.highlight_rect.boundingRect().x()
+
+ def add_plot(self, plot, ts, in_link):
+ label='plot%d'%(self.plot_counter,)
+ self.plot_counter += 1
+ pos = self.item.boundingRect().width()
+ self.item.document().addResource(QTextDocument.ImageResource, QUrl(label),
+ QVariant(plot.pixmap()))
+ qif = QTextImageFormat()
+ qif.setHeight(plot.height)
+ qif.setWidth(plot.width)
+ qif.setName(label)
+ self.cursor.insertImage(qif, QTextFrameFormat.InFlow)
+ if in_link:
+ self.create_link(pos, in_link)
+
+
+ def can_add_plot(self, plot):
+ pos = self.item.boundingRect().width() if self.item is not None else 0
+ return self.line_length - pos >= plot.width
+
+ def create_text_item(self, ts):
+ self.item = QGraphicsTextItem(self)
+ self.cursor = QTextCursor(self.item.document())
+ f = self.cursor.currentFrame()
+ ff = QTextFrameFormat()
+ ff.setBorder(0)
+ ff.setPadding(0)
+ ff.setMargin(0)
+ f.setFrameFormat(ff)
+ bf = QTextBlockFormat()
+ bf.setTopMargin(0)
+ bf.setRightMargin(0)
+ bf.setBottomMargin(0)
+ bf.setRightMargin(0)
+ bf.setNonBreakableLines(True)
+ self.cursor.setBlockFormat(bf)
+
+
+ def build_char_format(self, ts):
+ tcf = QTextCharFormat()
+ tcf.setFont(ts.font)
+ tcf.setVerticalAlignment(ts.valign)
+ tcf.setForeground(ts.textcolor)
+ tcf.setUnderlineStyle(self.line_map[ts.line_style])
+ return tcf
+
+ def populate(self, phrase, ts, wordspace, in_link):
phrase_pos = 0
processed = False
- for match in self.__class__.whitespace.finditer(phrase):
+ matches = self.__class__.whitespace.finditer(phrase)
+ tcf = self.build_char_format(ts)
+ if in_link:
+ start = self.item.boundingRect().width()
+ for match in matches:
processed = True
left, right = match.span()
- if left == 0:
- self.linepos += right*wordspace
- phrase_pos = right
- continue
- word = phrase[phrase_pos:left]
- rword = QGraphicsSimpleTextItem(word, self)
- rword.setFont(font)
- length = rword.boundingRect().width()
- if length > self.line_length - self.linepos:
- break_at, rword, length = self.hyphenate(word)
- if break_at == 0:
- return phrase_pos
- phrase_pos += break_at
- else:
- phrase_pos = right
- rword.setParentItem(self)
- rword.setPos(self.linepos, 0)
- self.linepos += length + (right-left)*wordspace
-
- if self.line_length - self.linepos < 15: # Efficiency to prevent excessive hyphenation
- return phrase_pos
-
+ if wordspace == 0:
+ right = left
+ word = phrase[phrase_pos:right]
+ self.cursor.insertText(word, tcf)
+ if self.item.boundingRect().width() > self.line_length:
+ self.cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor,
+ right-left)
+ self.cursor.removeSelectedText()
+ if self.item.boundingRect().width() <= self.line_length:
+ if in_link: self.create_link(start, in_link)
+ return right, True
+ self.cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor,
+ left-phrase_pos)
+ self.cursor.removeSelectedText()
+ if self.do_hyphenation:
+ tokens = hyphenate_word(word)
+ for i in range(len(tokens)-2, -1, -1):
+ part = ''.join(tokens[0:i+1])
+ self.cursor.insertText(part+'-', tcf)
+ if self.item.boundingRect().width() <= self.line_length:
+ if in_link: self.create_link(start, in_link)
+ return phrase_pos + len(part), True
+ self.cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor,
+ len(part)+1)
+ self.cursor.removeSelectedText()
+ if self.cursor.position() < 1: # Force hyphenation as word is longer than line
+ for i in range(len(word)-5, 0, -5):
+ part = word[:i]
+ self.cursor.insertText(part+'-', tcf)
+ if self.item.boundingRect().width() <= self.line_length:
+ if in_link: self.create_link(start, in_link)
+ return phrase_pos + len(part), True
+ self.cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor,
+ len(part)+1)
+ self.cursor.removeSelectedText()
+ return phrase_pos, True
+
+ if in_link: self.create_link(start, in_link)
+ phrase_pos = right
+
if not processed:
- self.populate(phrase+' ', font, 0)
+ return self.populate(phrase+' ', ts, 0, in_link)
+
+ return phrase_pos, False
+
+
def finalize(self, wordspace, vdebug):
crect = self.childrenBoundingRect()
- self.width = self.linepos - wordspace
+ self.width = crect.width() - wordspace
self.height = crect.height() + self.line_space
self.setRect(crect)
+
if vdebug:
self.setPen(QPen(Qt.yellow, 1, Qt.DotLine))
+ if self.highlight_rect is not None:
+ x = self.highlight_rect.boundingRect().x()
+ if self.link_end == sys.maxint:
+ self.link_end = crect.width()-x
+ self.highlight_rect.setRect(crect)
+ erect = self.highlight_rect.boundingRect()
+ erect.setX(x)
+ erect.setWidth(self.link_end)
+ self.highlight_rect.setRect(erect)
+ self.highlight_rect.setBrush(self.__class__.inactive_brush)
+ self.highlight_rect.setZValue(-1)
+ self.highlight_rect.setPen(self.__class__.no_pen)
+
+ return self.height
def getx(self, textwidth):
if self.align == 'head':
return self.offset
if self.align == 'foot':
return textwidth - self.width
- if self.align == 'center':
+ if self.align == 'center':
return (textwidth-self.width)/2.
def __unicode__(self):
s = u''
for word in self.children():
- s += qstring_to_unicode(word.text()) + ' '
- return
+ if not hasattr(word, 'toPlainText'):
+ continue
+ s += qstring_to_unicode(word.toPlainText())
+ return s
def __str__(self):
return unicode(self).encode('utf-8')
-
+
class ContentObject(object):
has_content = True
@@ -181,6 +336,9 @@ class Style(object):
for attr in kwds:
setattr(self, attr, self.__class__.map[attr](kwds[attr], self.fdpi))
+ def copy(self):
+ return copy.copy(self)
+
class TextStyle(Style):
@@ -201,10 +359,23 @@ class TextStyle(Style):
linecolor = COLOR,
)
- def __init__(self, style, dpi, ruby_tags):
- Style.__init__(self, style, dpi)
+ def __init__(self, style, font_loader, ruby_tags):
+ self.font_loader = font_loader
+ self.fontstyle = QFont.StyleNormal
+ self.valign = QTextCharFormat.AlignBottom
+ self.underline = False
+ self.overline = False
+ self.line_style = 'none'
+ Style.__init__(self, style, font_loader.dpi)
for attr in ruby_tags:
setattr(self, attr, ruby_tags[attr])
+ self.font = self.font_loader.font(self)
+
+
+ def update(self, *args, **kwds):
+ Style.update(self, *args, **kwds)
+ self.font = self.font_loader.font(self)
+
class BlockStyle(Style):
map = collections.defaultdict(lambda : NULL,
@@ -212,31 +383,48 @@ class BlockStyle(Style):
framecolor = COLOR,
)
-
+
class TextBlock(ContentObject):
- has_content = property(fget=lambda self: self.peek_index < len(self.lines)-2)
-
- def __init__(self, tb, font_loader, logger, opts, ruby_tags, parent=None, x=0, y=0):
+ has_content = property(fget=lambda self: self.peek_index < len(self.lines)-1)
+ XML_ENTITIES = dict(zip(Tag.XML_SPECIAL_CHARS_TO_ENTITIES.values(), Tag.XML_SPECIAL_CHARS_TO_ENTITIES.keys()))
+ XML_ENTITIES["quot"] = '"'
+
+ class HeightExceeded(Exception):
+ pass
+
+
+ def __init__(self, tb, font_loader, respect_max_y, text_width, logger,
+ opts, ruby_tags, link_activated,
+ parent=None, x=0, y=0):
ContentObject.__init__(self)
+ self.block_id = tb.id
self.bs, self.ts = BlockStyle(tb.style, font_loader.dpi), \
- TextStyle(tb.textstyle, font_loader.dpi, ruby_tags)
+ TextStyle(tb.textstyle, font_loader, ruby_tags)
self.bs.update(tb.attrs)
self.ts.update(tb.attrs)
self.lines = []
- self.line_length = self.bs.blockwidth - 2*self.bs.sidemargin
+ self.line_length = min(self.bs.blockwidth, text_width)
+ self.line_length -= 2*self.bs.sidemargin
self.line_offset = self.bs.sidemargin
self.first_line = True
- self.current_style = copy.copy(self.ts)
+ self.current_style = self.ts.copy()
self.current_line = None
self.font_loader, self.logger, self.opts = font_loader, logger, opts
- self.current_font = font_loader.font(self.current_style)
- self.create_lines(tb.content)
- if self.current_line:
- self.end_line()
+ self.in_link = False
+ self.link_activated = link_activated
+ self.max_y = self.bs.blockheight if (respect_max_y or self.bs.blockrule.lower() in ('vert-fixed', 'block-fixed')) else sys.maxint
+ self.height = 0
+ try:
+ if self.max_y > 0:
+ self.populate(tb.content)
+ self.end_line()
+ except TextBlock.HeightExceeded:
+ logger.warning('TextBlock height exceeded, truncating.')
self.peek_index = -1
+
def peek(self):
return self.lines[self.peek_index+1]
@@ -246,32 +434,82 @@ class TextBlock(ContentObject):
def reset(self):
self.peek_index = -1
- def create_lines(self, tb):
- for i in tb:
- if i.name == 'CR':
- self.lines.append(ParSkip(self.current_style.parskip))
- self.first_line = True
- elif i.name == 'P':
- self.process_container(i)
-
- def process_container(self, c):
- for i in c:
+ def end_link(self):
+ self.link_activated(self.in_link[0], on_creation=True)
+ self.in_link = False
+
+ def populate(self, tb):
+ self.create_line()
+ open_containers = collections.deque()
+ self.in_para = False
+ for i in tb.content:
if isinstance(i, basestring):
self.process_text(i)
+ elif i is None:
+ if len(open_containers) > 0:
+ for a, b in open_containers.pop():
+ if callable(a):
+ a(*b)
+ else:
+ setattr(self, a, b)
+ elif i.name == 'P':
+ open_containers.append((('in_para', False),))
+ self.in_para = True
elif i.name == 'CR':
- self.end_line()
+ if self.in_para:
+ self.end_line()
+ self.create_line()
+ else:
+ self.end_line()
+ delta = self.current_style.parskip
+ if isinstance(self.lines[-1], ParSkip):
+ delta += self.current_style.baselineskip
+ self.lines.append(ParSkip(delta))
+ self.first_line = True
+ elif i.name == 'Span':
+ open_containers.append((('current_style', self.current_style.copy()),))
+ self.current_style.update(i.attrs)
+ elif i.name == 'CharButton':
+ open_containers.append(((self.end_link, []),))
+ self.in_link = (i.attrs['refobj'], self.link_activated)
+ elif i.name == 'Italic':
+ open_containers.append((('current_style', self.current_style.copy()),))
+ self.current_style.update(fontstyle=QFont.StyleItalic)
elif i.name == 'Plot':
- pass #TODO: Plot
+ plot = Plot(i, self.font_loader.dpi)
+ if self.current_line is None:
+ self.create_line()
+ if not self.current_line.can_add_plot(plot):
+ self.end_line()
+ self.create_line()
+ self.current_line.add_plot(plot, self.current_style, self.in_link)
+ elif i.name == 'Sup':
+ open_containers.append((('current_style', self.current_style.copy()),))
+ self.current_style.valign=QTextCharFormat.AlignSuperScript
+ elif i.name == 'Sub':
+ open_containers.append((('current_style', self.current_style.copy()),))
+ self.current_style.valign=QTextCharFormat.AlignSubScript
+ elif i.name == 'EmpLine':
+ open_containers.append((('current_style', self.current_style.copy()),))
+ if i.attrs['emplineposition'] == 'before':
+ self.current_style.overline = True
+ if i.attrs['emplineposition'] == 'after':
+ self.current_style.underline = True
+ self.current_style.update(line_type=i.attrs['emplinetype'])
else:
- self.process_container(i)
+ self.logger.warning('Unhandled TextTag %s'%(i.name,))
+ if not i.self_closing:
+ open_containers.append([])
def __iter__(self):
for line in self.lines: yield line
def end_line(self):
if self.current_line is not None:
- self.lines.append(self.current_line)
- self.current_line.finalize(self.current_style.wordspace, self.opts.visual_debug)
+ self.height += self.current_line.finalize(self.current_style.wordspace, self.opts.visual_debug)
+ if self.height > self.max_y+10:
+ raise TextBlock.HeightExceeded
+ self.lines.append(self.current_line)
self.current_line = None
def create_line(self):
@@ -281,35 +519,65 @@ class TextBlock(ContentObject):
line_length -= self.current_style.parindent
line_offset += self.current_style.parindent
self.current_line = Line(line_offset, self.current_style.linespace,
- line_length, self.current_style.align, self.opts.hyphenate)
+ line_length, self.current_style.align,
+ self.opts.hyphenate, self.current_style, self.block_id)
self.first_line = False
def process_text(self, raw):
- if len(raw) == 0:
- return
- while True:
+ for ent, rep in TextBlock.XML_ENTITIES.items():
+ raw = raw.replace(u'&%s;'%ent, rep)
+ while len(raw) > 0:
if self.current_line is None:
self.create_line()
- pos = self.current_line.populate(raw, self.current_font.font,
- self.current_style.wordspace)
- if pos >= len(raw):
- self.end_line()
- break
+ pos, line_filled = self.current_line.populate(raw, self.current_style,
+ self.current_style.wordspace, self.in_link)
raw = raw[pos:]
-
+ if line_filled:
+ self.end_line()
+
def __unicode__(self):
return u'\n'.join(unicode(l) for l in self.lines)
def __str__(self):
- return unicode(self).encode('utf-8')
+
+ return '\n'+unicode(self).encode('utf-8')+'\n'
+
+class RuledLine(QGraphicsLineItem, ContentObject):
-def object_factory(container, obj):
+ map = {'solid': Qt.SolidLine, 'dashed': Qt.DashLine, 'dotted': Qt.DotLine, 'double': Qt.DashDotLine}
+
+ def __init__(self, rl):
+ QGraphicsLineItem.__init__(self, 0, 0, rl.linelength, 0)
+ ContentObject.__init__(self)
+ self.setPen(QPen(COLOR(rl.linecolor, None), rl.linewidth, ))
+
+
+class ImageBlock(PixmapItem, ContentObject):
+
+ def __init__(self, obj):
+ ContentObject.__init__(self)
+ x0, y0, x1, y1 = obj.attrs['x0'], obj.attrs['y0'], obj.attrs['x1'], obj.attrs['y1']
+ xsize, ysize, refstream = obj.attrs['xsize'], obj.attrs['ysize'], obj.refstream
+ data, encoding = refstream.stream, refstream.encoding
+ PixmapItem.__init__(self, data, encoding, x0, y0, x1, y1, xsize, ysize)
+ self.block_id = obj.id
+
+
+def object_factory(container, obj, respect_max_y=False):
if hasattr(obj, 'name'):
if obj.name.endswith('TextBlock'):
- return TextBlock(obj, container.font_loader, container.logger,
- container.opts, container.ruby_tags)
+ return TextBlock(obj, container.font_loader, respect_max_y, container.text_width,
+ container.logger,
+ container.opts, container.ruby_tags, container.link_activated)
+ elif obj.name.endswith('ImageBlock'):
+ return ImageBlock(obj)
+ elif isinstance(obj, _RuledLine):
+ return RuledLine(obj)
+ elif isinstance(obj, __Canvas):
+ return Canvas(container.font_loader, obj, container.logger, container.opts,
+ container.ruby_tags, container.link_activated)
return None
class _Canvas(QGraphicsRectItem):
@@ -317,7 +585,7 @@ class _Canvas(QGraphicsRectItem):
def __init__(self, font_loader, logger, opts, width=0, height=0, parent=None, x=0, y=0):
QGraphicsRectItem.__init__(self, x, y, width, height, parent)
self.font_loader, self.logger, self.opts = font_loader, logger, opts
- self.current_y, self.max_y = 0, height
+ self.current_y, self.max_y, self.max_x = 0, height, width
self.is_full = False
pen = QPen()
pen.setStyle(Qt.NoPen)
@@ -326,18 +594,36 @@ class _Canvas(QGraphicsRectItem):
def layout_block(self, block, x, y):
if isinstance(block, TextBlock):
self.layout_text_block(block, x, y)
+ elif isinstance(block, RuledLine):
+ self.layout_ruled_line(block, x, y)
+ elif isinstance(block, ImageBlock):
+ self.layout_image_block(block, x, y)
+ elif isinstance(block, Canvas):
+ self.layout_canvas(block, x, y)
+ def layout_canvas(self, canvas, x, y):
+ canvas.setParentItem(self)
+ canvas.setPos(x, y)
+ canvas.has_content = False
+ oy = self.current_y
+ for block, x, y in canvas.items:
+ self.layout_block(block, x, oy+y)
+ self.current_y = oy + canvas.max_y
+
+
def layout_text_block(self, block, x, y):
textwidth = block.bs.blockwidth - block.bs.sidemargin
+ if block.max_y == 0 or not block.lines: # Empty block skipping
+ self.is_full = False
+ return
line = block.peek()
- self.max_y = self.boundingRect().height()
y += block.bs.topskip
block_consumed = False
while y + line.height <= self.max_y:
block.commit()
if isinstance(line, QGraphicsItem):
line.setParentItem(self)
- line.setPos(line.getx(textwidth), y)
+ line.setPos(x + line.getx(textwidth), y)
y += line.height
if not block.has_content:
y += block.bs.footskip
@@ -348,49 +634,70 @@ class _Canvas(QGraphicsRectItem):
self.current_y = y
self.is_full = not block_consumed
-
-
+ def layout_ruled_line(self, rl, x, y):
+ br = rl.boundingRect()
+ rl.setParentItem(self)
+ rl.setPos(x, y+1)
+ self.current_y = y + br.height() + 1
+ self.is_full = y > self.max_y-5
+ rl.has_content = False
+
+ def layout_image_block(self, ib, x, y):
+ if self.current_y + ib.height > self.max_y-y and self.current_y < 5:
+ self.is_full = True
+ else:
+ br = ib.boundingRect()
+ ib.setParentItem(self)
+ ib.setPos(x, y)
+ self.current_y = y + br.height()
+ self.is_full = y > self.max_y-5
+ ib.has_content = False
+
-class Canvas(_Canvas):
+class Canvas(_Canvas, ContentObject):
- def __init__(self, font_loader, canvas, logger, opts, width=0, height=0):
+ def __init__(self, font_loader, canvas, logger, opts, ruby_tags, link_activated, width=0, height=0):
if hasattr(canvas, 'canvaswidth'):
width, height = canvas.canvaswidth, canvas.canvasheight
_Canvas.__init__(self, font_loader, logger, opts, width=width, height=height)
-
+ self.block_id = canvas.id
+ self.ruby_tags = ruby_tags
+ self.link_activated = link_activated
+ self.text_width = width
fg = canvas.framecolor
bg = canvas.bgcolor
if not opts.visual_debug and canvas.framemode != 'none':
self.setPen(Pen(fg, canvas.framewidth))
self.setBrush(QBrush(Color(bg)))
+ self.items = []
for po in canvas:
obj = po.object
- item = object_factory(self, obj)
- if item:
- self.layout_block(item, po.x, po.y)
+ item = object_factory(self, obj, respect_max_y=True)
+ if item:
+ self.items.append((item, po.x, po.y))
def layout_block(self, block, x, y):
block.reset()
_Canvas.layout_block(self, block, x, y)
class Header(Canvas):
- def __init__(self, font_loader, header, page_style, logger, opts):
- Canvas.__init__(self, font_loader, header, logger, opts,
+ def __init__(self, font_loader, header, page_style, logger, opts, ruby_tags, link_activated):
+ Canvas.__init__(self, font_loader, header, logger, opts, ruby_tags, link_activated,
page_style.textwidth, page_style.headheight)
if opts.visual_debug:
self.setPen(QPen(Qt.blue, 1, Qt.DashLine))
class Footer(Canvas):
- def __init__(self, font_loader, footer, page_style, logger, opts):
- Canvas.__init__(self, font_loader, footer, logger, opts,
+ def __init__(self, font_loader, footer, page_style, logger, opts, ruby_tags, link_activated):
+ Canvas.__init__(self, font_loader, footer, logger, opts, ruby_tags, link_activated,
page_style.textwidth, page_style.footheight)
if opts.visual_debug:
self.setPen(QPen(Qt.blue, 1, Qt.DashLine))
class Screen(_Canvas):
- def __init__(self, font_loader, chapter, odd, logger, opts):
+ def __init__(self, font_loader, chapter, odd, logger, opts, ruby_tags, link_activated):
self.logger, self.opts = logger, opts
page_style = chapter.style
sidemargin = page_style.oddsidemargin if odd else page_style.evensidemargin
@@ -412,22 +719,26 @@ class Screen(_Canvas):
if page_style.footheight > 0:
footer = chapter.oddfooter if odd else chapter.evenfooter
if header:
- header = Header(font_loader, header, page_style, logger, opts)
- header.setParentItem(self)
- header.setPos(self.content_x, self.header_y)
+ header = Header(font_loader, header, page_style, logger, opts, ruby_tags, link_activated)
+ self.layout_canvas(header, self.content_x, self.header_y)
if footer:
- footer = Footer(font_loader, footer, page_style)
- footer.setParentItem(self)
- footer.setPos(self.content_x, self.footer_y)
+ footer = Footer(font_loader, footer, page_style, logger, opts, ruby_tags, link_activated)
+ self.layout_canvas(footer, self.content_x, self.header_y)
self.page = None
def set_page(self, page):
- if self.page is not None:
- self.scene().removeItem(self.page)
+ if self.page is not None and self.page.scene():
+ self.scene().removeItem(self.page)
self.page = page
self.page.setPos(self.content_x, self.text_y)
self.scene().addItem(self.page)
+
+ def remove(self):
+ if self.scene():
+ if self.page is not None and self.page.scene():
+ self.scene().removeItem(self.page)
+ self.scene().removeItem(self)
class Page(_Canvas):
@@ -436,10 +747,14 @@ class Page(_Canvas):
_Canvas.__init__(self, font_loader, logger, opts, width, height)
if opts.visual_debug:
self.setPen(QPen(Qt.cyan, 1, Qt.DashLine))
-
+
+ def id(self):
+ for child in self.children():
+ if hasattr(child, 'block_id'):
+ return child.block_id
def add_block(self, block):
- self.layout_block(block, 0, self.current_y)
+ self.layout_block(block, 0, self.current_y)
class Chapter(object):
@@ -459,94 +774,194 @@ class Chapter(object):
def screen(self, odd):
return self.oddscreen if odd else self.evenscreen
+class History(collections.deque):
+
+ def __init__(self):
+ collections.deque.__init__(self)
+ self.pos = 0
+
+ def back(self):
+ if self.pos - 1 < 0: return None
+ self.pos -= 1
+ return self[self.pos]
+
+ def forward(self):
+ if self.pos + 1 >= len(self): return None
+ self.pos += 1
+ return self[self.pos]
+
+ def add(self, item):
+ while len(self) > self.pos+1:
+ self.pop()
+ self.append(item)
+ self.pos += 1
+
+
+
class Document(QGraphicsScene):
num_of_pages = property(fget=lambda self: sum(self.chapter_layout))
- def __init__(self, lrf, logger, opts):
+ def __init__(self, logger, opts):
QGraphicsScene.__init__(self)
- #opts.hyphenate = opts.hyphenate and lrf.doc_info.lower().strip() == 'en'
- self.lrf, self.logger, self.opts = lrf, logger, opts
+ self.logger, self.opts = logger, opts
self.pages = []
- self.dpi = lrf.device_info.dpi/10.
- self.ruby_tags = dict(**lrf.ruby_tags)
- self.load_fonts()
self.chapters = []
self.chapter_layout = None
self.current_screen = None
self.current_page = 0
- self.render_doc()
+ self.link_map = {}
+ self.chapter_map = {}
+ self.history = History()
+
+ def page_of(self, oid):
+ for chapter in self.chapters:
+ if oid in chapter.object_to_page_map:
+ return chapter.object_to_page_map[oid]
+
+ def get_page_num(self, chapterid, objid):
+ cnum = self.chapter_map[chapterid]
+ page = self.chapters[cnum].object_to_page_map[objid]
+ return sum(self.chapter_layout[:cnum])+page
+
+ def add_to_history(self):
+ page = self.chapter_page(self.current_page)[1]
+ page_id = page.id()
+ if page_id is not None:
+ self.history.add(page_id)
+
+ def link_activated(self, objid, on_creation=None):
+ if on_creation is None:
+ cid, oid = self.link_map[objid]
+ self.add_to_history()
+ page = self.get_page_num(cid, oid)
+ self.show_page(page)
+ else:
+ jb = self.objects[objid]
+ self.link_map[objid] = (jb.refpage, jb.refobj)
+
+ def back(self):
+ oid = self.history.back()
+ if oid is not None:
+ page = self.page_of(oid)
+ self.show_page(page)
-
- def load_fonts(self):
+ def forward(self):
+ oid = self.history.forward()
+ if oid is not None:
+ page = self.page_of(oid)
+ self.show_page(page)
+
+
+ def load_fonts(self, lrf):
font_map = {}
- for font in self.lrf.font_map:
- fdata = QByteArray(self.lrf.font_map[font].data)
- id = QFontDatabase.addApplicationFontFromData(fdata)
- font_map[font] = [str(i) for i in QFontDatabase.applicationFontFamilies(id)][0]
- for font in FONT_MAP:
- fdata = QByteArray(FONT_MAP[font].font_data)
+ for font in lrf.font_map:
+ fdata = QByteArray(lrf.font_map[font].data)
id = QFontDatabase.addApplicationFontFromData(fdata)
font_map[font] = [str(i) for i in QFontDatabase.applicationFontFamilies(id)][0]
+
+ from libprs500.ebooks.lrf.fonts.liberation import LiberationMono_BoldItalic
+ QFontDatabase.addApplicationFontFromData(QByteArray(LiberationMono_BoldItalic.font_data))
+ from libprs500.ebooks.lrf.fonts.liberation import LiberationMono_Italic
+ QFontDatabase.addApplicationFontFromData(QByteArray(LiberationMono_Italic.font_data))
+ from libprs500.ebooks.lrf.fonts.liberation import LiberationSerif_Bold
+ QFontDatabase.addApplicationFontFromData(QByteArray(LiberationSerif_Bold.font_data))
+ from libprs500.ebooks.lrf.fonts.liberation import LiberationSans_BoldItalic
+ QFontDatabase.addApplicationFontFromData(QByteArray(LiberationSans_BoldItalic.font_data))
+ from libprs500.ebooks.lrf.fonts.liberation import LiberationMono_Regular
+ QFontDatabase.addApplicationFontFromData(QByteArray(LiberationMono_Regular.font_data))
+ from libprs500.ebooks.lrf.fonts.liberation import LiberationSans_Italic
+ QFontDatabase.addApplicationFontFromData(QByteArray(LiberationSans_Italic.font_data))
+ from libprs500.ebooks.lrf.fonts.liberation import LiberationSerif_Regular
+ QFontDatabase.addApplicationFontFromData(QByteArray(LiberationSerif_Regular.font_data))
+ from libprs500.ebooks.lrf.fonts.liberation import LiberationSerif_Italic
+ QFontDatabase.addApplicationFontFromData(QByteArray(LiberationSerif_Italic.font_data))
+ from libprs500.ebooks.lrf.fonts.liberation import LiberationSans_Bold
+ QFontDatabase.addApplicationFontFromData(QByteArray(LiberationSans_Bold.font_data))
+ from libprs500.ebooks.lrf.fonts.liberation import LiberationMono_Bold
+ QFontDatabase.addApplicationFontFromData(QByteArray(LiberationMono_Bold.font_data))
+ from libprs500.ebooks.lrf.fonts.liberation import LiberationSerif_BoldItalic
+ QFontDatabase.addApplicationFontFromData(QByteArray(LiberationSerif_BoldItalic.font_data))
+ from libprs500.ebooks.lrf.fonts.liberation import LiberationSans_Regular
+ QFontDatabase.addApplicationFontFromData(QByteArray(LiberationSans_Regular.font_data))
+
self.font_loader = FontLoader(font_map, self.dpi)
- def render_chapter(self, chapter):
- oddscreen, evenscreen = Screen(self.font_loader, chapter, True, self.logger, self.opts), \
- Screen(self.font_loader, chapter, False, self.logger, self.opts)
+ def render_chapter(self, chapter, lrf):
+ oddscreen, evenscreen = Screen(self.font_loader, chapter, True, self.logger, self.opts, self.ruby_tags, self.link_activated), \
+ Screen(self.font_loader, chapter, False, self.logger, self.opts, self.ruby_tags, self.link_activated)
pages = []
width, height = oddscreen.text_width, oddscreen.text_height
current_page = Page(self.font_loader, self.logger, self.opts, width, height)
object_to_page_map = {}
for object in chapter:
+ self.text_width = width
block = object_factory(self, object)
if block is None:
continue
while block.has_content:
current_page.add_block(block)
- object_to_page_map[object.id] = len(pages)
+ object_to_page_map[object.id] = len(pages) + 1
if current_page.is_full:
pages.append(current_page)
current_page = Page(self.font_loader, self.logger, self.opts, width, height)
if current_page:
pages.append(current_page)
- self.chapters.append(Chapter(oddscreen, evenscreen, pages, object_to_page_map))
+ self.chapters.append(Chapter(oddscreen, evenscreen, pages, object_to_page_map))
+ self.chapter_map[chapter.id] = len(self.chapters)-1
- def render_doc(self):
- for pt in self.lrf.page_trees:
+ def render(self, lrf):
+ self.dpi = lrf.device_info.dpi/10.
+ self.ruby_tags = dict(**lrf.ruby_tags)
+ self.load_fonts(lrf)
+ self.objects = lrf.objects
+
+ num_chaps = 0
+ for pt in lrf.page_trees:
for chapter in pt:
- self.render_chapter(chapter)
+ num_chaps += 1
+ self.emit(SIGNAL('chapter_rendered(int)'), num_chaps)
+
+ for pt in lrf.page_trees:
+ for chapter in pt:
+ self.render_chapter(chapter, lrf)
+ self.emit(SIGNAL('chapter_rendered(int)'), -1)
self.chapter_layout = [i.num_of_pages for i in self.chapters]
+ self.objects = None
+
+ def chapter_page(self, num):
+ for chapter in self.chapters:
+ if num <= chapter.num_of_pages:
+ break
+ num -= chapter.num_of_pages
+ return chapter, chapter.page(num)
def show_page(self, num):
- if num < 1 or num > self.num_of_pages:
+ if num < 1 or num > self.num_of_pages or num == self.current_page:
return
- it = iter(self.chapters)
- chapter = it.next()
- num_before = 0
- while num > chapter.num_of_pages:
- num_before += chapter.num_of_pages
- chapter = it.next()
odd = num%2 == 1
- page = chapter.page(num - num_before)
+ self.current_page = num
+ chapter, page = self.chapter_page(num)
screen = chapter.screen(odd)
if self.current_screen is not None and self.current_screen is not screen:
- self.removeItem(self.current_screen)
+ self.current_screen.remove()
self.current_screen = screen
if self.current_screen.scene() is None:
self.addItem(self.current_screen)
self.current_screen.set_page(page)
- self.current_page = num
+ self.emit(SIGNAL('page_changed(PyQt_PyObject)'), self.current_page)
+
def next(self):
- self.show_page(self.current_page + 1)
+ self.next_by(1)
def previous(self):
- self.show_page(self.current_page - 1)
+ self.previous_by(1)
def next_by(self, num):
self.show_page(self.current_page + num)
@@ -557,4 +972,4 @@ class Document(QGraphicsScene):
def show_page_at_percent(self, p):
num = self.num_of_pages*(p/100.)
self.show_page(num)
-
\ No newline at end of file
+
diff --git a/src/libprs500/gui2/lrf_renderer/main.py b/src/libprs500/gui2/lrf_renderer/main.py
index 0ee6cd0569..44554c60aa 100644
--- a/src/libprs500/gui2/lrf_renderer/main.py
+++ b/src/libprs500/gui2/lrf_renderer/main.py
@@ -14,19 +14,40 @@
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
''''''
-import sys, logging, os
+import sys, logging, os, traceback
-from PyQt4.Qt import QApplication, QCoreApplication
-from PyQt4.QtCore import Qt, QObject
+from PyQt4.QtGui import QApplication, QKeySequence, QPainter
+from PyQt4.QtCore import Qt, QObject, SIGNAL, QCoreApplication, QThread
from libprs500 import __appname__, __version__, __author__, setup_cli_handlers, islinux
from libprs500.ebooks.lrf.parser import LRFDocument
from libprs500.gui2 import ORG_NAME, APP_UID
+from libprs500.gui2.dialogs.conversion_error import ConversionErrorDialog
from libprs500.gui2.lrf_renderer.main_ui import Ui_MainWindow
+from libprs500.gui2.main_window import MainWindow
from libprs500.gui2.lrf_renderer.document import Document
-class Main(QObject, Ui_MainWindow):
+class RenderWorker(QThread):
+
+ def __init__(self, parent, lrf_stream, logger, opts):
+ QThread.__init__(self, parent)
+ self.stream, self.logger, self.opts = lrf_stream, logger, opts
+ self.lrf = None
+ self.document = None
+ self.exception = None
+
+ def run(self):
+ try:
+ self.lrf = LRFDocument(self.stream)
+ self.stream.close()
+ self.stream = None
+ except Exception, err:
+ self.exception = err
+ self.formatted_traceback = traceback.format_exc()
+ self.emit(SIGNAL('parsed()'))
+
+class Main(QObject, Ui_MainWindow, MainWindow):
def __init__(self, window, stream, logger, opts):
QObject.__init__(self)
Ui_MainWindow.__init__(self)
@@ -34,28 +55,95 @@ class Main(QObject, Ui_MainWindow):
self.window = window
self.logger = logger
self.file_name = os.path.basename(stream.name) if hasattr(stream, 'name') else ''
- self.stream = stream
self.opts = opts
+ self.document = None
+ self.renderer = RenderWorker(self, stream, logger, opts)
+ QObject.connect(self.renderer, SIGNAL('parsed()'), self.parsed, Qt.QueuedConnection)
+ self.document = Document(self.logger, self.opts)
+ QObject.connect(self.document, SIGNAL('chapter_rendered(int)'), self.chapter_rendered)
+ QObject.connect(self.document, SIGNAL('page_changed(PyQt_PyObject)'), self.page_changed)
+ self.search.help_text = 'Search'
+ self.search.clear_to_help()
+
+ self.action_next_page.setShortcuts(QKeySequence.MoveToNextPage)
+ self.action_previous_page.setShortcuts(QKeySequence.MoveToPreviousPage)
+ QObject.connect(self.action_next_page, SIGNAL('triggered(bool)'), self.next)
+ QObject.connect(self.action_previous_page, SIGNAL('triggered(bool)'), self.previous)
+ QObject.connect(self.action_back, SIGNAL('triggered(bool)'), self.back)
+ QObject.connect(self.action_forward, SIGNAL('triggered(bool)'), self.forward)
+ QObject.connect(self.spin_box, SIGNAL('valueChanged(int)'), self.go_to_page)
+ QObject.connect(self.slider, SIGNAL('valueChanged(int)'), self.go_to_page)
+ self.next_button.setDefaultAction(self.action_next_page)
+ self.previous_button.setDefaultAction(self.action_previous_page)
+ self.back_button.setDefaultAction(self.action_back)
+ self.forward_button.setDefaultAction(self.action_forward)
+
+ self.graphics_view.setRenderHint(QPainter.Antialiasing, True)
+ self.graphics_view.setRenderHint(QPainter.TextAntialiasing, True)
+ self.graphics_view.setRenderHint(QPainter.SmoothPixmapTransform, True)
+
+ def page_changed(self, num):
+ self.slider.setValue(num)
+ self.spin_box.setValue(num)
+
def render(self):
- self.window.show()
- self.statusbar.showMessage('Parsing LRF file '+self.file_name)
- QCoreApplication.instance().processEvents()
- self.lrf = LRFDocument(self.stream)
- self.stream.close()
- self.stream = None
- self.graphics_view.resize(self.lrf.device_info.width+15, self.lrf.device_info.height)
- self.window.setWindowTitle(self.lrf.metadata.title + ' - ' + __appname__)
- self.statusbar.showMessage('Building graphical representation')
- QCoreApplication.instance().processEvents()
+ self.stack.setCurrentIndex(1)
+ self.renderer.start()
+
+
+ def parsed(self, *args):
+ if self.renderer.lrf is not None:
+ self.graphics_view.setMinimumSize(self.renderer.lrf.device_info.width+5,
+ self.renderer.lrf.device_info.height)
+ self.window.setWindowTitle(self.renderer.lrf.metadata.title + ' - ' + __appname__)
+ self.document_title = self.renderer.lrf.metadata.title
+ self.document.render(self.renderer.lrf)
+ self.renderer.lrf = None
+ self.graphics_view.setScene(self.document)
+ self.graphics_view.show()
+ self.spin_box.setRange(1, self.document.num_of_pages)
+ self.slider.setRange(1, self.document.num_of_pages)
+ self.spin_box.setSuffix(' of %d'%(self.document.num_of_pages,))
+ self.spin_box.updateGeometry()
+ self.stack.setCurrentIndex(0)
+ else:
+ exception = self.renderer.exception
+ print >>sys.stderr, 'Error rendering document'
+ print >>sys.stderr, exception
+ print >>sys.stderr, self.renderer.formatted_traceback
+ msg = u'%s: '%(exception.__class__.__name__,) + unicode(str(exception), 'utf8', 'replace') + u'
'
+ msg += u'Failed to render document
'
+ msg += u'Detailed traceback:
'
+ msg += self.renderer.formatted_traceback + '
'
+ d = ConversionErrorDialog(self.window, 'Error while rendering file', msg)
+ d.exec_()
+
+ def chapter_rendered(self, num):
+ if num > 0:
+ self.progress_bar.setMinimum(0)
+ self.progress_bar.setMaximum(num)
+ self.progress_bar.setValue(0)
+ self.progress_label.setText('Rendering '+ self.document_title)
+ else:
+ self.progress_bar.setValue(self.progress_bar.value()+1)
+ QCoreApplication.processEvents()
+
+ def next(self, triggered):
+ self.document.next()
+ def previous(self, triggered):
+ self.document.previous()
- self.document = Document(self.lrf, self.logger, self.opts)
- self.graphics_view.setScene(self.document)
- self.graphics_view.show()
- self.statusbar.clearMessage()
- QCoreApplication.instance().processEvents()
- self.document.next()
+ def go_to_page(self, num):
+ self.document.show_page(num)
+
+ def forward(self, triggered):
+ self.document.forward()
+
+ def back(self, triggered):
+ self.document.back()
+
def file_renderer(stream, logger, opts):
from PyQt4.Qt import QMainWindow
@@ -93,9 +181,13 @@ def main(args=sys.argv, logger=None):
QCoreApplication.setOrganizationName(ORG_NAME)
QCoreApplication.setApplicationName(APP_UID)
main = file_renderer(open(args[1], 'rb'), logger, opts)
+ sys.excepthook = main.unhandled_exception
+ main.window.show()
main.render()
return app.exec_()
return 0
if __name__ == '__main__':
- sys.exit(main())
\ No newline at end of file
+ sys.exit(main())
+
+
\ No newline at end of file
diff --git a/src/libprs500/gui2/lrf_renderer/main.ui b/src/libprs500/gui2/lrf_renderer/main.ui
index eef1b7e4e5..99fde22556 100644
--- a/src/libprs500/gui2/lrf_renderer/main.ui
+++ b/src/libprs500/gui2/lrf_renderer/main.ui
@@ -5,46 +5,254 @@
0
0
- 858
- 854
+ 614
+ 832
-
+
0
0
- MainWindow
+ LRF Viewer
-
-
-
-
- 0
- 10
- 281
- 321
-
+
+ :/library
+
+
+
+
+ 0
-
- true
+
+ 0
-
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 1
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ ...
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
-
+
+
+ 0
+
+
+ -1
+
+
+
+ -
+
+
+
+ 11
+ 75
+ true
+
+
+
+ Parsing LRF file
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
-
-
+
+ Next Page
+
+
+
+
+ :/images/previous.svg
+
+
+ Previous Page
+
+
+
+
+ :/images/back.svg
+
+
+ Back
+
+
+
+
+ :/images/forward.svg
+
+
+ Forward
+
+
-
+
+
+ SearchBox
+ QLineEdit
+
+
+
+
+
+
diff --git a/src/libprs500/gui2/main.py b/src/libprs500/gui2/main.py
index ce556bd604..20c92e6fe7 100644
--- a/src/libprs500/gui2/main.py
+++ b/src/libprs500/gui2/main.py
@@ -12,7 +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.Warning
-import os, sys, traceback, StringIO, textwrap
+import os, sys, textwrap
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \
QSettings, QVariant, QSize, QThread
@@ -30,6 +30,7 @@ from libprs500.devices.interface import Device
from libprs500.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \
initialize_file_icon_provider, BOOK_EXTENSIONS, \
pixmap_to_data, choose_dir, ORG_NAME
+from libprs500.gui2.main_window import MainWindow
from libprs500.gui2.main_ui import Ui_MainWindow
from libprs500.gui2.device import DeviceDetector, DeviceManager
from libprs500.gui2.status import StatusBar
@@ -41,7 +42,7 @@ from libprs500.gui2.dialogs.conversion_error import ConversionErrorDialog
from libprs500.gui2.dialogs.lrf_single import LRFSingleDialog
from libprs500.gui2.dialogs.password import PasswordDialog
-class Main(QObject, Ui_MainWindow):
+class Main(QObject, Ui_MainWindow, MainWindow):
def set_default_thumbnail(self, height):
r = QSvgRenderer(':/images/book.svg')
@@ -665,15 +666,7 @@ class Main(QObject, Ui_MainWindow):
e.accept()
- def unhandled_exception(self, type, value, tb):
- sio = StringIO.StringIO()
- traceback.print_exception(type, value, tb, file=sio)
- fe = sio.getvalue()
- print >>sys.stderr, fe
- msg = '' + unicode(str(value), 'utf8', 'replace') + '
'
- msg += 'Detailed traceback:
'+fe+'
'
- d = ConversionErrorDialog(self.window, 'ERROR: Unhandled exception', msg)
- d.exec_()
+
def main(args=sys.argv):
from PyQt4.Qt import QApplication, QMainWindow
diff --git a/src/libprs500/gui2/main_window.py b/src/libprs500/gui2/main_window.py
new file mode 100644
index 0000000000..e5d7374113
--- /dev/null
+++ b/src/libprs500/gui2/main_window.py
@@ -0,0 +1,30 @@
+## Copyright (C) 2007 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.
+
+import StringIO, traceback, sys
+
+from libprs500.gui2.dialogs.conversion_error import ConversionErrorDialog
+
+class MainWindow(object):
+
+ def unhandled_exception(self, type, value, tb):
+ sio = StringIO.StringIO()
+ traceback.print_exception(type, value, tb, file=sio)
+ fe = sio.getvalue()
+ print >>sys.stderr, fe
+ msg = '' + unicode(str(value), 'utf8', 'replace') + '
'
+ msg += 'Detailed traceback:
'+fe+'
'
+ d = ConversionErrorDialog(self.window, 'ERROR: Unhandled exception', msg)
+ d.exec_()
\ No newline at end of file