##// END OF EJS Templates
* Added a custom context menu to the RichIPythonWidget which allows saving plot as an images or SVG documents....
epatters -
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 = QtGui.QAction('Copy', menu)
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 = QtGui.QAction('Paste', menu)
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 = image_from_svg(plot_payload['data'])
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(image)
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, QtGui
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