Show More
@@ -0,0 +1,89 b'' | |||||
|
1 | """ Defines utility functions for working with SVG documents in Qt. | |||
|
2 | """ | |||
|
3 | ||||
|
4 | # System library imports. | |||
|
5 | from PyQt4 import QtCore, QtGui, QtSvg | |||
|
6 | ||||
|
7 | ||||
|
8 | def save_svg(string, parent=None): | |||
|
9 | """ Prompts the user to save an SVG document to disk. | |||
|
10 | ||||
|
11 | Parameters: | |||
|
12 | ----------- | |||
|
13 | string : str | |||
|
14 | A Python string or QString containing a SVG document. | |||
|
15 | ||||
|
16 | parent : QWidget, optional | |||
|
17 | The parent to use for the file dialog. | |||
|
18 | ||||
|
19 | Returns: | |||
|
20 | -------- | |||
|
21 | The name of the file to which the document was saved, or None if the save | |||
|
22 | was cancelled. | |||
|
23 | """ | |||
|
24 | dialog = QtGui.QFileDialog(parent, 'Save SVG Document') | |||
|
25 | dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) | |||
|
26 | dialog.setDefaultSuffix('svg') | |||
|
27 | dialog.setNameFilter('SVG document (*.svg)') | |||
|
28 | if dialog.exec_(): | |||
|
29 | filename = dialog.selectedFiles()[0] | |||
|
30 | f = open(filename, 'w') | |||
|
31 | try: | |||
|
32 | f.write(string) | |||
|
33 | finally: | |||
|
34 | f.close() | |||
|
35 | return filename | |||
|
36 | return None | |||
|
37 | ||||
|
38 | def svg_to_clipboard(string): | |||
|
39 | """ Copy a SVG document to the clipboard. | |||
|
40 | ||||
|
41 | Parameters: | |||
|
42 | ----------- | |||
|
43 | string : str | |||
|
44 | A Python string or QString containing a SVG document. | |||
|
45 | """ | |||
|
46 | if isinstance(string, basestring): | |||
|
47 | bytes = QtCore.QByteArray(string) | |||
|
48 | else: | |||
|
49 | bytes = string.toAscii() | |||
|
50 | mime_data = QtCore.QMimeData() | |||
|
51 | mime_data.setData('image/svg+xml', bytes) | |||
|
52 | QtGui.QApplication.clipboard().setMimeData(mime_data) | |||
|
53 | ||||
|
54 | def svg_to_image(string, size=None): | |||
|
55 | """ Convert a SVG document to a QImage. | |||
|
56 | ||||
|
57 | Parameters: | |||
|
58 | ----------- | |||
|
59 | string : str | |||
|
60 | A Python string or QString containing a SVG document. | |||
|
61 | ||||
|
62 | size : QSize, optional | |||
|
63 | The size of the image that is produced. If not specified, the SVG | |||
|
64 | document's default size is used. | |||
|
65 | ||||
|
66 | Raises: | |||
|
67 | ------- | |||
|
68 | ValueError | |||
|
69 | If an invalid SVG string is provided. | |||
|
70 | ||||
|
71 | Returns: | |||
|
72 | -------- | |||
|
73 | A QImage of format QImage.Format_ARGB32. | |||
|
74 | """ | |||
|
75 | if isinstance(string, basestring): | |||
|
76 | bytes = QtCore.QByteArray.fromRawData(string) # shallow copy | |||
|
77 | else: | |||
|
78 | bytes = string.toAscii() | |||
|
79 | ||||
|
80 | renderer = QtSvg.QSvgRenderer(bytes) | |||
|
81 | if not renderer.isValid(): | |||
|
82 | raise ValueError('Invalid SVG data.') | |||
|
83 | ||||
|
84 | if size is None: | |||
|
85 | size = renderer.defaultSize() | |||
|
86 | image = QtGui.QImage(size, QtGui.QImage.Format_ARGB32) | |||
|
87 | painter = QtGui.QPainter(image) | |||
|
88 | renderer.render(painter) | |||
|
89 | return image |
@@ -941,22 +941,16 b' class ConsoleWidget(QtGui.QWidget):' | |||||
941 | """ |
|
941 | """ | |
942 | menu = QtGui.QMenu() |
|
942 | menu = QtGui.QMenu() | |
943 |
|
943 | |||
944 |
copy_action = |
|
944 | copy_action = menu.addAction('Copy', self.copy) | |
945 | copy_action.triggered.connect(self.copy) |
|
|||
946 | copy_action.setEnabled(self._get_cursor().hasSelection()) |
|
945 | copy_action.setEnabled(self._get_cursor().hasSelection()) | |
947 | copy_action.setShortcut(QtGui.QKeySequence.Copy) |
|
946 | copy_action.setShortcut(QtGui.QKeySequence.Copy) | |
948 | menu.addAction(copy_action) |
|
|||
949 |
|
947 | |||
950 |
paste_action = |
|
948 | paste_action = menu.addAction('Paste', self.paste) | |
951 | paste_action.triggered.connect(self.paste) |
|
|||
952 | paste_action.setEnabled(self.can_paste()) |
|
949 | paste_action.setEnabled(self.can_paste()) | |
953 | paste_action.setShortcut(QtGui.QKeySequence.Paste) |
|
950 | paste_action.setShortcut(QtGui.QKeySequence.Paste) | |
954 | menu.addAction(paste_action) |
|
|||
955 | menu.addSeparator() |
|
|||
956 |
|
951 | |||
957 | select_all_action = QtGui.QAction('Select All', menu) |
|
952 | menu.addSeparator() | |
958 | select_all_action.triggered.connect(self.select_all) |
|
953 | menu.addAction('Select All', self.select_all) | |
959 | menu.addAction(select_all_action) |
|
|||
960 |
|
954 | |||
961 | menu.exec_(self._control.mapToGlobal(pos)) |
|
955 | menu.exec_(self._control.mapToGlobal(pos)) | |
962 |
|
956 |
@@ -2,7 +2,7 b'' | |||||
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.util import image_from_svg |
|
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 | |||
@@ -12,6 +12,9 b' class RichIPythonWidget(IPythonWidget):' | |||||
12 | text version. |
|
12 | text version. | |
13 | """ |
|
13 | """ | |
14 |
|
14 | |||
|
15 | # Protected class variables. | |||
|
16 | _svg_text_format_property = 1 | |||
|
17 | ||||
15 | #--------------------------------------------------------------------------- |
|
18 | #--------------------------------------------------------------------------- | |
16 | # 'QObject' interface |
|
19 | # 'QObject' interface | |
17 | #--------------------------------------------------------------------------- |
|
20 | #--------------------------------------------------------------------------- | |
@@ -20,6 +23,33 b' class RichIPythonWidget(IPythonWidget):' | |||||
20 | """ Create a RichIPythonWidget. |
|
23 | """ Create a RichIPythonWidget. | |
21 | """ |
|
24 | """ | |
22 | super(RichIPythonWidget, self).__init__(kind='rich', parent=parent) |
|
25 | super(RichIPythonWidget, self).__init__(kind='rich', parent=parent) | |
|
26 | ||||
|
27 | #--------------------------------------------------------------------------- | |||
|
28 | # 'ConsoleWidget' protected interface | |||
|
29 | #--------------------------------------------------------------------------- | |||
|
30 | ||||
|
31 | def _show_context_menu(self, pos): | |||
|
32 | """ Reimplemented to show a custom context menu for images. | |||
|
33 | """ | |||
|
34 | format = self._control.cursorForPosition(pos).charFormat() | |||
|
35 | name = format.stringProperty(QtGui.QTextFormat.ImageName) | |||
|
36 | if name.isEmpty(): | |||
|
37 | super(RichIPythonWidget, self)._show_context_menu(pos) | |||
|
38 | else: | |||
|
39 | menu = QtGui.QMenu() | |||
|
40 | ||||
|
41 | menu.addAction('Copy Image', lambda: self._copy_image(name)) | |||
|
42 | menu.addAction('Save Image As...', lambda: self._save_image(name)) | |||
|
43 | menu.addSeparator() | |||
|
44 | ||||
|
45 | svg = format.stringProperty(self._svg_text_format_property) | |||
|
46 | if not svg.isEmpty(): | |||
|
47 | menu.addSeparator() | |||
|
48 | menu.addAction('Copy SVG', lambda: svg_to_clipboard(svg)) | |||
|
49 | menu.addAction('Save SVG As...', | |||
|
50 | lambda: save_svg(svg, self._control)) | |||
|
51 | ||||
|
52 | menu.exec_(self._control.mapToGlobal(pos)) | |||
23 |
|
53 | |||
24 | #--------------------------------------------------------------------------- |
|
54 | #--------------------------------------------------------------------------- | |
25 | # 'FrontendWidget' protected interface |
|
55 | # 'FrontendWidget' protected interface | |
@@ -30,14 +60,59 b' class RichIPythonWidget(IPythonWidget):' | |||||
30 | """ |
|
60 | """ | |
31 | plot_payload = payload.get('plot', None) |
|
61 | plot_payload = payload.get('plot', None) | |
32 | if plot_payload and plot_payload['format'] == 'svg': |
|
62 | if plot_payload and plot_payload['format'] == 'svg': | |
|
63 | svg = plot_payload['data'] | |||
33 | try: |
|
64 | try: | |
34 |
image = |
|
65 | image = svg_to_image(svg) | |
35 | except ValueError: |
|
66 | except ValueError: | |
36 | self._append_plain_text('Received invalid plot data.') |
|
67 | self._append_plain_text('Received invalid plot data.') | |
37 | else: |
|
68 | else: | |
|
69 | format = self._add_image(image) | |||
|
70 | format.setProperty(self._svg_text_format_property, svg) | |||
38 | cursor = self._get_end_cursor() |
|
71 | cursor = self._get_end_cursor() | |
39 | cursor.insertBlock() |
|
72 | cursor.insertBlock() | |
40 |
cursor.insertImage( |
|
73 | cursor.insertImage(format) | |
41 | cursor.insertBlock() |
|
74 | cursor.insertBlock() | |
42 | else: |
|
75 | else: | |
43 | super(RichIPythonWidget, self)._handle_execute_payload(payload) |
|
76 | super(RichIPythonWidget, self)._handle_execute_payload(payload) | |
|
77 | ||||
|
78 | #--------------------------------------------------------------------------- | |||
|
79 | # 'RichIPythonWidget' protected interface | |||
|
80 | #--------------------------------------------------------------------------- | |||
|
81 | ||||
|
82 | def _add_image(self, image): | |||
|
83 | """ Adds the specified QImage to the document and returns a | |||
|
84 | QTextImageFormat that references it. | |||
|
85 | """ | |||
|
86 | document = self._control.document() | |||
|
87 | name = QtCore.QString.number(image.cacheKey()) | |||
|
88 | document.addResource(QtGui.QTextDocument.ImageResource, | |||
|
89 | QtCore.QUrl(name), image) | |||
|
90 | format = QtGui.QTextImageFormat() | |||
|
91 | format.setName(name) | |||
|
92 | return format | |||
|
93 | ||||
|
94 | def _copy_image(self, name): | |||
|
95 | """ Copies the ImageResource with 'name' to the clipboard. | |||
|
96 | """ | |||
|
97 | image = self._get_image(name) | |||
|
98 | QtGui.QApplication.clipboard().setImage(image) | |||
|
99 | ||||
|
100 | def _get_image(self, name): | |||
|
101 | """ Returns the QImage stored as the ImageResource with 'name'. | |||
|
102 | """ | |||
|
103 | document = self._control.document() | |||
|
104 | variant = document.resource(QtGui.QTextDocument.ImageResource, | |||
|
105 | QtCore.QUrl(name)) | |||
|
106 | return variant.toPyObject() | |||
|
107 | ||||
|
108 | def _save_image(self, name, format='PNG'): | |||
|
109 | """ Shows a save dialog for the ImageResource with 'name'. | |||
|
110 | """ | |||
|
111 | dialog = QtGui.QFileDialog(self._control, 'Save Image') | |||
|
112 | dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) | |||
|
113 | dialog.setDefaultSuffix(format.lower()) | |||
|
114 | dialog.setNameFilter('%s file (*.%s)' % (format, format.lower())) | |||
|
115 | if dialog.exec_(): | |||
|
116 | filename = dialog.selectedFiles()[0] | |||
|
117 | image = self._get_image(name) | |||
|
118 | image.save(filename, format) |
@@ -2,7 +2,7 b'' | |||||
2 | """ |
|
2 | """ | |
3 |
|
3 | |||
4 | # System library imports. |
|
4 | # System library imports. | |
5 |
from PyQt4 import QtCore |
|
5 | from PyQt4 import QtCore | |
6 |
|
6 | |||
7 | # IPython imports. |
|
7 | # IPython imports. | |
8 | from IPython.utils.traitlets import HasTraits |
|
8 | from IPython.utils.traitlets import HasTraits | |
@@ -23,42 +23,3 b' class MetaQObjectHasTraits(MetaQObject, MetaHasTraits):' | |||||
23 | QObject. See QtKernelManager for an example. |
|
23 | QObject. See QtKernelManager for an example. | |
24 | """ |
|
24 | """ | |
25 | pass |
|
25 | pass | |
26 |
|
||||
27 | #----------------------------------------------------------------------------- |
|
|||
28 | # Functions |
|
|||
29 | #----------------------------------------------------------------------------- |
|
|||
30 |
|
||||
31 | def image_from_svg(string, size=None): |
|
|||
32 | """ Convert a SVG document to a QImage. |
|
|||
33 |
|
||||
34 | Parameters: |
|
|||
35 | ----------- |
|
|||
36 | string : str |
|
|||
37 | A Python string containing a SVG document. |
|
|||
38 |
|
||||
39 | size : QSize, optional |
|
|||
40 | The size of the image that is produced. If not specified, the SVG |
|
|||
41 | document's default size is used. |
|
|||
42 |
|
||||
43 | Raises: |
|
|||
44 | ------- |
|
|||
45 | ValueError |
|
|||
46 | If an invalid SVG string is provided. |
|
|||
47 |
|
||||
48 | Returns: |
|
|||
49 | -------- |
|
|||
50 | A QImage of format QImage.Format_ARGB32. |
|
|||
51 | """ |
|
|||
52 | from PyQt4 import QtSvg |
|
|||
53 |
|
||||
54 | bytes = QtCore.QByteArray.fromRawData(string) # shallow copy |
|
|||
55 | renderer = QtSvg.QSvgRenderer(bytes) |
|
|||
56 | if not renderer.isValid(): |
|
|||
57 | raise ValueError('Invalid SVG data.') |
|
|||
58 |
|
||||
59 | if size is None: |
|
|||
60 | size = renderer.defaultSize() |
|
|||
61 | image = QtGui.QImage(size, QtGui.QImage.Format_ARGB32) |
|
|||
62 | painter = QtGui.QPainter(image) |
|
|||
63 | renderer.render(painter) |
|
|||
64 | return image |
|
General Comments 0
You need to be logged in to leave comments.
Login now