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 | 942 | menu = QtGui.QMenu() |
|
943 | 943 | |
|
944 |
copy_action = |
|
|
945 | copy_action.triggered.connect(self.copy) | |
|
944 | copy_action = menu.addAction('Copy', self.copy) | |
|
946 | 945 | copy_action.setEnabled(self._get_cursor().hasSelection()) |
|
947 | 946 | copy_action.setShortcut(QtGui.QKeySequence.Copy) |
|
948 | menu.addAction(copy_action) | |
|
949 | 947 | |
|
950 |
paste_action = |
|
|
951 | paste_action.triggered.connect(self.paste) | |
|
948 | paste_action = menu.addAction('Paste', self.paste) | |
|
952 | 949 | paste_action.setEnabled(self.can_paste()) |
|
953 | 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) | |
|
958 | select_all_action.triggered.connect(self.select_all) | |
|
959 | menu.addAction(select_all_action) | |
|
952 | menu.addSeparator() | |
|
953 | menu.addAction('Select All', self.select_all) | |
|
960 | 954 | |
|
961 | 955 | menu.exec_(self._control.mapToGlobal(pos)) |
|
962 | 956 |
@@ -2,7 +2,7 b'' | |||
|
2 | 2 | from PyQt4 import QtCore, QtGui |
|
3 | 3 | |
|
4 | 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 | 6 | from ipython_widget import IPythonWidget |
|
7 | 7 | |
|
8 | 8 | |
@@ -12,6 +12,9 b' class RichIPythonWidget(IPythonWidget):' | |||
|
12 | 12 | text version. |
|
13 | 13 | """ |
|
14 | 14 | |
|
15 | # Protected class variables. | |
|
16 | _svg_text_format_property = 1 | |
|
17 | ||
|
15 | 18 | #--------------------------------------------------------------------------- |
|
16 | 19 | # 'QObject' interface |
|
17 | 20 | #--------------------------------------------------------------------------- |
@@ -20,6 +23,33 b' class RichIPythonWidget(IPythonWidget):' | |||
|
20 | 23 | """ Create a RichIPythonWidget. |
|
21 | 24 | """ |
|
22 | 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 | 55 | # 'FrontendWidget' protected interface |
@@ -30,14 +60,59 b' class RichIPythonWidget(IPythonWidget):' | |||
|
30 | 60 | """ |
|
31 | 61 | plot_payload = payload.get('plot', None) |
|
32 | 62 | if plot_payload and plot_payload['format'] == 'svg': |
|
63 | svg = plot_payload['data'] | |
|
33 | 64 | try: |
|
34 |
image = |
|
|
65 | image = svg_to_image(svg) | |
|
35 | 66 | except ValueError: |
|
36 | 67 | self._append_plain_text('Received invalid plot data.') |
|
37 | 68 | else: |
|
69 | format = self._add_image(image) | |
|
70 | format.setProperty(self._svg_text_format_property, svg) | |
|
38 | 71 | cursor = self._get_end_cursor() |
|
39 | 72 | cursor.insertBlock() |
|
40 |
cursor.insertImage( |
|
|
73 | cursor.insertImage(format) | |
|
41 | 74 | cursor.insertBlock() |
|
42 | 75 | else: |
|
43 | 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 | 4 | # System library imports. |
|
5 |
from PyQt4 import QtCore |
|
|
5 | from PyQt4 import QtCore | |
|
6 | 6 | |
|
7 | 7 | # IPython imports. |
|
8 | 8 | from IPython.utils.traitlets import HasTraits |
@@ -23,42 +23,3 b' class MetaQObjectHasTraits(MetaQObject, MetaHasTraits):' | |||
|
23 | 23 | QObject. See QtKernelManager for an example. |
|
24 | 24 | """ |
|
25 | 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