##// END OF EJS Templates
IPythonWidget now supports styling the syntax highlighting.
epatters -
Show More
@@ -41,7 +41,7 class IPythonWidget(FrontendWidget):
41 self._prompt_count = 0
41 self._prompt_count = 0
42
42
43 # Set a default stylesheet.
43 # Set a default stylesheet.
44 self.reset_style_sheet()
44 self.reset_styling()
45
45
46 #---------------------------------------------------------------------------
46 #---------------------------------------------------------------------------
47 # 'FrontendWidget' interface
47 # 'FrontendWidget' interface
@@ -106,17 +106,35 class IPythonWidget(FrontendWidget):
106 # 'IPythonWidget' interface
106 # 'IPythonWidget' interface
107 #---------------------------------------------------------------------------
107 #---------------------------------------------------------------------------
108
108
109 def reset_style_sheet(self):
109 def reset_styling(self):
110 """ Sets the style sheet to the default style sheet.
110 """ Restores the default IPythonWidget styling.
111 """
111 """
112 self.set_style_sheet(self.default_stylesheet)
112 self.set_styling(self.default_stylesheet, syntax_style='default')
113
113 #self.set_styling(self.dark_stylesheet, syntax_style='monokai')
114 def set_style_sheet(self, stylesheet):
114
115 """ Sets the style sheet.
115 def set_styling(self, stylesheet, syntax_style=None):
116 """ Sets the IPythonWidget styling.
117
118 Parameters:
119 -----------
120 stylesheet : str
121 A CSS stylesheet. The stylesheet can contain classes for:
122 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
123 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
124 3. IPython: .error, .in-prompt, .out-prompt, etc.
125
126 syntax_style : str or None [default None]
127 If specified, use the Pygments style with given name. Otherwise,
128 the stylesheet is queried for Pygments style information.
116 """
129 """
117 self.setStyleSheet(stylesheet)
130 self.setStyleSheet(stylesheet)
118 self.document().setDefaultStyleSheet(stylesheet)
131 self.document().setDefaultStyleSheet(stylesheet)
119
132
133 if syntax_style is None:
134 self._highlighter.set_style_sheet(stylesheet)
135 else:
136 self._highlighter.set_style(syntax_style)
137
120
138
121 if __name__ == '__main__':
139 if __name__ == '__main__':
122 from IPython.frontend.qt.kernelmanager import QtKernelManager
140 from IPython.frontend.qt.kernelmanager import QtKernelManager
@@ -1,9 +1,9
1 # System library imports.
1 # System library imports.
2 from PyQt4 import QtGui
2 from PyQt4 import QtGui
3 from pygments.formatters.html import HtmlFormatter
3 from pygments.lexer import RegexLexer, _TokenType, Text, Error
4 from pygments.lexer import RegexLexer, _TokenType, Text, Error
4 from pygments.lexers import PythonLexer
5 from pygments.lexers import PythonLexer
5 from pygments.styles.default import DefaultStyle
6 from pygments.styles import get_style_by_name
6 from pygments.token import Comment
7
7
8
8
9 def get_tokens_unprocessed(self, text, stack=('root',)):
9 def get_tokens_unprocessed(self, text, stack=('root',)):
@@ -87,20 +87,23 class PygmentsBlockUserData(QtGui.QTextBlockUserData):
87 class PygmentsHighlighter(QtGui.QSyntaxHighlighter):
87 class PygmentsHighlighter(QtGui.QSyntaxHighlighter):
88 """ Syntax highlighter that uses Pygments for parsing. """
88 """ Syntax highlighter that uses Pygments for parsing. """
89
89
90 #---------------------------------------------------------------------------
91 # 'QSyntaxHighlighter' interface
92 #---------------------------------------------------------------------------
93
90 def __init__(self, parent, lexer=None):
94 def __init__(self, parent, lexer=None):
91 super(PygmentsHighlighter, self).__init__(parent)
95 super(PygmentsHighlighter, self).__init__(parent)
92
96
97 self._document = QtGui.QTextDocument()
98 self._formatter = HtmlFormatter(nowrap=True)
93 self._lexer = lexer if lexer else PythonLexer()
99 self._lexer = lexer if lexer else PythonLexer()
94 self._style = DefaultStyle
100 self.set_style('default')
95 # Caches for formats and brushes.
96 self._brushes = {}
97 self._formats = {}
98
101
99 def highlightBlock(self, qstring):
102 def highlightBlock(self, qstring):
100 """ Highlight a block of text.
103 """ Highlight a block of text.
101 """
104 """
102 qstring = unicode(qstring)
105 qstring = unicode(qstring)
103 prev_data = self.previous_block_data()
106 prev_data = self.currentBlock().previous().userData()
104
107
105 if prev_data is not None:
108 if prev_data is not None:
106 self._lexer._saved_state_stack = prev_data.syntax_stack
109 self._lexer._saved_state_stack = prev_data.syntax_stack
@@ -123,18 +126,67 class PygmentsHighlighter(QtGui.QSyntaxHighlighter):
123 # Clean up for the next go-round.
126 # Clean up for the next go-round.
124 del self._lexer._saved_state_stack
127 del self._lexer._saved_state_stack
125
128
126 def previous_block_data(self):
129 #---------------------------------------------------------------------------
127 """ Convenience method for returning the previous block's user data.
130 # 'PygmentsHighlighter' interface
131 #---------------------------------------------------------------------------
132
133 def set_style(self, style):
134 """ Sets the style to the specified Pygments style.
135 """
136 if isinstance(style, basestring):
137 style = get_style_by_name(style)
138 self._style = style
139 self._clear_caches()
140
141 def set_style_sheet(self, stylesheet):
142 """ Sets a CSS stylesheet. The classes in the stylesheet should
143 correspond to those generated by:
144
145 pygmentize -S <style> -f html
146
147 Note that 'set_style' and 'set_style_sheet' completely override each
148 other, i.e. they cannot be used in conjunction.
149 """
150 self._document.setDefaultStyleSheet(stylesheet)
151 self._style = None
152 self._clear_caches()
153
154 #---------------------------------------------------------------------------
155 # Protected interface
156 #---------------------------------------------------------------------------
157
158 def _clear_caches(self):
159 """ Clear caches for brushes and formats.
128 """
160 """
129 return self.currentBlock().previous().userData()
161 self._brushes = {}
162 self._formats = {}
130
163
131 def _get_format(self, token):
164 def _get_format(self, token):
132 """ Returns a QTextCharFormat for token or None.
165 """ Returns a QTextCharFormat for token or None.
133 """
166 """
134 if token in self._formats:
167 if token in self._formats:
135 return self._formats[token]
168 return self._formats[token]
169
170 if self._style is None:
171 result = self._get_format_from_document(token, self._document)
172 else:
173 result = self._get_format_from_style(token, self._style)
174
175 self._formats[token] = result
176 return result
177
178 def _get_format_from_document(self, token, document):
179 """ Returns a QTextCharFormat for token by
180 """
181 code, html = self._formatter._format_lines([(token, 'dummy')]).next()
182 self._document.setHtml(html)
183 return QtGui.QTextCursor(self._document).charFormat()
184
185 def _get_format_from_style(self, token, style):
186 """ Returns a QTextCharFormat for token by reading a Pygments style.
187 """
136 result = None
188 result = None
137 for key, value in self._style.style_for_token(token).items():
189 for key, value in style.style_for_token(token).items():
138 if value:
190 if value:
139 if result is None:
191 if result is None:
140 result = QtGui.QTextCharFormat()
192 result = QtGui.QTextCharFormat()
@@ -155,13 +207,6 class PygmentsHighlighter(QtGui.QSyntaxHighlighter):
155 result.setFontStyleHint(QtGui.QFont.Times)
207 result.setFontStyleHint(QtGui.QFont.Times)
156 elif key == 'mono':
208 elif key == 'mono':
157 result.setFontStyleHint(QtGui.QFont.TypeWriter)
209 result.setFontStyleHint(QtGui.QFont.TypeWriter)
158 elif key == 'border':
159 # Borders are normally used for errors. We can't do a border
160 # so instead we do a wavy underline
161 result.setUnderlineStyle(
162 QtGui.QTextCharFormat.WaveUnderline)
163 result.setUnderlineColor(self._get_color(value))
164 self._formats[token] = result
165 return result
210 return result
166
211
167 def _get_brush(self, color):
212 def _get_brush(self, color):
General Comments 0
You need to be logged in to leave comments. Login now