Show More
@@ -1,128 +1,127 b'' | |||||
1 | # System library imports |
|
1 | # System library imports | |
2 | from PyQt4 import QtCore, QtGui |
|
2 | from PyQt4 import QtCore, QtGui | |
3 |
|
3 | |||
4 | # Local imports |
|
4 | # Local imports | |
5 | from IPython.frontend.qt.svg import save_svg, svg_to_clipboard, svg_to_image |
|
5 | from IPython.frontend.qt.svg import save_svg, svg_to_clipboard, svg_to_image | |
6 | from ipython_widget import IPythonWidget |
|
6 | from ipython_widget import IPythonWidget | |
7 |
|
7 | |||
8 |
|
8 | |||
9 | class RichIPythonWidget(IPythonWidget): |
|
9 | class RichIPythonWidget(IPythonWidget): | |
10 | """ An IPythonWidget that supports rich text, including lists, images, and |
|
10 | """ An IPythonWidget that supports rich text, including lists, images, and | |
11 | tables. Note that raw performance will be reduced compared to the plain |
|
11 | tables. Note that raw performance will be reduced compared to the plain | |
12 | text version. |
|
12 | text version. | |
13 | """ |
|
13 | """ | |
14 |
|
14 | |||
15 | # Protected class variables. |
|
15 | # Protected class variables. | |
16 | _svg_text_format_property = 1 |
|
16 | _svg_text_format_property = 1 | |
17 |
|
17 | |||
18 | #--------------------------------------------------------------------------- |
|
18 | #--------------------------------------------------------------------------- | |
19 | # 'object' interface |
|
19 | # 'object' interface | |
20 | #--------------------------------------------------------------------------- |
|
20 | #--------------------------------------------------------------------------- | |
21 |
|
21 | |||
22 | def __init__(self, *args, **kw): |
|
22 | def __init__(self, *args, **kw): | |
23 | """ Create a RichIPythonWidget. |
|
23 | """ Create a RichIPythonWidget. | |
24 | """ |
|
24 | """ | |
25 | kw['kind'] = 'rich' |
|
25 | kw['kind'] = 'rich' | |
26 | super(RichIPythonWidget, self).__init__(*args, **kw) |
|
26 | super(RichIPythonWidget, self).__init__(*args, **kw) | |
27 |
|
27 | |||
28 | #--------------------------------------------------------------------------- |
|
28 | #--------------------------------------------------------------------------- | |
29 | # 'ConsoleWidget' protected interface |
|
29 | # 'ConsoleWidget' protected interface | |
30 | #--------------------------------------------------------------------------- |
|
30 | #--------------------------------------------------------------------------- | |
31 |
|
31 | |||
32 | def _show_context_menu(self, pos): |
|
32 | def _show_context_menu(self, pos): | |
33 | """ Reimplemented to show a custom context menu for images. |
|
33 | """ Reimplemented to show a custom context menu for images. | |
34 | """ |
|
34 | """ | |
35 | format = self._control.cursorForPosition(pos).charFormat() |
|
35 | format = self._control.cursorForPosition(pos).charFormat() | |
36 | name = format.stringProperty(QtGui.QTextFormat.ImageName) |
|
36 | name = format.stringProperty(QtGui.QTextFormat.ImageName) | |
37 | if name.isEmpty(): |
|
37 | if name.isEmpty(): | |
38 | super(RichIPythonWidget, self)._show_context_menu(pos) |
|
38 | super(RichIPythonWidget, self)._show_context_menu(pos) | |
39 | else: |
|
39 | else: | |
40 | menu = QtGui.QMenu() |
|
40 | menu = QtGui.QMenu() | |
41 |
|
41 | |||
42 | menu.addAction('Copy Image', lambda: self._copy_image(name)) |
|
42 | menu.addAction('Copy Image', lambda: self._copy_image(name)) | |
43 | menu.addAction('Save Image As...', lambda: self._save_image(name)) |
|
43 | menu.addAction('Save Image As...', lambda: self._save_image(name)) | |
44 | menu.addSeparator() |
|
44 | menu.addSeparator() | |
45 |
|
45 | |||
46 | svg = format.stringProperty(self._svg_text_format_property) |
|
46 | svg = format.stringProperty(self._svg_text_format_property) | |
47 | if not svg.isEmpty(): |
|
47 | if not svg.isEmpty(): | |
48 | menu.addSeparator() |
|
48 | menu.addSeparator() | |
49 | menu.addAction('Copy SVG', lambda: svg_to_clipboard(svg)) |
|
49 | menu.addAction('Copy SVG', lambda: svg_to_clipboard(svg)) | |
50 | menu.addAction('Save SVG As...', |
|
50 | menu.addAction('Save SVG As...', | |
51 | lambda: save_svg(svg, self._control)) |
|
51 | lambda: save_svg(svg, self._control)) | |
52 |
|
52 | |||
53 | menu.exec_(self._control.mapToGlobal(pos)) |
|
53 | menu.exec_(self._control.mapToGlobal(pos)) | |
54 |
|
54 | |||
55 | #--------------------------------------------------------------------------- |
|
55 | #--------------------------------------------------------------------------- | |
56 | # 'FrontendWidget' protected interface |
|
56 | # 'FrontendWidget' protected interface | |
57 | #--------------------------------------------------------------------------- |
|
57 | #--------------------------------------------------------------------------- | |
58 |
|
58 | |||
59 | def _process_execute_ok(self, msg): |
|
59 | def _process_execute_ok(self, msg): | |
60 | """ Reimplemented to handle matplotlib plot payloads. |
|
60 | """ Reimplemented to handle matplotlib plot payloads. | |
61 | """ |
|
61 | """ | |
62 | payload = msg['content']['payload'] |
|
62 | payload = msg['content']['payload'] | |
63 |
|
|
63 | for item in payload: | |
64 |
f |
|
64 | if item['type'] == 'plot': | |
65 |
if item['t |
|
65 | if item['format'] == 'svg': | |
66 |
|
|
66 | svg = item['data'] | |
67 |
|
|
67 | try: | |
68 |
|
|
68 | image = svg_to_image(svg) | |
69 | image = svg_to_image(svg) |
|
69 | except ValueError: | |
70 | except ValueError: |
|
70 | self._append_plain_text('Received invalid plot data.') | |
71 | self._append_plain_text('Received invalid plot data.') |
|
|||
72 | else: |
|
|||
73 | format = self._add_image(image) |
|
|||
74 | format.setProperty(self._svg_text_format_property, svg) |
|
|||
75 | cursor = self._get_end_cursor() |
|
|||
76 | cursor.insertBlock() |
|
|||
77 | cursor.insertImage(format) |
|
|||
78 | cursor.insertBlock() |
|
|||
79 | else: |
|
71 | else: | |
80 |
|
|
72 | format = self._add_image(image) | |
81 | pass |
|
73 | format.setProperty(self._svg_text_format_property, svg) | |
|
74 | cursor = self._get_end_cursor() | |||
|
75 | cursor.insertBlock() | |||
|
76 | cursor.insertImage(format) | |||
|
77 | cursor.insertBlock() | |||
82 | else: |
|
78 | else: | |
83 |
# Add other p |
|
79 | # Add other plot formats here! | |
84 | pass |
|
80 | pass | |
|
81 | else: | |||
|
82 | # Add other payload types here! | |||
|
83 | pass | |||
85 | else: |
|
84 | else: | |
86 | super(RichIPythonWidget, self)._process_execute_ok(msg) |
|
85 | super(RichIPythonWidget, self)._process_execute_ok(msg) | |
87 |
|
86 | |||
88 | #--------------------------------------------------------------------------- |
|
87 | #--------------------------------------------------------------------------- | |
89 | # 'RichIPythonWidget' protected interface |
|
88 | # 'RichIPythonWidget' protected interface | |
90 | #--------------------------------------------------------------------------- |
|
89 | #--------------------------------------------------------------------------- | |
91 |
|
90 | |||
92 | def _add_image(self, image): |
|
91 | def _add_image(self, image): | |
93 | """ Adds the specified QImage to the document and returns a |
|
92 | """ Adds the specified QImage to the document and returns a | |
94 | QTextImageFormat that references it. |
|
93 | QTextImageFormat that references it. | |
95 | """ |
|
94 | """ | |
96 | document = self._control.document() |
|
95 | document = self._control.document() | |
97 | name = QtCore.QString.number(image.cacheKey()) |
|
96 | name = QtCore.QString.number(image.cacheKey()) | |
98 | document.addResource(QtGui.QTextDocument.ImageResource, |
|
97 | document.addResource(QtGui.QTextDocument.ImageResource, | |
99 | QtCore.QUrl(name), image) |
|
98 | QtCore.QUrl(name), image) | |
100 | format = QtGui.QTextImageFormat() |
|
99 | format = QtGui.QTextImageFormat() | |
101 | format.setName(name) |
|
100 | format.setName(name) | |
102 | return format |
|
101 | return format | |
103 |
|
102 | |||
104 | def _copy_image(self, name): |
|
103 | def _copy_image(self, name): | |
105 | """ Copies the ImageResource with 'name' to the clipboard. |
|
104 | """ Copies the ImageResource with 'name' to the clipboard. | |
106 | """ |
|
105 | """ | |
107 | image = self._get_image(name) |
|
106 | image = self._get_image(name) | |
108 | QtGui.QApplication.clipboard().setImage(image) |
|
107 | QtGui.QApplication.clipboard().setImage(image) | |
109 |
|
108 | |||
110 | def _get_image(self, name): |
|
109 | def _get_image(self, name): | |
111 | """ Returns the QImage stored as the ImageResource with 'name'. |
|
110 | """ Returns the QImage stored as the ImageResource with 'name'. | |
112 | """ |
|
111 | """ | |
113 | document = self._control.document() |
|
112 | document = self._control.document() | |
114 | variant = document.resource(QtGui.QTextDocument.ImageResource, |
|
113 | variant = document.resource(QtGui.QTextDocument.ImageResource, | |
115 | QtCore.QUrl(name)) |
|
114 | QtCore.QUrl(name)) | |
116 | return variant.toPyObject() |
|
115 | return variant.toPyObject() | |
117 |
|
116 | |||
118 | def _save_image(self, name, format='PNG'): |
|
117 | def _save_image(self, name, format='PNG'): | |
119 | """ Shows a save dialog for the ImageResource with 'name'. |
|
118 | """ Shows a save dialog for the ImageResource with 'name'. | |
120 | """ |
|
119 | """ | |
121 | dialog = QtGui.QFileDialog(self._control, 'Save Image') |
|
120 | dialog = QtGui.QFileDialog(self._control, 'Save Image') | |
122 | dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) |
|
121 | dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) | |
123 | dialog.setDefaultSuffix(format.lower()) |
|
122 | dialog.setDefaultSuffix(format.lower()) | |
124 | dialog.setNameFilter('%s file (*.%s)' % (format, format.lower())) |
|
123 | dialog.setNameFilter('%s file (*.%s)' % (format, format.lower())) | |
125 | if dialog.exec_(): |
|
124 | if dialog.exec_(): | |
126 | filename = dialog.selectedFiles()[0] |
|
125 | filename = dialog.selectedFiles()[0] | |
127 | image = self._get_image(name) |
|
126 | image = self._get_image(name) | |
128 | image.save(filename, format) |
|
127 | image.save(filename, format) |
General Comments 0
You need to be logged in to leave comments.
Login now