Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Expose Pygments styles as options #171

Closed
wants to merge 3 commits into from

2 participants

Min RK Fernando Perez
Min RK
Owner

IPythonWidget already supported using various pygments styles. I just added some flags to ipythonqt, and tweaked the default style code, so that they can be set at launch:

ipythonqt --dark will run IPython with the default dark theme
ipythonqt --style will run IPython using the specific style. Anything pygments accepts will work.
e.g.
ipythonqt --style fruity

The style code also guesses whether a theme is dark or light, and chooses the right stylesheet. This can be overridden if --dark is manually specified.

If you want to use your own css stylesheet, you can pass that with --stylesheet.

No logic was added to the widgets themselves, just the ipythonqt script and a styles file that sets up the defaults.

Fernando Perez
Owner

Min, once you finish this one with the --colors changes according to our review/discussion yesterday, I think it will be ready to merge. Thanks for the great job!

Min RK
Owner

Oops, forgot to push that the other night. It's up now.

Min RK
Owner

Merge branch 'minrk-pygments' into trunk

Provide full support for all the Pygments styles at the console, and
properly synchronize with IPython's own Linux/LightBG color schemes.
In the long run we need to refactor our internal coloring code to only
use CSS, but for now this provides a fairly clean user experience.

Closed by e6c4eee (pull request)

Fernando Perez
Owner

Thanks a lot for this, very nice feature to have.

markvoorhies markvoorhies referenced this pull request from a commit in markvoorhies/ipython
Min RK minrk Merge branch 'minrk-pygments' into trunk
Provide full support for all the Pygments styles at the console, and
properly synchronize with IPython's own Linux/LightBG color schemes.
In the long run we need to refactor our internal coloring code to only
use CSS, but for now this provides a fairly clean user experience.

Closes gh-171 (pull request)
e6c4eee
Fernando Perez fperez referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Damián Avila damianavila referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
David Renshaw dwrensha referenced this pull request from a commit
Andrew Kelley andrewrk improve import by URL feature. closes #171
 * Drop dependency on temp module
 * Download files to music-folder/.tmp/ before moving them.
 * Don't watch folders that start with a '.'
 * Don't allow generated paths to start with a '.'

Fixes import file race condition.

Prevents needless file copy operation when importing in situations where
the music directory is in a different device than /tmp.
5c8f45b
matthew von rocketstein mattvonrocketstein referenced this pull request from a commit in mattvonrocketstein/ipython
Min RK minrk Merge branch 'minrk-pygments' into trunk
Provide full support for all the Pygments styles at the console, and
properly synchronize with IPython's own Linux/LightBG color schemes.
In the long run we need to refactor our internal coloring code to only
use CSS, but for now this provides a fairly clean user experience.

Closes gh-171 (pull request)
857a7ed
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
41 IPython/frontend/qt/console/ipython_widget.py
View
@@ -25,33 +25,14 @@
from IPython.core.usage import default_gui_banner
from IPython.utils.traitlets import Bool, Str
from frontend_widget import FrontendWidget
+from styles import (default_light_style_sheet, default_light_syntax_style,
+ default_dark_style_sheet, default_dark_syntax_style,
+ default_bw_style_sheet, default_bw_syntax_style)
#-----------------------------------------------------------------------------
# Constants
#-----------------------------------------------------------------------------
-# The default light style sheet: black text on a white background.
-default_light_style_sheet = '''
- .error { color: red; }
- .in-prompt { color: navy; }
- .in-prompt-number { font-weight: bold; }
- .out-prompt { color: darkred; }
- .out-prompt-number { font-weight: bold; }
-'''
-default_light_syntax_style = 'default'
-
-# The default dark style sheet: white text on a black background.
-default_dark_style_sheet = '''
- QPlainTextEdit, QTextEdit { background-color: black; color: white }
- QFrame { border: 1px solid grey; }
- .error { color: red; }
- .in-prompt { color: lime; }
- .in-prompt-number { color: lime; font-weight: bold; }
- .out-prompt { color: red; }
- .out-prompt-number { color: red; font-weight: bold; }
-'''
-default_dark_syntax_style = 'monokai'
-
# Default strings to build and display input and output prompts (and separators
# in between)
default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
@@ -349,21 +330,27 @@ def _show_interpreter_prompt_for_reply(self, msg):
# 'IPythonWidget' interface
#---------------------------------------------------------------------------
- def set_default_style(self, lightbg=True):
+ def set_default_style(self, colors='lightbg'):
""" Sets the widget style to the class defaults.
Parameters:
-----------
- lightbg : bool, optional (default True)
+ colors : str, optional (default lightbg)
Whether to use the default IPython light background or dark
- background style.
+ background or B&W style.
"""
- if lightbg:
+ colors = colors.lower()
+ if colors=='lightbg':
self.style_sheet = default_light_style_sheet
self.syntax_style = default_light_syntax_style
- else:
+ elif colors=='linux':
self.style_sheet = default_dark_style_sheet
self.syntax_style = default_dark_syntax_style
+ elif colors=='nocolor':
+ self.style_sheet = default_bw_style_sheet
+ self.syntax_style = default_bw_syntax_style
+ else:
+ raise KeyError("No such color scheme: %s"%colors)
#---------------------------------------------------------------------------
# 'IPythonWidget' protected interface
67 IPython/frontend/qt/console/ipythonqt.py
View
@@ -7,12 +7,13 @@
# Systemm library imports
from PyQt4 import QtGui
-
+from pygments.styles import get_all_styles
# Local imports
from IPython.external.argparse import ArgumentParser
from IPython.frontend.qt.console.frontend_widget import FrontendWidget
from IPython.frontend.qt.console.ipython_widget import IPythonWidget
from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
+from IPython.frontend.qt.console import styles
from IPython.frontend.qt.kernelmanager import QtKernelManager
#-----------------------------------------------------------------------------
@@ -129,7 +130,7 @@ def main():
kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
help='set the REP channel port [default random]')
kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
- help='set the heartbeat port [default: random]')
+ help='set the heartbeat port [default random]')
egroup = kgroup.add_mutually_exclusive_group()
egroup.add_argument('--pure', action='store_true', help = \
@@ -148,9 +149,36 @@ def main():
help='enable rich text support')
wgroup.add_argument('--gui-completion', action='store_true',
help='use a GUI widget for tab completion')
+ wgroup.add_argument('--style', type=str,
+ choices = list(get_all_styles()),
+ help='specify a pygments style for by name.')
+ wgroup.add_argument('--stylesheet', type=str,
+ help="path to a custom CSS stylesheet.")
+ wgroup.add_argument('--colors', type=str,
+ help="Set the color scheme (LightBG,Linux,NoColor). This is guessed\
+ based on the pygments style if not set.")
args = parser.parse_args()
+ # parse the colors arg down to current known labels
+ if args.colors:
+ colors=args.colors.lower()
+ if colors in ('lightbg', 'light'):
+ colors='lightbg'
+ elif colors in ('dark', 'linux'):
+ colors='linux'
+ else:
+ colors='nocolor'
+ elif args.style:
+ if args.style=='bw':
+ colors='nocolor'
+ elif styles.dark_style(args.style):
+ colors='linux'
+ else:
+ colors='lightbg'
+ else:
+ colors=None
+
# Don't let Qt or ZMQ swallow KeyboardInterupts.
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
@@ -163,13 +191,15 @@ def main():
if not args.existing:
# if not args.ip in LOCAL_IPS+ALL_ALIAS:
# raise ValueError("Must bind a local ip, such as: %s"%LOCAL_IPS)
-
+
kwargs = dict(ip=args.ip)
if args.pure:
kwargs['ipython']=False
- elif args.pylab:
- kwargs['pylab']=args.pylab
-
+ else:
+ kwargs['colors']=colors
+ if args.pylab:
+ kwargs['pylab']=args.pylab
+
kernel_manager.start_kernel(**kwargs)
kernel_manager.start_channels()
@@ -186,6 +216,31 @@ def main():
widget.gui_completion = args.gui_completion
widget.kernel_manager = kernel_manager
+ # configure the style:
+ if not args.pure: # only IPythonWidget supports styles
+ if args.style:
+ widget.syntax_style = args.style
+ widget.style_sheet = styles.sheet_from_template(args.style, colors)
+ widget._syntax_style_changed()
+ widget._style_sheet_changed()
+ elif colors:
+ # use a default style
+ widget.set_default_style(colors=colors)
+ else:
+ # this is redundant for now, but allows the widget's
+ # defaults to change
+ widget.set_default_style()
+
+ if args.stylesheet:
+ # we got an expicit stylesheet
+ if os.path.isfile(args.stylesheet):
+ with open(args.stylesheet) as f:
+ sheet = f.read()
+ widget.style_sheet = sheet
+ widget._style_sheet_changed()
+ else:
+ raise IOError("Stylesheet %r not found."%args.stylesheet)
+
# Create the main window.
window = MainWindow(app, widget, args.existing, may_close=local_kernel)
window.setWindowTitle('Python' if args.pure else 'IPython')
119 IPython/frontend/qt/console/styles.py
View
@@ -0,0 +1,119 @@
+""" Style utilities, templates, and defaults for syntax highlighting widgets.
+"""
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+
+from colorsys import rgb_to_hls
+from pygments.styles import get_style_by_name
+from pygments.token import Token
+
+#-----------------------------------------------------------------------------
+# Constants
+#-----------------------------------------------------------------------------
+
+# The default light style sheet: black text on a white background.
+default_light_style_template = '''
+ QPlainTextEdit, QTextEdit { background-color: %(bgcolor)s;
+ color: %(fgcolor)s ;
+ selection-background-color: %(select)s}
+ .error { color: red; }
+ .in-prompt { color: navy; }
+ .in-prompt-number { font-weight: bold; }
+ .out-prompt { color: darkred; }
+ .out-prompt-number { font-weight: bold; }
+'''
+default_light_style_sheet = default_light_style_template%dict(
+ bgcolor='white', fgcolor='black', select="#ccc")
+default_light_syntax_style = 'default'
+
+# The default dark style sheet: white text on a black background.
+default_dark_style_template = '''
+ QPlainTextEdit, QTextEdit { background-color: %(bgcolor)s;
+ color: %(fgcolor)s ;
+ selection-background-color: %(select)s}
+ QFrame { border: 1px solid grey; }
+ .error { color: red; }
+ .in-prompt { color: lime; }
+ .in-prompt-number { color: lime; font-weight: bold; }
+ .out-prompt { color: red; }
+ .out-prompt-number { color: red; font-weight: bold; }
+'''
+default_dark_style_sheet = default_dark_style_template%dict(
+ bgcolor='black', fgcolor='white', select="#555")
+default_dark_syntax_style = 'monokai'
+
+# The default monochrome
+default_bw_style_sheet = '''
+ QPlainTextEdit, QTextEdit { background-color: white;
+ color: black ;
+ selection-background-color: #cccccc}
+ .in-prompt-number { font-weight: bold; }
+ .out-prompt-number { font-weight: bold; }
+'''
+default_bw_syntax_style = 'bw'
+
+
+def hex_to_rgb(color):
+ """Convert a hex color to rgb integer tuple."""
+ if color.startswith('#'):
+ color = color[1:]
+ if len(color) == 3:
+ color = ''.join([c*2 for c in color])
+ if len(color) != 6:
+ return False
+ try:
+ r = int(color[:2],16)
+ g = int(color[:2],16)
+ b = int(color[:2],16)
+ except ValueError:
+ return False
+ else:
+ return r,g,b
+
+def dark_color(color):
+ """Check whether a color is 'dark'.
+
+ Currently, this is simply whether the luminance is <50%"""
+ rgb = hex_to_rgb(color)
+ if rgb:
+ return rgb_to_hls(*rgb)[1] < 128
+ else: # default to False
+ return False
+
+def dark_style(stylename):
+ """Guess whether the background of the style with name 'stylename'
+ counts as 'dark'."""
+ return dark_color(get_style_by_name(stylename).background_color)
+
+def get_colors(stylename):
+ """Construct the keys to be used building the base stylesheet
+ from a templatee."""
+ style = get_style_by_name(stylename)
+ fgcolor = style.style_for_token(Token.Text)['color'] or ''
+ if len(fgcolor) in (3,6):
+ # could be 'abcdef' or 'ace' hex, which needs '#' prefix
+ try:
+ int(fgcolor, 16)
+ except TypeError:
+ pass
+ else:
+ fgcolor = "#"+fgcolor
+
+ return dict(
+ bgcolor = style.background_color,
+ select = style.highlight_color,
+ fgcolor = fgcolor
+ )
+
+def sheet_from_template(name, colors='lightbg'):
+ """Use one of the base templates, and set bg/fg/select colors."""
+ colors = colors.lower()
+ if colors=='lightbg':
+ return default_light_style_template%get_colors(name)
+ elif colors=='linux':
+ return default_dark_style_template%get_colors(name)
+ elif colors=='nocolor':
+ return default_bw_style_sheet
+ else:
+ raise KeyError("No such color scheme: %s"%colors)
14 IPython/zmq/ipkernel.py
View
@@ -535,7 +535,7 @@ def start(self):
#-----------------------------------------------------------------------------
def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
- independent=False, pylab=False):
+ independent=False, pylab=False, colors=None):
"""Launches a localhost kernel, binding to the specified ports.
Parameters
@@ -566,6 +566,9 @@ def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
string is passed, matplotlib will use the specified backend. Otherwise,
matplotlib's default backend will be used.
+ colors : None or string, optional (default None)
+ If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
+
Returns
-------
A tuple of form:
@@ -581,6 +584,9 @@ def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
extra_arguments.append('--ip')
if isinstance(ip, basestring):
extra_arguments.append(ip)
+ if colors is not None:
+ extra_arguments.append('--colors')
+ extra_arguments.append(colors)
return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
xrep_port, pub_port, req_port, hb_port,
independent, extra_arguments)
@@ -595,6 +601,10 @@ def main():
"Pre-load matplotlib and numpy for interactive use. If GUI is not \
given, the GUI backend is matplotlib's, otherwise use one of: \
['tk', 'gtk', 'qt', 'wx', 'inline'].")
+ parser.add_argument('--colors',
+ type=str, dest='colors',
+ help="Set the color scheme (NoColor, Linux, and LightBG).",
+ metavar='ZMQInteractiveShell.colors')
namespace = parser.parse_args()
kernel_class = Kernel
@@ -616,6 +626,8 @@ def main():
if kernel_class is None:
raise ValueError('GUI is not supported: %r' % gui)
pylabtools.activate_matplotlib(backend)
+ if namespace.colors:
+ ZMQInteractiveShell.colors=namespace.colors
kernel = make_kernel(namespace, kernel_class, OutStream)
Something went wrong with that request. Please try again.