mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
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:
parent
a050a683c1
commit
4815ecf0ae
@ -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'
|
||||||
|
@ -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)
|
||||||
|
@ -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>
|
||||||
|
2071
src/libprs500/gui2/images/view.svg
Normal file
2071
src/libprs500/gui2/images/view.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 73 KiB |
@ -108,11 +108,8 @@ 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
|
||||||
@ -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)
|
||||||
|
@ -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()'))
|
|
||||||
|
|
||||||
class Main(QObject, Ui_MainWindow, MainWindow):
|
def abort(self):
|
||||||
def __init__(self, window, stream, logger, opts):
|
if self.lrf is not None:
|
||||||
QObject.__init__(self)
|
self.aborted = True
|
||||||
|
self.lrf.keep_parsing = False
|
||||||
|
|
||||||
|
class Main(MainWindow, Ui_MainWindow):
|
||||||
|
|
||||||
|
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):
|
||||||
@ -160,13 +175,21 @@ 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):
|
|
||||||
from PyQt4.Qt import QMainWindow
|
def file_renderer(stream, opts, parent=None, logger=None):
|
||||||
window = QMainWindow()
|
if logger is None:
|
||||||
window.setAttribute(Qt.WA_DeleteOnClose)
|
level = logging.DEBUG if opts.verbose else logging.INFO
|
||||||
window.setWindowTitle(__appname__ + ' - LRF Viewer')
|
logger = logging.getLogger('lrfviewer')
|
||||||
return Main(window, stream, logger, opts)
|
setup_cli_handlers(logger, level)
|
||||||
|
|
||||||
|
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
|
||||||
|
@ -43,6 +43,11 @@ class PixmapItem(QGraphicsPixmapItem):
|
|||||||
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):
|
||||||
|
|
||||||
@ -214,6 +219,9 @@ class TextBlock(object):
|
|||||||
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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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_()
|
Loading…
x
Reference in New Issue
Block a user