IPython Notebook Duck-Punching (I)

If it walks like a duck and talks like a duck, it’s a duck. So if this duck is not giving you the noise that you want, you’ve got to just punch that duck until it returns what you expect.

This will try to be a series of blog post (or nbviewer post) on the IPython notebook. Mainly in responses to comment on Better typography for IPython notebooks and some of the comment at the end of the page, especially this one, that will have a full answer next post.

First some presentation, I am Matthias, you can usually find me on github aka @carreau, or on twitter @mbussonn. I'm a Phd student in Biophysic, more physic than bio. And I contributed mostly to the IPython notebook.

This would also probably be the starting point for the IPython Advance tutorial I'll be giving next August in Euro SciPy.

Addendum

I was a little optimistic in presupposing that custom.css was availlable in 0.13.1, so most of what is discussed here need a more recent developpement version to work.

A more general issue

The question about changing the style of the notebook come often and it is part in my opinion of the question of type :

Why dont you want to integrate X? Why is it not implemented?

I would like to take some time to respond to it, in both a general way and try to introduce to to some concept of the notebook few people know about. Brian Granger already did some good post about it here and here.

As I am also responsible for the refactoring of nbconvert and are the principal maintainer of nbviewer I'll try to share my thought on what the future will be, and what is currently in progress.

So, back to our question, why do we not implement some feature.

Well, a non negligeable part of the time, because they already exist, you are just not aware of that. Plese read the doc (or say you did) and ask how to do it because you didn't well understood. Once you acchieve to do it, a good way to help us and other is to improve the doc, Pull Request are welcomed :-)

Second possibility: You don't need code in the core to do it. We provide more or less easy way to hook onto IPython. And as there a are many reason for which some things are better outside of IPython than in the core, we will encourage you to do ship it separately.

Third: we won't special case only for you. This does not mean we don't like what you did, more often it mean that the internal of IPython shoudl allow what you did to be an extension, they just don't allow it yet. Bare with us for some time or help us to acchieve our goal and it will be better for everyone soon !

What about css theming ?

Short answer : I haven't seen any requests about theming that cannot be done without patching IPython code in itself. Some part could be made much easier, and it is in progress.

What you ask (Custom theme, share them...) is already here, you just don't know how to do it.

Carl said :

Warning: I don’t know what I’m doing. Don’t make any of these changes, or any others, without backing up the files first.

But me, I do know what I'm dooing when speeking of IPython, but not when speaking of design, so let's start to tinker with IPython to have custom css, and easy to share.

First thing to remember if you ever think of modifying a file in IPython/notebook/html/static/* you are dooing it wrong.

Let's dive a little into how to customise the IPython notebook.

Adding custom css:

How to add custom css to notebook, starting by the wrong way. Things you must never do.

Modifyin IPython source files.

Never do that, except if you want to open a pull request to fix a bug. Actually you should never modify a file which is under IPython/frontend/html/notebook/static/* because you don't need to do it. We'll se later why and what to do.

This mean, that if you are not admin on your machine, or you just don't want to modify the system file, you can still read the rest and try by yourself.

CSS In a markdown cell

Probably the worse way to do it. You can create a markdown cell with style tag in it, and write some css that will apply to the notebook.

It will most likely break on future version, you have to add it every time. You will bother other when sharing your notebook, and it will probably break the conversion process into pdf/rst/markdown when you use nbconvert

Html markup will not be the same in nbviewer, so your post might be ugly, and if there are any update of something at some point, you will have to update all your .ipynb files.

Even if this is great to test some css as a quick an dirty way (like I did for this notebook) I strongly advise not to do it.

It is not yet clear with how things are right now, but a notebook is a document that contain data, and the frontend is responsible for the formatting. Right now the notebook server has few frontends:

- the browser one
- and the [emacs client](https://github.com/tkf/emacs-ipython-notebook).

But this is likely to change, so please no style in Markdown cell.

The answer : custom.css

So here we are, the right way to add custoom css to a notebook when you look at it throught the browser interface, use the custom.css file.

This file is not created by default, and can exist on a per-profile basis, if you don't know what ipython profiles are, then you are probably using the default profile. In short, profile are a way to have different configuration for ipython which you can choose through a command line flag (ipython notebook --profile=<profilename>).

Let's locate our profile folder:

In [2]:
%%bash 
ipython locate
/Users/matthiasbussonnier/.ipython

This tells me that IPyton is expecting profiles in the above directory, and more specially on profile named foo will have the corresponding files in

/Users/matthiasbussonnier/.ipython/profile_foo/

let's create a profile for the sake of this blog post.

In [3]:
%%bash
ipython profile create customcss
[ProfileCreate] Generating default config file: u'/Users/matthiasbussonnier/.ipython/profile_customcss/ipython_config.py'
[ProfileCreate] Generating default config file: u'/Users/matthiasbussonnier/.ipython/profile_customcss/ipython_qtconsole_config.py'
[ProfileCreate] Generating default config file: u'/Users/matthiasbussonnier/.ipython/profile_customcss/ipython_notebook_config.py'

This created the needed folder structure for IPython to work, but we won't be interested in those file for now. If you do not want to create a custom profile, you could also modify the files in profile_default which is the profile IPython uses when nothing is specified.

I will now create the file I am interested in : static/custom/custom.css

In [5]:
%%bash
mkdir ~/.ipython/profile_customcss/static/
mkdir ~/.ipython/profile_customcss/static/custom/
touch ~/.ipython/profile_customcss/static/custom/custom.css

Use the file magic to write something in it.

In [34]:
%%file /Users/matthiasbussonnier/.ipython/profile_customcss/static/custom/custom.css
/**write your css in here**/
/* like */

<style>
    div.cell{
        max-width:750px;
        margin-left:auto;
        margin-right:auto;
    }

    h1 {
        text-align:center;
        font-familly:"Charis SIL", serif;
    }
</style>
Overwriting /Users/matthiasbussonnier/.ipython/profile_customcss/static/custom/custom.css
In [35]:
cat ~/.ipython/profile_customcss/static/custom/custom.css
/**write your css in here**/
/* like */

<style>
    div.cell{
        max-width:750px;
        margin-left:auto;
        margin-right:auto;
    }

    h1 {
        text-align:center;
        font-familly:"Charis SIL", serif;
    }
</style>

Now every time you start ipython with :

$ ipython notebook --profile customcss
[NotebookApp] Using existing profile dir: u'~/.ipython/profile_customcss'
[NotebookApp] Serving notebooks from local directory: /Users/matthiasbussonnier/
[NotebookApp] The IPython Notebook is running at: http://<ip>:8889/
[NotebookApp] Use Control-C to stop this server and shut down all kernels.

You will get the right css, let's try :

In [36]:
%%bash 
curl  --noproxy localhost  http://localhost:8889/static/custom/custom.css
/**write your css in here**/
/* like */

<style>
    div.cell{
        max-width:750px;
        margin-left:auto;
        margin-right:auto;
    }

    h1 {
        text-align:center;
        font-familly:"Charis SIL", serif;
    }
</style>
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   240  100   240    0     0   102k      0 --:--:-- --:--:-- --:--:--  234k

Yeah ! We now get you custom css that will be loaded in notebook, without dangerous file modifications, and without using root rights !

Things to come.

On IPython notebook-server side

I must warn you, do not rely too much on current css and classes to make your custom theme. We are both refactoring and introducing new tools to make our (and your) life easier.

We are progressively moving our css to bootrap, and we currently have part of it that is generated through a compilation of less file.

This allow us to introduce css variables, so that you can, for example, set a global HUE for the theme an a radius for the corner, recompile, and you get your new theme ready. Just use it as a custom css in your profile dir and you are good to go.

Here are one example of what you can do. And as a bonus, (I'll let you search) we added a notebook flag to compile css on the fly in the browser, so you can develop your theme with a less folder wirhout triggering compilation yourself.

img

I told you I was not good in design.

So now our notebook look more like an ugly duckling, but we now how to pet it so that it behave more like we want, and you can share it!

On nbconvert/viewer side

The notebook format suppor metadata, so I don't see any reason not to set a prefered theme for a notebook whe viewing with a specific application/frontend. This might include nbviewer, it we consider that css are safe enough and got the time to add the concept of user to nbviewer I don't see any reason not to support external css. @damiamavilla already have build a slideshow version of nbviewer (that we will probably release in the next 6 month) that support multiple theme for the same notebook. And if you made some theme, feel free to share, I even think that a user-governed repository with multiple css woudl be great.

Conclusion

Custom css is doable, and will improve, and the more you help us, the faster it will arrives ! Also this show you that this does not need to be part of IPython core to exist, and having it separately will allow faster release cycle or even rolling release of themes.

If you have any comment corrections, I think you'll probably find the gist/github repo that correspond to this notebook.

Next post trailer.

Custom.css is not the only file that you can use to inject css in the notebook. Actually any file that you ask to the webserver that start with the /static/ prefix will be first searched in your profile dir. You can also add path with NotebookApp.extra_static_paths=<List> configuration option.

So as you'll have guessed, custom.css exist in the directory where the static resources are installed, it only contains comments.

Next time, we will use custom.js (I think you can find it by yourself) as an entry point into the notebook to load more javascript dynamically and look at where we can hook to create a css selector. I'll dive into the recent api we added to javascript, and what are the great things you can do with it.

I'll let you prepare a few themes to play with, feel free to share them on ipython-contrib.

Not many javascript knowledge will required, you just need to find the curly bracket on your keyboard.