{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# IPython Notebook Duck-Punching (I)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> If it walks like a duck and talks like a duck, it\u2019s a duck. So if this duck is not giving you the noise that you want, you\u2019ve got to just punch that duck until it returns what you expect." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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](http://slendrmeans.wordpress.com/2012/12/05/better-typography-for-ipython-notebooks) and some of the comment at the end of the page, especially [this one](http://slendrmeans.wordpress.com/2012/12/05/better-typography-for-ipython-notebooks/#comment-148), that will have a full answer [next post](/urls/raw.github.com/Carreau/posts/master/02-css-selector.ipynb).\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First some presentation, I am Matthias, you can usually find me on github aka [@carreau](https://github.com/carreau), or on twitter @mbussonn. I'm a Phd student in Biophysic, more physic than bio. And I contributed mostly to the IPython notebook." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This would also probably be the starting point for the IPython Advance tutorial I'll be giving next August in [Euro SciPy](https://www.euroscipy.org/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Addendum" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## A more general issue" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The question about changing the style of the notebook come often and it is part in my opinion of the question of type :\n", "\n", "> Why dont you want to integrate X? Why is it not implemented?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "Brian Granger already did some good post about it [here](http://brianegranger.com/?p=249) and [here](http://brianegranger.com/?p=261)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As I am also responsible for the refactoring of [nbconvert](https://github.com/ipython/nbconvert) and are the principal maintainer of [nbviewer](http://nbviewer.ipython.org/) I'll try to share my thought on what the future will be, and what is currently in progress.\n", "\n", "So, back to our question, why do we not implement some feature.\n", "\n", "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.\n", "Once you acchieve to do it, a good way to help us and other is to improve the doc, Pull Request are welcomed :-)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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 !" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What about css theming ?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "What you ask (Custom theme, share them...) is already here, you just don't know how to do it.\n", "\n", "[Carl](http://slendrmeans.wordpress.com/) said : \n", "\n", "> Warning: I don\u2019t know what I\u2019m doing. Don\u2019t make any of these changes, or any others, without backing up the files first.\n", "\n", "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First thing to remember if you ever think of modifying a file in `IPython/notebook/html/static/*` you are dooing it wrong." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's dive a little into how to customise the IPython notebook." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Adding custom css:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How to add custom css to notebook, starting by the wrong way. Things you must never do. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Modifyin IPython source files." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Never do that, except if you want to open a pull request to fix a bug. \n", "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.\n", "\n", "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### CSS In a markdown cell" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Probably the worse way to do it.\n", "You can create a markdown cell with style tag in it, and write some css that will apply to the notebook.\n", "\n", "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](https://github.com/ipython/nbconvert)\n", "\n", "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.\n", "\n", "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.\n", "\n", "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: \n", " - the browser one\n", " - and the [emacs client](https://github.com/tkf/emacs-ipython-notebook).\n", "\n", "But this is likely to change, so please no style in Markdown cell. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The answer : custom.css" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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=`). \n", "\n", "Let's locate our profile folder:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/Users/matthiasbussonnier/.ipython\n" ] } ], "source": [ "%%bash \n", "ipython locate" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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 \n", "\n", "`/Users/matthiasbussonnier/.ipython/profile_foo/`\n", "\n", "let's create a profile for the sake of this blog post." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[ProfileCreate] Generating default config file: u'/Users/matthiasbussonnier/.ipython/profile_customcss/ipython_config.py'\n", "[ProfileCreate] Generating default config file: u'/Users/matthiasbussonnier/.ipython/profile_customcss/ipython_qtconsole_config.py'\n", "[ProfileCreate] Generating default config file: u'/Users/matthiasbussonnier/.ipython/profile_customcss/ipython_notebook_config.py'\n" ] } ], "source": [ "%%bash\n", "ipython profile create customcss" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I will now create the file I am interested in : \n", "`static/custom/custom.css`" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%bash\n", "mkdir ~/.ipython/profile_customcss/static/\n", "mkdir ~/.ipython/profile_customcss/static/custom/\n", "touch ~/.ipython/profile_customcss/static/custom/custom.css" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use the file magic to write something in it." ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting /Users/matthiasbussonnier/.ipython/profile_customcss/static/custom/custom.css\n" ] } ], "source": [ "%%file /Users/matthiasbussonnier/.ipython/profile_customcss/static/custom/custom.css\n", "/**write your css in here**/\n", "/* like */\n", "\n", "" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/**write your css in here**/\r\n", "/* like */\r\n", "\r\n", "" ] } ], "source": [ "cat ~/.ipython/profile_customcss/static/custom/custom.css" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now every time you start ipython with :\n", "\n", "```\n", "$ ipython notebook --profile customcss\n", "[NotebookApp] Using existing profile dir: u'~/.ipython/profile_customcss'\n", "[NotebookApp] Serving notebooks from local directory: /Users/matthiasbussonnier/\n", "[NotebookApp] The IPython Notebook is running at: http://:8889/\n", "[NotebookApp] Use Control-C to stop this server and shut down all kernels.\n", "```\n", "\n", "\n", "You will get the right css, let's try : " ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/**write your css in here**/\n", "/* like */\n", "\n", "" ] }, { "name": "stderr", "output_type": "stream", "text": [ " % Total % Received % Xferd Average Speed Time Time Time Current\n", " Dload Upload Total Spent Left Speed\n", "\r", " 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\r", "100 240 100 240 0 0 102k 0 --:--:-- --:--:-- --:--:-- 234k\n" ] } ], "source": [ "%%bash \n", "curl --noproxy localhost http://localhost:8889/static/custom/custom.css" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yeah ! We now get you custom css that will be loaded in notebook, without dangerous file modifications, and without using root rights !" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Things to come." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### On IPython notebook-server side" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are progressively moving our css to [bootrap](http://twitter.github.com/bootstrap/), and we currently have part of it that is generated through a compilation of `less` file.\n", "\n", "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "
\n", "![img](https://f.cloud.github.com/assets/335567/22913/bb412fdc-4a19-11e2-9a9b-2700e5b24843.png)\n", "
\n", "\n", "I told you I was not good in design.\n", "\n", "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!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### On nbconvert/viewer side" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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](https://github.com/ipython-contrib/IPython-contrib) repository with multiple css woudl be great." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Conclusion" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you have any comment corrections, I think you'll probably find the gist/github repo that correspond to this notebook." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Next post trailer." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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=` configuration option. \n", "\n", "So as you'll have guessed, `custom.css` exist in the directory where the static resources are installed, it only contains comments." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I'll let you prepare a few themes to play with, feel free to share them on [ipython-contrib](https://github.com/ipython-contrib/IPython-contrib). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Not many javascript knowledge will required, you just need to find the curly bracket on your keyboard." ] } ], "metadata": { "kernelspec": { "display_name": "IPython (Python 3)", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3" }, "signature": "sha256:f183ba7615a1ca1cb4355f96680270c4e02c09eb09a1b4bb0c3a2793ace9d0f4" }, "nbformat": 4, "nbformat_minor": 0 }