Show More
@@ -161,10 +161,34 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
161 | self._reading_callback = None |
|
161 | self._reading_callback = None | |
162 | self._tab_width = 8 |
|
162 | self._tab_width = 8 | |
163 | self._text_completing_pos = 0 |
|
163 | self._text_completing_pos = 0 | |
|
164 | self._filename = os.path.join(os.curdir, 'ipython.html') | |||
164 |
|
165 | |||
165 | # Set a monospaced font. |
|
166 | # Set a monospaced font. | |
166 | self.reset_font() |
|
167 | self.reset_font() | |
167 |
|
168 | |||
|
169 | # Configure actions. | |||
|
170 | action = QtGui.QAction('Print', None) | |||
|
171 | action.setEnabled(True) | |||
|
172 | action.setShortcut(QtGui.QKeySequence.Print) | |||
|
173 | action.triggered.connect(self.print_) | |||
|
174 | self.addAction(action) | |||
|
175 | self._print_action = action | |||
|
176 | ||||
|
177 | action = QtGui.QAction('Save as HTML/XML', None) | |||
|
178 | action.setEnabled(self.can_export()) | |||
|
179 | action.setShortcut(QtGui.QKeySequence.Save) | |||
|
180 | action.triggered.connect(self.export) | |||
|
181 | self.addAction(action) | |||
|
182 | self._export_action = action | |||
|
183 | ||||
|
184 | action = QtGui.QAction('Select All', None) | |||
|
185 | action.setEnabled(True) | |||
|
186 | action.setShortcut(QtGui.QKeySequence.SelectAll) | |||
|
187 | action.triggered.connect(self.select_all) | |||
|
188 | self.addAction(action) | |||
|
189 | self._select_all_action = action | |||
|
190 | ||||
|
191 | ||||
168 | def eventFilter(self, obj, event): |
|
192 | def eventFilter(self, obj, event): | |
169 | """ Reimplemented to ensure a console-like behavior in the underlying |
|
193 | """ Reimplemented to ensure a console-like behavior in the underlying | |
170 | text widgets. |
|
194 | text widgets. | |
@@ -301,6 +325,12 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
301 | return not QtGui.QApplication.clipboard().text().isEmpty() |
|
325 | return not QtGui.QApplication.clipboard().text().isEmpty() | |
302 | return False |
|
326 | return False | |
303 |
|
327 | |||
|
328 | def can_export(self): | |||
|
329 | """Returns whether we can export. Currently only rich widgets | |||
|
330 | can export html. | |||
|
331 | """ | |||
|
332 | return self.kind == "rich" | |||
|
333 | ||||
304 | def clear(self, keep_input=True): |
|
334 | def clear(self, keep_input=True): | |
305 | """ Clear the console. |
|
335 | """ Clear the console. | |
306 |
|
336 | |||
@@ -502,96 +532,111 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
502 | def print_(self, printer = None): |
|
532 | def print_(self, printer = None): | |
503 | """ Print the contents of the ConsoleWidget to the specified QPrinter. |
|
533 | """ Print the contents of the ConsoleWidget to the specified QPrinter. | |
504 | """ |
|
534 | """ | |
505 |
if(printer |
|
535 | if (not printer): | |
506 | printer = QtGui.QPrinter() |
|
536 | printer = QtGui.QPrinter() | |
507 | if(QtGui.QPrintDialog(printer).exec_() != QtGui.QDialog.Accepted): |
|
537 | if(QtGui.QPrintDialog(printer).exec_() != QtGui.QDialog.Accepted): | |
508 | return |
|
538 | return | |
509 | self._control.print_(printer) |
|
539 | self._control.print_(printer) | |
510 |
|
540 | |||
511 |
def export |
|
541 | def export(self, parent = None): | |
512 | """ Export the contents of the ConsoleWidget as HTML with inline PNGs. |
|
542 | """Export HTML/XML in various modes from one Dialog.""" | |
513 | """ |
|
543 | parent = parent or None # sometimes parent is False | |
514 | self.export_html(parent, inline = True) |
|
544 | dialog = QtGui.QFileDialog(parent, 'Save Console as...') | |
515 |
|
545 | dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) | ||
516 | def export_html(self, parent = None, inline = False): |
|
546 | filters = [ | |
|
547 | 'HTML with inline PNGs (*.html *.htm)', | |||
|
548 | 'HTML with external PNGs (*.html *.htm)', | |||
|
549 | 'XHTML with inline SVGs (*.xhtml *.xml)' | |||
|
550 | ] | |||
|
551 | dialog.setNameFilters(filters) | |||
|
552 | if self._filename: | |||
|
553 | dialog.selectFile(self._filename) | |||
|
554 | root,ext = os.path.splitext(self._filename) | |||
|
555 | if ext.lower() in ('.xml', '.xhtml'): | |||
|
556 | dialog.selectNameFilter(filters[-1]) | |||
|
557 | if dialog.exec_(): | |||
|
558 | filename = str(dialog.selectedFiles()[0]) | |||
|
559 | self._filename = filename | |||
|
560 | choice = str(dialog.selectedNameFilter()) | |||
|
561 | ||||
|
562 | if choice.startswith('XHTML'): | |||
|
563 | exporter = self.export_xhtml | |||
|
564 | else: | |||
|
565 | exporter = lambda filename: self.export_html(filename, 'inline' in choice) | |||
|
566 | ||||
|
567 | try: | |||
|
568 | return exporter(filename) | |||
|
569 | except Exception, e: | |||
|
570 | title = self.window().windowTitle() | |||
|
571 | msg = "Error while saving to: %s\n"%filename+str(e) | |||
|
572 | reply = QtGui.QMessageBox.warning(self, title, msg, | |||
|
573 | QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok) | |||
|
574 | return None | |||
|
575 | ||||
|
576 | def export_html(self, filename, inline=False): | |||
517 | """ Export the contents of the ConsoleWidget as HTML. |
|
577 | """ Export the contents of the ConsoleWidget as HTML. | |
518 |
|
578 | |||
519 | Parameters: |
|
579 | Parameters: | |
520 | ----------- |
|
580 | ----------- | |
|
581 | filename : str | |||
|
582 | The file to be saved. | |||
521 | inline : bool, optional [default True] |
|
583 | inline : bool, optional [default True] | |
522 |
|
||||
523 | If True, include images as inline PNGs. Otherwise, |
|
584 | If True, include images as inline PNGs. Otherwise, | |
524 | include them as links to external PNG files, mimicking |
|
585 | include them as links to external PNG files, mimicking | |
525 |
|
|
586 | web browsers' "Web Page, Complete" behavior. | |
526 | """ |
|
587 | """ | |
527 | dialog = QtGui.QFileDialog(parent, 'Save HTML Document') |
|
588 | if(inline): | |
528 | dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) |
|
589 | path = None | |
529 | dialog.setDefaultSuffix('html') |
|
590 | else: | |
530 | dialog.setNameFilter('HTML Document (*.htm *.html *)') |
|
591 | root,ext = os.path.splitext(filename) | |
531 | if dialog.exec_(): |
|
592 | path = root+"_files" | |
532 | filename = str(dialog.selectedFiles()[0]) |
|
593 | if os.path.isfile(path): | |
533 | if(inline): |
|
594 | raise OSError("%s exists, but is not a directory."%path) | |
534 | path = None |
|
595 | ||
535 | else: |
|
596 | f = open(filename, 'w') | |
536 | offset = filename.rfind(".") |
|
597 | try: | |
537 | if(offset > 0): |
|
598 | # N.B. this is overly restrictive, but Qt's output is | |
538 | path = filename[:offset]+"_files" |
|
599 | # predictable... | |
539 | else: |
|
600 | img_re = re.compile(r'<img src="(?P<name>[\d]+)" />') | |
540 | path = filename+"_files" |
|
601 | html = self.fix_html_encoding( | |
541 | if os.path.isfile(path): |
|
602 | str(self._control.toHtml().toUtf8())) | |
542 | raise OSError("%s exists, but is not a dir"%path) |
|
603 | f.write(img_re.sub( | |
543 | # don't mkdir unless there are images |
|
604 | lambda x: self.image_tag(x, path = path, format = "png"), | |
544 |
|
|
605 | html)) | |
545 | # os.mkdir(path) |
|
606 | except Exception, e: | |
546 | # except OSError: |
|
607 | f.close() | |
547 | # # TODO: check that this is an "already exists" error |
|
608 | raise e | |
548 | # pass |
|
609 | else: | |
549 |
|
610 | f.close() | ||
550 | f = open(filename, 'w') |
|
611 | return filename | |
551 | try: |
|
612 | ||
552 | # N.B. this is overly restrictive, but Qt's output is |
|
|||
553 | # predictable... |
|
|||
554 | img_re = re.compile(r'<img src="(?P<name>[\d]+)" />') |
|
|||
555 | html = self.fix_html_encoding( |
|
|||
556 | str(self._control.toHtml().toUtf8())) |
|
|||
557 | f.write(img_re.sub( |
|
|||
558 | lambda x: self.image_tag(x, path = path, format = "png"), |
|
|||
559 | html)) |
|
|||
560 | finally: |
|
|||
561 | f.close() |
|
|||
562 | return filename |
|
|||
563 | return None |
|
|||
564 |
|
613 | |||
565 |
def export_xhtml(self, |
|
614 | def export_xhtml(self, filename): | |
566 | """ Export the contents of the ConsoleWidget as XHTML with inline SVGs. |
|
615 | """ Export the contents of the ConsoleWidget as XHTML with inline SVGs. | |
567 | """ |
|
616 | """ | |
568 | dialog = QtGui.QFileDialog(parent, 'Save XHTML Document') |
|
617 | f = open(filename, 'w') | |
569 | dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) |
|
618 | try: | |
570 | dialog.setDefaultSuffix('xml') |
|
619 | # N.B. this is overly restrictive, but Qt's output is | |
571 | dialog.setNameFilter('XHTML document (*.xml *.xhtml *)') |
|
620 | # predictable... | |
572 | if dialog.exec_(): |
|
621 | img_re = re.compile(r'<img src="(?P<name>[\d]+)" />') | |
573 | filename = str(dialog.selectedFiles()[0]) |
|
622 | html = str(self._control.toHtml().toUtf8()) | |
574 | f = open(filename, 'w') |
|
623 | # Hack to make xhtml header -- note that we are not doing | |
575 | try: |
|
624 | # any check for valid xml | |
576 | # N.B. this is overly restrictive, but Qt's output is |
|
625 | offset = html.find("<html>") | |
577 | # predictable... |
|
626 | assert(offset > -1) | |
578 | img_re = re.compile(r'<img src="(?P<name>[\d]+)" />') |
|
627 | html = ('<html xmlns="http://www.w3.org/1999/xhtml">\n'+ | |
579 | html = str(self._control.toHtml().toUtf8()) |
|
628 | html[offset+6:]) | |
580 | # Hack to make xhtml header -- note that we are not doing |
|
629 | # And now declare UTF-8 encoding | |
581 | # any check for valid xml |
|
630 | html = self.fix_html_encoding(html) | |
582 | offset = html.find("<html>") |
|
631 | f.write(img_re.sub( | |
583 | assert(offset > -1) |
|
632 | lambda x: self.image_tag(x, path = None, format = "svg"), | |
584 | html = ('<html xmlns="http://www.w3.org/1999/xhtml">\n'+ |
|
633 | html)) | |
585 | html[offset+6:]) |
|
634 | except Exception, e: | |
586 | # And now declare UTF-8 encoding |
|
635 | f.close() | |
587 | html = self.fix_html_encoding(html) |
|
636 | raise e | |
588 | f.write(img_re.sub( |
|
637 | else: | |
589 | lambda x: self.image_tag(x, path = None, format = "svg"), |
|
638 | f.close() | |
590 | html)) |
|
639 | return filename | |
591 | finally: |
|
|||
592 | f.close() |
|
|||
593 | return filename |
|
|||
594 | return None |
|
|||
595 |
|
640 | |||
596 | def fix_html_encoding(self, html): |
|
641 | def fix_html_encoding(self, html): | |
597 | """ Return html string, with a UTF-8 declaration added to <HEAD>. |
|
642 | """ Return html string, with a UTF-8 declaration added to <HEAD>. | |
@@ -857,7 +902,7 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
857 | def _context_menu_make(self, pos): |
|
902 | def _context_menu_make(self, pos): | |
858 | """ Creates a context menu for the given QPoint (in widget coordinates). |
|
903 | """ Creates a context menu for the given QPoint (in widget coordinates). | |
859 | """ |
|
904 | """ | |
860 | menu = QtGui.QMenu() |
|
905 | menu = QtGui.QMenu(self) | |
861 |
|
906 | |||
862 | cut_action = menu.addAction('Cut', self.cut) |
|
907 | cut_action = menu.addAction('Cut', self.cut) | |
863 | cut_action.setEnabled(self.can_cut()) |
|
908 | cut_action.setEnabled(self.can_cut()) | |
@@ -872,22 +917,12 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
872 | paste_action.setShortcut(QtGui.QKeySequence.Paste) |
|
917 | paste_action.setShortcut(QtGui.QKeySequence.Paste) | |
873 |
|
918 | |||
874 | menu.addSeparator() |
|
919 | menu.addSeparator() | |
875 |
menu.addAction( |
|
920 | menu.addAction(self._select_all_action) | |
876 |
|
921 | |||
877 | if self.kind == 'rich': |
|
922 | menu.addSeparator() | |
878 | # only the rich frontend can export html/xml |
|
923 | menu.addAction(self._export_action) | |
879 |
|
|
924 | menu.addAction(self._print_action) | |
880 | print_action = menu.addAction('Print', self.print_) |
|
925 | ||
881 | print_action.setEnabled(True) |
|
|||
882 | html_action = menu.addAction('Export HTML (external PNGs)', |
|
|||
883 | self.export_html) |
|
|||
884 | html_action.setEnabled(True) |
|
|||
885 | html_inline_action = menu.addAction('Export HTML (inline PNGs)', |
|
|||
886 | self.export_html_inline) |
|
|||
887 | html_inline_action.setEnabled(True) |
|
|||
888 | xhtml_action = menu.addAction('Export XHTML (inline SVGs)', |
|
|||
889 | self.export_xhtml) |
|
|||
890 | xhtml_action.setEnabled(True) |
|
|||
891 | return menu |
|
926 | return menu | |
892 |
|
927 | |||
893 | def _control_key_down(self, modifiers, include_command=True): |
|
928 | def _control_key_down(self, modifiers, include_command=True): |
General Comments 0
You need to be logged in to leave comments.
Login now