##// END OF EJS Templates
Merge branch 'master' of http://github.com/markvoorhies/ipython into markvoorhies-master...
Fernando Perez -
r3150:aae8597d merge
parent child Browse files
Show More
@@ -507,6 +507,136 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
507 return
507 return
508 self._control.print_(printer)
508 self._control.print_(printer)
509
509
510 def export_html_inline(self, parent = None):
511 """ Export the contents of the ConsoleWidget as HTML with inline PNGs.
512 """
513 self.export_html(parent, inline = True)
514
515 def export_html(self, parent = None, inline = False):
516 """ Export the contents of the ConsoleWidget as HTML.
517
518 Parameters:
519 -----------
520 inline : bool, optional [default True]
521
522 If True, include images as inline PNGs. Otherwise,
523 include them as links to external PNG files, mimicking
524 Firefox's "Web Page, complete" behavior.
525 """
526 dialog = QtGui.QFileDialog(parent, 'Save HTML Document')
527 dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
528 dialog.setDefaultSuffix('htm')
529 dialog.setNameFilter('HTML document (*.htm)')
530 if dialog.exec_():
531 filename = str(dialog.selectedFiles()[0])
532 if(inline):
533 path = None
534 else:
535 offset = filename.rfind(".")
536 if(offset > 0):
537 path = filename[:offset]+"_files"
538 else:
539 path = filename+"_files"
540 import os
541 try:
542 os.mkdir(path)
543 except OSError:
544 # TODO: check that this is an "already exists" error
545 pass
546
547 f = open(filename, 'w')
548 try:
549 # N.B. this is overly restrictive, but Qt's output is
550 # predictable...
551 img_re = re.compile(r'<img src="(?P<name>[\d]+)" />')
552 html = self.fix_html_encoding(
553 str(self._control.toHtml().toUtf8()))
554 f.write(img_re.sub(
555 lambda x: self.image_tag(x, path = path, format = "png"),
556 html))
557 finally:
558 f.close()
559 return filename
560 return None
561
562 def export_xhtml(self, parent = None):
563 """ Export the contents of the ConsoleWidget as XHTML with inline SVGs.
564 """
565 dialog = QtGui.QFileDialog(parent, 'Save XHTML Document')
566 dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
567 dialog.setDefaultSuffix('xml')
568 dialog.setNameFilter('XHTML document (*.xml)')
569 if dialog.exec_():
570 filename = str(dialog.selectedFiles()[0])
571 f = open(filename, 'w')
572 try:
573 # N.B. this is overly restrictive, but Qt's output is
574 # predictable...
575 img_re = re.compile(r'<img src="(?P<name>[\d]+)" />')
576 html = str(self._control.toHtml().toUtf8())
577 # Hack to make xhtml header -- note that we are not doing
578 # any check for valid xml
579 offset = html.find("<html>")
580 assert(offset > -1)
581 html = ('<html xmlns="http://www.w3.org/1999/xhtml">\n'+
582 html[offset+6:])
583 # And now declare UTF-8 encoding
584 html = self.fix_html_encoding(html)
585 f.write(img_re.sub(
586 lambda x: self.image_tag(x, path = None, format = "svg"),
587 html))
588 finally:
589 f.close()
590 return filename
591 return None
592
593 def fix_html_encoding(self, html):
594 """ Return html string, with a UTF-8 declaration added to <HEAD>.
595
596 Assumes that html is Qt generated and has already been UTF-8 encoded
597 and coerced to a python string. If the expected head element is
598 not found, the given object is returned unmodified.
599
600 This patching is needed for proper rendering of some characters
601 (e.g., indented commands) when viewing exported HTML on a local
602 system (i.e., without seeing an encoding declaration in an HTTP
603 header).
604
605 C.f. http://www.w3.org/International/O-charset for details.
606 """
607 offset = html.find("<head>")
608 if(offset > -1):
609 html = (html[:offset+6]+
610 '\n<meta http-equiv="Content-Type" '+
611 'content="text/html; charset=utf-8" />\n'+
612 html[offset+6:])
613
614 return html
615
616 def image_tag(self, match, path = None, format = "png"):
617 """ Return (X)HTML mark-up for the image-tag given by match.
618
619 Parameters
620 ----------
621 match : re.SRE_Match
622 A match to an HTML image tag as exported by Qt, with
623 match.group("Name") containing the matched image ID.
624
625 path : string|None, optional [default None]
626 If not None, specifies a path to which supporting files
627 may be written (e.g., for linked images).
628 If None, all images are to be included inline.
629
630 format : "png"|"svg", optional [default "png"]
631 Format for returned or referenced images.
632
633 Subclasses supporting image display should override this
634 method.
635 """
636
637 # Default case -- not enough information to generate tag
638 return ""
639
510 def prompt_to_top(self):
640 def prompt_to_top(self):
511 """ Moves the prompt to the top of the viewport.
641 """ Moves the prompt to the top of the viewport.
512 """
642 """
@@ -744,7 +874,15 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
744 menu.addSeparator()
874 menu.addSeparator()
745 print_action = menu.addAction('Print', self.print_)
875 print_action = menu.addAction('Print', self.print_)
746 print_action.setEnabled(True)
876 print_action.setEnabled(True)
747
877 html_action = menu.addAction('Export HTML (external PNGs)',
878 self.export_html)
879 html_action.setEnabled(True)
880 html_inline_action = menu.addAction('Export HTML (inline PNGs)',
881 self.export_html_inline)
882 html_inline_action.setEnabled(True)
883 xhtml_action = menu.addAction('Export XHTML (inline SVGs)',
884 self.export_xhtml)
885 xhtml_action.setEnabled(True)
748 return menu
886 return menu
749
887
750 def _control_key_down(self, modifiers, include_command=True):
888 def _control_key_down(self, modifiers, include_command=True):
@@ -25,6 +25,9 b' class RichIPythonWidget(IPythonWidget):'
25 """
25 """
26 kw['kind'] = 'rich'
26 kw['kind'] = 'rich'
27 super(RichIPythonWidget, self).__init__(*args, **kw)
27 super(RichIPythonWidget, self).__init__(*args, **kw)
28 # Dictionary for resolving Qt names to images when
29 # generating XHTML output
30 self._name_to_svg = {}
28
31
29 #---------------------------------------------------------------------------
32 #---------------------------------------------------------------------------
30 # 'ConsoleWidget' protected interface
33 # 'ConsoleWidget' protected interface
@@ -68,6 +71,7 b' class RichIPythonWidget(IPythonWidget):'
68 self._append_plain_text('Received invalid plot data.')
71 self._append_plain_text('Received invalid plot data.')
69 else:
72 else:
70 format = self._add_image(image)
73 format = self._add_image(image)
74 self._name_to_svg[str(format.name())] = svg
71 format.setProperty(self._svg_text_format_property, svg)
75 format.setProperty(self._svg_text_format_property, svg)
72 cursor = self._get_end_cursor()
76 cursor = self._get_end_cursor()
73 cursor.insertBlock()
77 cursor.insertBlock()
@@ -121,3 +125,68 b' class RichIPythonWidget(IPythonWidget):'
121 filename = dialog.selectedFiles()[0]
125 filename = dialog.selectedFiles()[0]
122 image = self._get_image(name)
126 image = self._get_image(name)
123 image.save(filename, format)
127 image.save(filename, format)
128
129 def image_tag(self, match, path = None, format = "png"):
130 """ Return (X)HTML mark-up for the image-tag given by match.
131
132 Parameters
133 ----------
134 match : re.SRE_Match
135 A match to an HTML image tag as exported by Qt, with
136 match.group("Name") containing the matched image ID.
137
138 path : string|None, optional [default None]
139 If not None, specifies a path to which supporting files
140 may be written (e.g., for linked images).
141 If None, all images are to be included inline.
142
143 format : "png"|"svg", optional [default "png"]
144 Format for returned or referenced images.
145
146 Subclasses supporting image display should override this
147 method.
148 """
149
150 if(format == "png"):
151 try:
152 image = self._get_image(match.group("name"))
153 except KeyError:
154 return "<b>Couldn't find image %s</b>" % match.group("name")
155
156 if(path is not None):
157 relpath = path[path.rfind("/")+1:]
158 if(image.save("%s/qt_img%s.png" % (path,match.group("name")),
159 "PNG")):
160 return '<img src="%s/qt_img%s.png">' % (relpath,
161 match.group("name"))
162 else:
163 return "<b>Couldn't save image!</b>"
164 else:
165 ba = QtCore.QByteArray()
166 buffer_ = QtCore.QBuffer(ba)
167 buffer_.open(QtCore.QIODevice.WriteOnly)
168 image.save(buffer_, "PNG")
169 buffer_.close()
170 import re
171 return '<img src="data:image/png;base64,\n%s\n" />' % (
172 re.sub(r'(.{60})',r'\1\n',str(ba.toBase64())))
173
174 elif(format == "svg"):
175 try:
176 svg = str(self._name_to_svg[match.group("name")])
177 except KeyError:
178 return "<b>Couldn't find image %s</b>" % match.group("name")
179
180 # Not currently checking path, because it's tricky to find a
181 # cross-browser way to embed external SVG images (e.g., via
182 # object or embed tags).
183
184 # Chop stand-alone header from matplotlib SVG
185 offset = svg.find("<svg")
186 assert(offset > -1)
187
188 return svg[offset:]
189
190 else:
191 return '<b>Unrecognized image format</b>'
192
General Comments 0
You need to be logged in to leave comments. Login now